mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-24 21:00:55 +03:00
Merge branch 'ImproveCurrentPlayingUI' into 'develop'
Draft: improve feedback of currentPlayingLayout See merge request ultrasonic/ultrasonic!1047
This commit is contained in:
commit
e967139f94
@ -73,6 +73,7 @@ import org.moire.ultrasonic.util.ShortcutUtil
|
||||
import org.moire.ultrasonic.util.Storage
|
||||
import org.moire.ultrasonic.util.UncaughtExceptionHandler
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import org.moire.ultrasonic.util.Util.ifNotNull
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
@ -179,11 +180,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
|
||||
currentFragmentId = destination.id
|
||||
// Handle the hiding of the NowPlaying fragment when the Player is active
|
||||
if (currentFragmentId == R.id.playerFragment) {
|
||||
hideNowPlaying()
|
||||
} else {
|
||||
if (!nowPlayingHidden) showNowPlaying()
|
||||
}
|
||||
computeNowPlayingVisibility()
|
||||
}
|
||||
|
||||
// Determine if this is a first run
|
||||
@ -199,15 +196,16 @@ class NavigationActivity : AppCompatActivity() {
|
||||
Util.ensurePermissionToPostNotification(this)
|
||||
|
||||
rxBusSubscription += RxBus.dismissNowPlayingCommandObservable.subscribe {
|
||||
nowPlayingHidden = true
|
||||
hideNowPlaying()
|
||||
computeNowPlayingVisibility(false)
|
||||
}
|
||||
|
||||
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
||||
if (it.state == STATE_READY)
|
||||
showNowPlaying()
|
||||
else
|
||||
hideNowPlaying()
|
||||
// If state is ready then nowPlaying will be visible again
|
||||
if (it.state == STATE_READY) {
|
||||
computeNowPlayingVisibility(true)
|
||||
} else {
|
||||
computeNowPlayingVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
rxBusSubscription += RxBus.themeChangedEventObservable.subscribe {
|
||||
@ -314,8 +312,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
// Lifecycle support's constructor registers some event receivers so it should be created early
|
||||
lifecycleSupport.onCreate()
|
||||
|
||||
if (!nowPlayingHidden) showNowPlaying()
|
||||
else hideNowPlaying()
|
||||
computeNowPlayingVisibility()
|
||||
}
|
||||
|
||||
/*
|
||||
@ -555,9 +552,14 @@ class NavigationActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showNowPlaying() {
|
||||
private fun computeNowPlayingVisibility(forceNewVisibility: Boolean? = null) {
|
||||
forceNewVisibility.ifNotNull { nowPlayingHidden = !it }
|
||||
if (!Settings.showNowPlaying) {
|
||||
hideNowPlaying()
|
||||
nowPlayingView?.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
if (nowPlayingHidden) {
|
||||
nowPlayingView?.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
@ -566,7 +568,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
nowPlayingHidden = false
|
||||
// Do not show for Player fragment
|
||||
if (currentFragmentId == R.id.playerFragment) {
|
||||
hideNowPlaying()
|
||||
nowPlayingView?.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
@ -578,15 +580,11 @@ class NavigationActivity : AppCompatActivity() {
|
||||
nowPlayingView?.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
hideNowPlaying()
|
||||
nowPlayingView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideNowPlaying() {
|
||||
nowPlayingView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun setMenuForServerCapabilities() {
|
||||
val isOnline = !ActiveServerProvider.isOffline()
|
||||
val activeServer = activeServerProvider.getActiveServer()
|
||||
|
@ -10,18 +10,19 @@ package org.moire.ultrasonic.fragment
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import java.lang.Exception
|
||||
import kotlin.math.abs
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
@ -38,9 +39,88 @@ import timber.log.Timber
|
||||
* Contains the mini-now playing information box displayed at the bottom of the screen
|
||||
*/
|
||||
class NowPlayingFragment : Fragment() {
|
||||
private var isInitialized = false
|
||||
private lateinit var nowPlayingCollectionAdapter: NowPlayingCollectionAdapter
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private var rxBusSubscription: Disposable? = null
|
||||
|
||||
private var downX = 0f
|
||||
private var downY = 0f
|
||||
private val mediaPlayerManager: MediaPlayerManager by inject()
|
||||
private val pageChangeCallback = PageChangeCallback()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.now_playing_wrapper, container, false)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (mediaPlayerManager.currentMediaItemIndex >= 0)
|
||||
viewPager.setCurrentItem(mediaPlayerManager.currentMediaItemIndex, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
nowPlayingCollectionAdapter = NowPlayingCollectionAdapter(this, mediaPlayerManager)
|
||||
viewPager = view.findViewById(R.id.pager)
|
||||
viewPager.currentItem = mediaPlayerManager.currentMediaItemIndex
|
||||
viewPager.adapter = nowPlayingCollectionAdapter
|
||||
isInitialized = false
|
||||
|
||||
// Subscribe to updates on current Item
|
||||
rxBusSubscription = RxBus.playerStateObservable.subscribe {
|
||||
if (it.state == Player.STATE_READY) {
|
||||
viewPager.setCurrentItem(it.index, true)
|
||||
isInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
viewPager.registerOnPageChangeCallback(pageChangeCallback)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
rxBusSubscription?.dispose()
|
||||
viewPager.unregisterOnPageChangeCallback(pageChangeCallback)
|
||||
}
|
||||
|
||||
private inner class PageChangeCallback : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (!isInitialized) return
|
||||
val newIndex = mediaPlayerManager.getUnshuffledIndexOf(position)
|
||||
if (mediaPlayerManager.currentMediaItemIndex != newIndex) {
|
||||
mediaPlayerManager.seekTo(newIndex, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NowPlayingCollectionAdapter(
|
||||
fragment: Fragment,
|
||||
private val playerManager: MediaPlayerManager
|
||||
) : FragmentStateAdapter(fragment) {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return playerManager.mediaItemCount
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return playerManager.getMediaItemAt(playerManager.getUnshuffledIndexOf(position))
|
||||
.hashCode().toLong()
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
// Return a NEW fragment instance in createFragment(int)
|
||||
val mediaItem = playerManager.getMediaItemAt(playerManager.getUnshuffledIndexOf(position))
|
||||
return NowPlayingChildFragment(mediaItem)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the mini-now playing information box displayed at the bottom of the screen
|
||||
*/
|
||||
class NowPlayingChildFragment(private val mediaItem: MediaItem?) : Fragment() {
|
||||
|
||||
private var playButton: MaterialButton? = null
|
||||
private var nowPlayingAlbumArtImage: ImageView? = null
|
||||
@ -79,7 +159,7 @@ class NowPlayingFragment : Fragment() {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
rxBusSubscription!!.dispose()
|
||||
rxBusSubscription?.dispose()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@ -91,7 +171,7 @@ class NowPlayingFragment : Fragment() {
|
||||
playButton!!.setIconResource(R.drawable.media_start)
|
||||
}
|
||||
|
||||
val file = mediaPlayerManager.currentMediaItem?.toTrack()
|
||||
val file = mediaItem?.toTrack()
|
||||
|
||||
if (file != null) {
|
||||
val title = file.title
|
||||
@ -121,53 +201,14 @@ class NowPlayingFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
requireView().setOnTouchListener { _: View?, event: MotionEvent ->
|
||||
handleOnTouch(event)
|
||||
requireView().setOnClickListener {
|
||||
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
|
||||
.navigate(R.id.playerFragment)
|
||||
}
|
||||
|
||||
// This empty onClickListener is necessary for the onTouchListener to work
|
||||
requireView().setOnClickListener { }
|
||||
playButton!!.setOnClickListener { mediaPlayerManager.togglePlayPause() }
|
||||
} catch (all: Exception) {
|
||||
Timber.w(all, "Failed to get notification cover art")
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOnTouch(event: MotionEvent): Boolean {
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
downX = event.x
|
||||
downY = event.y
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val upX = event.x
|
||||
val upY = event.y
|
||||
val deltaX = downX - upX
|
||||
val deltaY = downY - upY
|
||||
|
||||
if (abs(deltaX) > MIN_DISTANCE) {
|
||||
// left or right
|
||||
if (deltaX < 0) {
|
||||
mediaPlayerManager.seekToPrevious()
|
||||
}
|
||||
if (deltaX > 0) {
|
||||
mediaPlayerManager.seekToNext()
|
||||
}
|
||||
} else if (abs(deltaY) > MIN_DISTANCE) {
|
||||
if (deltaY < 0) {
|
||||
RxBus.dismissNowPlayingCommandPublisher.onNext(Unit)
|
||||
}
|
||||
} else {
|
||||
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
|
||||
.navigate(R.id.playerFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MIN_DISTANCE = 30
|
||||
}
|
||||
}
|
||||
|
14
ultrasonic/src/main/res/layout/now_playing_wrapper.xml
Normal file
14
ultrasonic/src/main/res/layout/now_playing_wrapper.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@+id/now_playing_wrapper"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content">
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
Loading…
x
Reference in New Issue
Block a user