Make Queue nav destination
Fix a heisenleak by making QueueFragment a navigation destination instead of being directly instantiated.
This commit is contained in:
parent
c664d22a43
commit
bc7950e7af
8 changed files with 59 additions and 11 deletions
|
@ -15,7 +15,6 @@ import androidx.fragment.app.activityViewModels
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBinding
|
||||
import org.oxycblt.auxio.playback.queue.QueueFragment
|
||||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
import org.oxycblt.auxio.theme.accent
|
||||
import org.oxycblt.auxio.theme.disable
|
||||
|
@ -71,7 +70,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
|
||||
setOnMenuItemClickListener {
|
||||
if (it.itemId == R.id.action_queue) {
|
||||
QueueFragment().show(parentFragmentManager, "TAG_QUEUE")
|
||||
findNavController().navigate(PlaybackFragmentDirections.actionShowQueue())
|
||||
}
|
||||
|
||||
true
|
||||
|
@ -186,6 +185,14 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
queueMenuItem.isEnabled = true
|
||||
queueMenuItem.icon = iconQueueActive
|
||||
}
|
||||
|
||||
// If someone edits the queue to make it have no songs left, then disable the
|
||||
// skip next button.
|
||||
if (playbackModel.index.value!! == it.size) {
|
||||
binding.playbackSkipNext.disable(requireContext())
|
||||
} else {
|
||||
binding.playbackSkipNext.enable(requireContext())
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, "Fragment Created.")
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.Transformations
|
|||
import androidx.lifecycle.ViewModel
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.toDuration
|
||||
|
@ -22,6 +23,9 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
private val mSong = MutableLiveData<Song?>()
|
||||
val song: LiveData<Song?> get() = mSong
|
||||
|
||||
private val mParent = MutableLiveData<BaseModel?>()
|
||||
val parent: LiveData<BaseModel?> get() = mParent
|
||||
|
||||
private val mPosition = MutableLiveData(0L)
|
||||
val position: LiveData<Long> get() = mPosition
|
||||
|
||||
|
@ -32,6 +36,9 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
private val mIndex = MutableLiveData(0)
|
||||
val index: LiveData<Int> get() = mIndex
|
||||
|
||||
private val mMode = MutableLiveData(PlaybackMode.ALL_SONGS)
|
||||
val mode: LiveData<PlaybackMode> get() = mMode
|
||||
|
||||
// States
|
||||
private val mIsPlaying = MutableLiveData(false)
|
||||
val isPlaying: LiveData<Boolean> get() = mIsPlaying
|
||||
|
@ -194,6 +201,10 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
mSong.value = song
|
||||
}
|
||||
|
||||
override fun onParentUpdate(parent: BaseModel?) {
|
||||
mParent.value = parent
|
||||
}
|
||||
|
||||
override fun onPositionUpdate(position: Long) {
|
||||
if (!mIsSeeking.value!!) {
|
||||
mPosition.value = position / 1000
|
||||
|
@ -208,6 +219,10 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
mIndex.value = index
|
||||
}
|
||||
|
||||
override fun onModeUpdate(mode: PlaybackMode) {
|
||||
mMode.value = mode
|
||||
}
|
||||
|
||||
override fun onPlayingUpdate(isPlaying: Boolean) {
|
||||
mIsPlaying.value = isPlaying
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -12,6 +13,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.theme.accent
|
||||
import org.oxycblt.auxio.theme.applyDivider
|
||||
import org.oxycblt.auxio.theme.toColor
|
||||
|
@ -28,9 +30,7 @@ class QueueFragment : BottomSheetDialogFragment() {
|
|||
): View? {
|
||||
val binding = FragmentQueueBinding.inflate(inflater)
|
||||
|
||||
val helper = ItemTouchHelper(
|
||||
QueueDragCallback(playbackModel)
|
||||
)
|
||||
val helper = ItemTouchHelper(QueueDragCallback(playbackModel))
|
||||
val queueAdapter = QueueAdapter(helper)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
@ -47,9 +47,19 @@ class QueueFragment : BottomSheetDialogFragment() {
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
playbackModel.mode.observe(viewLifecycleOwner) {
|
||||
if (it == PlaybackMode.ALL_SONGS) {
|
||||
binding.queueHeader.setText(R.string.label_next_songs)
|
||||
} else {
|
||||
binding.queueHeader.text = getString(
|
||||
R.string.format_next_from, playbackModel.parent.value!!.name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
||||
if (it.isEmpty()) {
|
||||
dismiss()
|
||||
findNavController().navigateUp()
|
||||
|
||||
return@observe
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ class PlaybackStateManager private constructor() {
|
|||
callbacks.forEach { it.onPositionUpdate(value) }
|
||||
}
|
||||
private var mParent: BaseModel? = null
|
||||
set(value) {
|
||||
field = value
|
||||
callbacks.forEach { it.onParentUpdate(value) }
|
||||
}
|
||||
|
||||
// Queue
|
||||
private var mQueue = mutableListOf<Song>()
|
||||
|
@ -47,6 +51,7 @@ class PlaybackStateManager private constructor() {
|
|||
callbacks.forEach { it.onModeUpdate(value) }
|
||||
}
|
||||
|
||||
// Status
|
||||
private var mIsPlaying = false
|
||||
set(value) {
|
||||
field = value
|
||||
|
@ -131,6 +136,7 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
|
||||
fun playParentModel(baseModel: BaseModel, shuffled: Boolean) {
|
||||
// This should never occur.
|
||||
if (baseModel is Song || baseModel is Header) {
|
||||
Log.e(
|
||||
this::class.simpleName,
|
||||
|
@ -295,8 +301,7 @@ class PlaybackStateManager private constructor() {
|
|||
// If specified, make the current song the first member of the queue.
|
||||
if (keepSong) {
|
||||
mSong?.let {
|
||||
mQueue.remove(it)
|
||||
mQueue.add(0, it)
|
||||
moveQueueItems(mQueue.indexOf(it), 0)
|
||||
}
|
||||
} else {
|
||||
// Otherwise, just start from the zeroth position in the queue.
|
||||
|
@ -400,6 +405,7 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
interface Callback {
|
||||
fun onSongUpdate(song: Song?) {}
|
||||
fun onParentUpdate(parent: BaseModel?) {}
|
||||
fun onPositionUpdate(position: Long) {}
|
||||
fun onQueueUpdate(queue: MutableList<Song>) {}
|
||||
fun onModeUpdate(mode: PlaybackMode) {}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -115,5 +115,14 @@
|
|||
android:id="@+id/playback_fragment"
|
||||
android:name="org.oxycblt.auxio.playback.PlaybackFragment"
|
||||
android:label="PlaybackFragment"
|
||||
tools:layout="@layout/fragment_playback" />
|
||||
tools:layout="@layout/fragment_playback" >
|
||||
<action
|
||||
android:id="@+id/action_show_queue"
|
||||
app:destination="@id/queue_fragment" />
|
||||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/queue_fragment"
|
||||
android:name="org.oxycblt.auxio.playback.queue.QueueFragment"
|
||||
android:label="QueueFragment"
|
||||
tools:layout="@layout/fragment_queue"/>
|
||||
</navigation>
|
|
@ -27,6 +27,7 @@
|
|||
<string name="label_play">Play</string>
|
||||
<string name="label_queue">Queue</string>
|
||||
<string name="label_queue_add">Add to queue</string>
|
||||
<string name="label_next_songs">Next from: All Songs</string>
|
||||
<string name="label_notification_playback">Music Playback</string>
|
||||
<string name="label_service_playback">The music playback service for Auxio.</string>
|
||||
|
||||
|
@ -62,6 +63,7 @@
|
|||
<string name="format_info">%1$s / %2$s</string>
|
||||
<string name="format_double_info">%1$s / %2$s / %3$s</string>
|
||||
<string name="format_double_counts">%1$s, %2$s</string>
|
||||
<string name="format_next_from">Next From: %s</string>
|
||||
|
||||
<plurals name="format_song_count">
|
||||
<item quantity="one">%s Song</item>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</style>
|
||||
|
||||
<!--
|
||||
Hack to get QueueFragment to not overlap the Status Bar or Navigation Bar
|
||||
Fix to get QueueFragment to not overlap the Status Bar or Navigation Bar
|
||||
https://stackoverflow.com/a/57790787/14143986
|
||||
-->
|
||||
<style name="Theme.BottomSheetFix" parent="@style/Theme.Design.BottomSheetDialog">
|
||||
|
|
Loading…
Reference in a new issue