mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-28 06:32:14 +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.Storage
|
||||||
import org.moire.ultrasonic.util.UncaughtExceptionHandler
|
import org.moire.ultrasonic.util.UncaughtExceptionHandler
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
import org.moire.ultrasonic.util.Util.ifNotNull
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,11 +180,7 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
currentFragmentId = destination.id
|
currentFragmentId = destination.id
|
||||||
// Handle the hiding of the NowPlaying fragment when the Player is active
|
// Handle the hiding of the NowPlaying fragment when the Player is active
|
||||||
if (currentFragmentId == R.id.playerFragment) {
|
computeNowPlayingVisibility()
|
||||||
hideNowPlaying()
|
|
||||||
} else {
|
|
||||||
if (!nowPlayingHidden) showNowPlaying()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if this is a first run
|
// Determine if this is a first run
|
||||||
@ -199,15 +196,16 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
Util.ensurePermissionToPostNotification(this)
|
Util.ensurePermissionToPostNotification(this)
|
||||||
|
|
||||||
rxBusSubscription += RxBus.dismissNowPlayingCommandObservable.subscribe {
|
rxBusSubscription += RxBus.dismissNowPlayingCommandObservable.subscribe {
|
||||||
nowPlayingHidden = true
|
computeNowPlayingVisibility(false)
|
||||||
hideNowPlaying()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
||||||
if (it.state == STATE_READY)
|
// If state is ready then nowPlaying will be visible again
|
||||||
showNowPlaying()
|
if (it.state == STATE_READY) {
|
||||||
else
|
computeNowPlayingVisibility(true)
|
||||||
hideNowPlaying()
|
} else {
|
||||||
|
computeNowPlayingVisibility()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rxBusSubscription += RxBus.themeChangedEventObservable.subscribe {
|
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
|
// Lifecycle support's constructor registers some event receivers so it should be created early
|
||||||
lifecycleSupport.onCreate()
|
lifecycleSupport.onCreate()
|
||||||
|
|
||||||
if (!nowPlayingHidden) showNowPlaying()
|
computeNowPlayingVisibility()
|
||||||
else hideNowPlaying()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -555,9 +552,14 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNowPlaying() {
|
private fun computeNowPlayingVisibility(forceNewVisibility: Boolean? = null) {
|
||||||
|
forceNewVisibility.ifNotNull { nowPlayingHidden = !it }
|
||||||
if (!Settings.showNowPlaying) {
|
if (!Settings.showNowPlaying) {
|
||||||
hideNowPlaying()
|
nowPlayingView?.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (nowPlayingHidden) {
|
||||||
|
nowPlayingView?.visibility = View.GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,7 +568,7 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
nowPlayingHidden = false
|
nowPlayingHidden = false
|
||||||
// Do not show for Player fragment
|
// Do not show for Player fragment
|
||||||
if (currentFragmentId == R.id.playerFragment) {
|
if (currentFragmentId == R.id.playerFragment) {
|
||||||
hideNowPlaying()
|
nowPlayingView?.visibility = View.GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,15 +580,11 @@ class NavigationActivity : AppCompatActivity() {
|
|||||||
nowPlayingView?.visibility = View.VISIBLE
|
nowPlayingView?.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hideNowPlaying()
|
nowPlayingView?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideNowPlaying() {
|
|
||||||
nowPlayingView?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setMenuForServerCapabilities() {
|
private fun setMenuForServerCapabilities() {
|
||||||
val isOnline = !ActiveServerProvider.isOffline()
|
val isOnline = !ActiveServerProvider.isOffline()
|
||||||
val activeServer = activeServerProvider.getActiveServer()
|
val activeServer = activeServerProvider.getActiveServer()
|
||||||
|
@ -10,18 +10,19 @@ package org.moire.ultrasonic.fragment
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.Player
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import java.lang.Exception
|
|
||||||
import kotlin.math.abs
|
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.moire.ultrasonic.NavigationGraphDirections
|
import org.moire.ultrasonic.NavigationGraphDirections
|
||||||
import org.moire.ultrasonic.R
|
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
|
* Contains the mini-now playing information box displayed at the bottom of the screen
|
||||||
*/
|
*/
|
||||||
class NowPlayingFragment : Fragment() {
|
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 val mediaPlayerManager: MediaPlayerManager by inject()
|
||||||
private var downY = 0f
|
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 playButton: MaterialButton? = null
|
||||||
private var nowPlayingAlbumArtImage: ImageView? = null
|
private var nowPlayingAlbumArtImage: ImageView? = null
|
||||||
@ -79,7 +159,7 @@ class NowPlayingFragment : Fragment() {
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
rxBusSubscription!!.dispose()
|
rxBusSubscription?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@ -91,7 +171,7 @@ class NowPlayingFragment : Fragment() {
|
|||||||
playButton!!.setIconResource(R.drawable.media_start)
|
playButton!!.setIconResource(R.drawable.media_start)
|
||||||
}
|
}
|
||||||
|
|
||||||
val file = mediaPlayerManager.currentMediaItem?.toTrack()
|
val file = mediaItem?.toTrack()
|
||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
val title = file.title
|
val title = file.title
|
||||||
@ -121,53 +201,14 @@ class NowPlayingFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requireView().setOnTouchListener { _: View?, event: MotionEvent ->
|
requireView().setOnClickListener {
|
||||||
handleOnTouch(event)
|
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() }
|
playButton!!.setOnClickListener { mediaPlayerManager.togglePlayPause() }
|
||||||
} catch (all: Exception) {
|
} catch (all: Exception) {
|
||||||
Timber.w(all, "Failed to get notification cover art")
|
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