main: add proper backwards navigation

Move the backwards navigation hook from DetailFragment to MainFragment
and modify it so that it collapses the playback panel when one navs
back.
This commit is contained in:
OxygenCobalt 2021-11-25 09:48:06 -07:00
parent 805035f0d8
commit 2ae22500d3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 73 additions and 50 deletions

View file

@ -24,10 +24,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
@ -41,12 +44,12 @@ import org.oxycblt.auxio.util.logD
* A wrapper around the home fragment that shows the playback fragment and controls * A wrapper around the home fragment that shows the playback fragment and controls
* the more high-level navigation features. * the more high-level navigation features.
* @author OxygenCobalt * @author OxygenCobalt
* TODO: Handle backnav with playback view
*/ */
class MainFragment : Fragment(), PlaybackLayout.ActionCallback { class MainFragment : Fragment(), PlaybackLayout.ActionCallback {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels()
private val musicModel: MusicViewModel by activityViewModels() private val musicModel: MusicViewModel by activityViewModels()
private var callback: Callback? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -66,24 +69,31 @@ class MainFragment : Fragment(), PlaybackLayout.ActionCallback {
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
Callback(binding).also {
callback = it
}
)
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
binding.mainBarLayout.setActionCallback(this) binding.playbackLayout.setActionCallback(this)
binding.mainBarLayout.setSong(playbackModel.song.value) binding.playbackLayout.setSong(playbackModel.song.value)
binding.mainBarLayout.setPlaying(playbackModel.isPlaying.value!!) binding.playbackLayout.setPlaying(playbackModel.isPlaying.value!!)
binding.mainBarLayout.setPosition(playbackModel.position.value!!) binding.playbackLayout.setPosition(playbackModel.position.value!!)
playbackModel.song.observe(viewLifecycleOwner) { song -> playbackModel.song.observe(viewLifecycleOwner) { song ->
binding.mainBarLayout.setSong(song) binding.playbackLayout.setSong(song)
} }
playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying -> playbackModel.isPlaying.observe(viewLifecycleOwner) { isPlaying ->
binding.mainBarLayout.setPlaying(isPlaying) binding.playbackLayout.setPlaying(isPlaying)
} }
playbackModel.position.observe(viewLifecycleOwner) { pos -> playbackModel.position.observe(viewLifecycleOwner) { pos ->
binding.mainBarLayout.setPosition(pos) binding.playbackLayout.setPosition(pos)
} }
// Initialize music loading. Do it here so that it shows on every fragment that this // Initialize music loading. Do it here so that it shows on every fragment that this
@ -144,6 +154,23 @@ class MainFragment : Fragment(), PlaybackLayout.ActionCallback {
return binding.root return binding.root
} }
override fun onResume() {
super.onResume()
callback?.isEnabled = true
}
override fun onPause() {
super.onPause()
callback?.isEnabled = false
}
override fun onDestroyView() {
super.onDestroyView()
// This callback has access to the binding, so make sure we clear it when we're done.
callback = null
}
override fun onNavToItem() { override fun onNavToItem() {
detailModel.navToItem(playbackModel.song.value ?: return) detailModel.navToItem(playbackModel.song.value ?: return)
} }
@ -159,4 +186,24 @@ class MainFragment : Fragment(), PlaybackLayout.ActionCallback {
override fun onNext() { override fun onNext() {
playbackModel.skipNext() playbackModel.skipNext()
} }
/**
* A back press callback that handles how to respond to backwards navigation in the detail
* fragments and the playback panel.
*/
inner class Callback(private val binding: FragmentMainBinding) : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
if (!binding.playbackLayout.collapse()) {
val navController = binding.exploreNavHost.findNavController()
if (navController.currentDestination?.id == navController.graph.startDestination) {
isEnabled = false
requireActivity().onBackPressed()
isEnabled = true
} else {
navController.navigateUp()
}
}
}
}
} }

View file

@ -18,9 +18,6 @@
package org.oxycblt.auxio.detail package org.oxycblt.auxio.detail
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.forEach import androidx.core.view.forEach
@ -44,22 +41,12 @@ abstract class DetailFragment : Fragment() {
protected val playbackModel: PlaybackViewModel by activityViewModels() protected val playbackModel: PlaybackViewModel by activityViewModels()
protected val binding by memberBinding(FragmentDetailBinding::inflate) protected val binding by memberBinding(FragmentDetailBinding::inflate)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
callback.isEnabled = true
detailModel.setNavigating(false) detailModel.setNavigating(false)
} }
override fun onPause() {
super.onPause()
callback.isEnabled = false
}
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
@ -149,21 +136,4 @@ abstract class DetailFragment : Fragment() {
show() show()
} }
} }
// Override the back button so that going back will only exit the detail fragments instead of
// the entire app.
private val callback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
val navController = findNavController()
// Check if it's the root of nested fragments in this NavHost
if (navController.currentDestination?.id == navController.graph.startDestination) {
isEnabled = false
requireActivity().onBackPressed()
isEnabled = true
} else {
navController.navigateUp()
}
}
}
} }

View file

@ -23,6 +23,7 @@ import com.google.android.material.shape.MaterialShapeDrawable
import org.oxycblt.auxio.BuildConfig import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.resolveAttr import org.oxycblt.auxio.util.resolveAttr
import org.oxycblt.auxio.util.systemBarsCompat import org.oxycblt.auxio.util.systemBarsCompat
import kotlin.math.abs import kotlin.math.abs
@ -198,11 +199,17 @@ class PlaybackLayout @JvmOverloads constructor(
/** /**
* Collapse the panel if it is currently expanded. * Collapse the panel if it is currently expanded.
* @return If the panel was collapsed or not.
*/ */
fun collapse() { fun collapse(): Boolean {
logD(panelState)
if (panelState == PanelState.EXPANDED) { if (panelState == PanelState.EXPANDED) {
applyState(PanelState.COLLAPSED) applyState(PanelState.COLLAPSED)
logD("I AM EXPANDED WILL COLLAPSE")
return true
} }
return false
} }
private fun applyState(state: PanelState) { private fun applyState(state: PanelState) {

View file

@ -5,10 +5,9 @@
tools:context=".MainFragment"> tools:context=".MainFragment">
<org.oxycblt.auxio.playback.PlaybackLayout <org.oxycblt.auxio.playback.PlaybackLayout
android:id="@+id/main_bar_layout" android:id="@+id/playback_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/explore_nav_host" android:id="@+id/explore_nav_host"

View file

@ -18,17 +18,17 @@
<action <action
android:id="@+id/action_show_settings" android:id="@+id/action_show_settings"
app:destination="@id/settings_fragment" app:destination="@id/settings_fragment"
app:enterAnim="@anim/anim_nav_slide_up" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/anim_stationary" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/anim_stationary" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/anim_nav_slide_down" /> app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action <action
android:id="@+id/action_show_about" android:id="@+id/action_show_about"
app:destination="@id/about_fragment" app:destination="@id/about_fragment"
app:enterAnim="@anim/anim_nav_slide_up" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/anim_stationary" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/anim_stationary" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/anim_nav_slide_down" /> app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment> </fragment>
<fragment <fragment