From a9515e19c08dac271a647b70bbeb1f48caae5280 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Wed, 3 Aug 2022 19:54:14 -0600 Subject: [PATCH] all: cleanup Clean up the slightly rushed changes I made as I worked on the split UI. --- .../bottomsheet/NeoBottomSheetBehavior.java | 24 ++-- .../java/org/oxycblt/auxio/MainActivity.kt | 4 + .../java/org/oxycblt/auxio/MainFragment.kt | 130 ++++++++---------- .../oxycblt/auxio/detail/DetailViewModel.kt | 4 +- .../auxio/playback/PlaybackSheetBehavior.kt | 2 +- .../auxio/playback/PlaybackViewModel.kt | 9 +- .../playback/state/PlaybackStateManager.kt | 45 +++--- .../oxycblt/auxio/ui/AuxioSheetBehavior.kt | 7 +- .../auxio/ui/BottomSheetContentBehavior.kt | 2 - .../oxycblt/auxio/widgets/WidgetComponent.kt | 2 +- 10 files changed, 114 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/com/google/android/material/bottomsheet/NeoBottomSheetBehavior.java b/app/src/main/java/com/google/android/material/bottomsheet/NeoBottomSheetBehavior.java index 79f3f5c27..91d13bca6 100644 --- a/app/src/main/java/com/google/android/material/bottomsheet/NeoBottomSheetBehavior.java +++ b/app/src/main/java/com/google/android/material/bottomsheet/NeoBottomSheetBehavior.java @@ -83,7 +83,7 @@ import java.util.Map; * window-like. For BottomSheetDialog use {@link BottomSheetDialog#setTitle(int)}, and for * BottomSheetDialogFragment use {@link ViewCompat#setAccessibilityPaneTitle(View, CharSequence)}. * - * Modified at several points by OxygenCobalt to fix insane issues. + * Modified at several points by OxygenCobalt to work around miscellaneous insanity. */ public class NeoBottomSheetBehavior extends CoordinatorLayout.Behavior { @@ -720,8 +720,8 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be } } else if (dy < 0) { // Downward if (!target.canScrollVertically(-1)) { - // MODIFICATION: Add enableHidingGestures method - if (newTop <= collapsedOffset || (hideable && enableHidingGestures())) { + // MODIFICATION: Add isHideableWhenDragging method + if (newTop <= collapsedOffset || (hideable && isHideableWhenDragging())) { if (!draggable) { // Prevent dragging return; @@ -775,8 +775,8 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be } } } - // MODIFICATION: Add enableHidingGestures method - } else if (hideable && shouldHide(child, getYVelocity()) && enableHidingGestures()) { + // MODIFICATION: Add isHideableWhenDragging method + } else if (hideable && shouldHide(child, getYVelocity()) && isHideableWhenDragging()) { targetState = STATE_HIDDEN; } else if (lastNestedScrollDy == 0) { int currentTop = child.getTop(); @@ -1725,8 +1725,8 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be } } } - // MODIFICATION: Add enableHidingGestures method - } else if (hideable && shouldHide(releasedChild, yvel) && enableHidingGestures()) { + // MODIFICATION: Add isHideableWhenDragging method + } else if (hideable && shouldHide(releasedChild, yvel) && isHideableWhenDragging()) { // Hide if the view was either released low or it was a significant vertical swipe // otherwise settle to closest expanded state. if ((Math.abs(xvel) < Math.abs(yvel) && yvel > SIGNIFICANT_VEL_THRESHOLD) @@ -1798,9 +1798,9 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be @Override public int clampViewPositionVertical(@NonNull View child, int top, int dy) { - // MODIFICATION: Add enableHidingGestures method + // MODIFICATION: Add isHideableWhenDragging method return MathUtils.clamp( - top, getExpandedOffset(), (hideable && enableHidingGestures()) ? parentHeight : collapsedOffset); + top, getExpandedOffset(), (hideable && isHideableWhenDragging()) ? parentHeight : collapsedOffset); } @Override @@ -1810,8 +1810,8 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be @Override public int getViewVerticalDragRange(@NonNull View child) { - // MODIFICATION: Add enableHidingGestures method - if (hideable && enableHidingGestures()) { + // MODIFICATION: Add isHideableWhenDragging method + if (hideable && isHideableWhenDragging()) { return parentHeight; } else { return collapsedOffset; @@ -1886,7 +1886,7 @@ public class NeoBottomSheetBehavior extends CoordinatorLayout.Be * @hide */ @RestrictTo(LIBRARY_GROUP) - public boolean enableHidingGestures() { + public boolean isHideableWhenDragging() { return true; } diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 05fdf113a..c6165759c 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -44,6 +44,10 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat * * TODO: Add multi-select * + * TODO: Separate playback views by height + * + * TODO: Find better way to handler recycler divider visibility + * * @author OxygenCobalt */ class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 6263566ec..2c6c2db88 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -76,9 +76,7 @@ class MainFragment : val playbackSheetBehavior = binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior - val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? - if (queueSheetBehavior != null) { unlikelyToBeNull(binding.handleWrapper).setOnClickListener { if (playbackSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED && @@ -87,6 +85,8 @@ class MainFragment : } } } else { + // Dual-pane mode, color/pad the queue sheet manually. Note that we do not round + // corners, as the queue sheet cannot be dragged. binding.queueSheet.apply { background = MaterialShapeDrawable.createWithElevationOverlay(context).apply { @@ -133,8 +133,10 @@ class MainFragment : override fun onPreDraw(): Boolean { // CoordinatorLayout is insane and thus makes bottom sheet callbacks insane. Do our - // checks before every draw. + // checks before every draw, which is not ideal in the slightest but also has minimal + // performance impact since we are only mutating attributes used during drawing. val binding = requireBinding() + val playbackSheetBehavior = binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior @@ -144,6 +146,30 @@ class MainFragment : val halfOutRatio = min(playbackRatio * 2, 1f) val halfInPlaybackRatio = max(playbackRatio - 0.5f, 0f) * 2 + val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? + + if (queueSheetBehavior != null) { + // Queue sheet, take queue into account so the playback bar is shown and the playback + // panel is hidden when the queue sheet is expanded. + val queueRatio = max(queueSheetBehavior.calculateSlideOffset(), 0f) + val halfOutQueueRatio = min(queueRatio * 2, 1f) + val halfInQueueRatio = max(queueRatio - 0.5f, 0f) * 2 + binding.playbackBarFragment.alpha = max(1 - halfOutRatio, halfInQueueRatio) + binding.playbackPanelFragment.alpha = min(halfInPlaybackRatio, 1 - halfOutQueueRatio) + binding.queueFragment.alpha = queueRatio + + if (playbackModel.song.value != null) { + // Hack around the playback sheet intercepting swipe events on the queue bar + playbackSheetBehavior.isDraggable = + queueSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED + } + } else { + // No queue sheet, fade normally based on the playback sheet + binding.playbackBarFragment.alpha = 1 - halfOutRatio + binding.playbackPanelFragment.alpha = halfInPlaybackRatio + binding.queueSheet.alpha = halfInPlaybackRatio + } + binding.exploreNavHost.apply { alpha = outPlaybackRatio isInvisible = alpha == 0f @@ -153,61 +179,18 @@ class MainFragment : requireContext().getDimenSafe(R.dimen.elevation_normal) * outPlaybackRatio playbackSheetBehavior.sheetBackgroundDrawable.alpha = (outPlaybackRatio * 255).toInt() - val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? + binding.playbackBarFragment.apply { + isInvisible = alpha == 0f + lastInsets?.let { translationY = it.systemBarInsetsCompat.top * halfOutRatio } + } - if (queueSheetBehavior != null) { - val queueRatio = max(queueSheetBehavior.calculateSlideOffset(), 0f) - val halfOutQueueRatio = min(queueRatio * 2, 1f) - val halfInQueueRatio = max(queueRatio - 0.5f, 0f) * 2 + binding.playbackPanelFragment.isInvisible = binding.playbackPanelFragment.alpha == 0f + binding.queueFragment.isInvisible = binding.queueFragment.alpha == 0f - binding.playbackBarFragment.apply { - alpha = max(1 - halfOutRatio, halfInQueueRatio) - isInvisible = alpha == 0f - lastInsets?.let { translationY = it.systemBarInsetsCompat.top * halfOutRatio } - } - - binding.playbackPanelFragment.apply { - alpha = min(halfInPlaybackRatio, 1 - halfOutQueueRatio) - isInvisible = alpha == 0f - } - - binding.queueFragment.apply { - alpha = queueRatio - isInvisible = alpha == 0f - } - - if (playbackModel.song.value != null) { - // Hack around the playback sheet intercepting swipe events on the queue bar - playbackSheetBehavior.isDraggable = - queueSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED - } else { - // Sometimes lingering drags can un-hide the playback sheet even when we intend to - // hide it, make sure we keep it hidden. - tryHideAll() - } - } else { - - binding.playbackBarFragment.apply { - alpha = 1 - halfOutRatio - isInvisible = alpha == 0f - lastInsets?.let { translationY = it.systemBarInsetsCompat.top * halfOutRatio } - } - - binding.playbackPanelFragment.apply { - alpha = halfInPlaybackRatio - isInvisible = alpha == 0f - } - - binding.queueSheet.apply { - alpha = halfInPlaybackRatio - isInvisible = alpha == 0f - } - - if (playbackModel.song.value == null) { - // Sometimes lingering drags can un-hide the playback sheet even when we intend to - // hide it, make sure we keep it hidden. - tryHideAll() - } + if (playbackModel.song.value == null) { + // Sometimes lingering drags can un-hide the playback sheet even when we intend to + // hide it, make sure we keep it hidden. + tryHideAll() } return true @@ -250,8 +233,8 @@ class MainFragment : val playbackSheetBehavior = binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior - if (playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN && - playbackSheetBehavior.state != BottomSheetBehavior.STATE_EXPANDED) { + if (playbackSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) { + // State is collapsed and non-hidden, expand playbackSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED } } @@ -261,8 +244,8 @@ class MainFragment : val playbackSheetBehavior = binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior - if (playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN && - playbackSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) { + if (playbackSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { + // Make sure the queue is also collapsed here. val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? @@ -277,13 +260,17 @@ class MainFragment : binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior if (playbackSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN) { - playbackSheetBehavior.isDraggable = true - playbackSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED - val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? + // Queue sheet behavior is either collapsed or expanded, no hiding needed queueSheetBehavior?.isDraggable = true + + playbackSheetBehavior.apply { + // Make sure the view is draggable, at least until the draw checks kick in. + isDraggable = true + state = BottomSheetBehavior.STATE_COLLAPSED + } } } @@ -296,6 +283,8 @@ class MainFragment : val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? + // Make these views non-draggable so the user can't halt the hiding event. + queueSheetBehavior?.apply { isDraggable = false state = BottomSheetBehavior.STATE_COLLAPSED @@ -314,30 +303,33 @@ class MainFragment : * * TODO: Migrate to new predictive API */ - inner class DynamicBackPressedCallback : OnBackPressedCallback(false) { + private inner class DynamicBackPressedCallback : OnBackPressedCallback(false) { override fun handleOnBackPressed() { val binding = requireBinding() + val playbackSheetBehavior = + binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior + val queueSheetBehavior = binding.queueSheet.coordinatorLayoutBehavior as QueueSheetBehavior? if (queueSheetBehavior != null && - queueSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) { + queueSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED && + playbackSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { + // Collapse the queue first if it is expanded. queueSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED return } - val playbackSheetBehavior = - binding.playbackSheet.coordinatorLayoutBehavior as PlaybackSheetBehavior - if (playbackSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED && playbackSheetBehavior.state != BottomSheetBehavior.STATE_HIDDEN) { + // Then collapse the playback sheet. playbackSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED return } + // First navigate upwards in the explore graph, then navigate back in the activity. val navController = binding.exploreNavHost.findNavController() - if (navController.currentDestination?.id == navController.graph.startDestinationId) { isEnabled = false requireActivity().onBackPressed() diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 9393b185a..21de2a091 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -246,6 +246,8 @@ class DetailViewModel(application: Application) : } when (it.releaseType.refinement) { + ReleaseType.Refinement.LIVE -> R.string.lbl_live_group + ReleaseType.Refinement.REMIX -> R.string.lbl_remix_group null -> when (it.releaseType) { is ReleaseType.Album -> R.string.lbl_albums @@ -255,8 +257,6 @@ class DetailViewModel(application: Application) : is ReleaseType.Soundtrack -> R.string.lbl_soundtracks is ReleaseType.Mixtape -> R.string.lbl_mixtapes } - ReleaseType.Refinement.LIVE -> R.string.lbl_live_group - ReleaseType.Refinement.REMIX -> R.string.lbl_remix_group } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSheetBehavior.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSheetBehavior.kt index a89a592da..b2c96e648 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSheetBehavior.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSheetBehavior.kt @@ -41,5 +41,5 @@ class PlaybackSheetBehavior(context: Context, attributeSet: AttributeS super.onInterceptTouchEvent(parent, child, event) && state != STATE_EXPANDED // Note: This is an extension to Auxio's vendored BottomSheetBehavior - override fun enableHidingGestures() = false + override fun isHideableWhenDragging() = false } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index be71292ff..8f480902f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -156,10 +156,9 @@ class PlaybackViewModel(application: Application) : private fun performActionImpl(action: DelayedAction, library: MusicStore.Library) { when (action) { is DelayedAction.RestoreState -> { - if (!playbackManager.isInitialized) { - viewModelScope.launch { - playbackManager.restoreState(PlaybackStateDatabase.getInstance(application)) - } + viewModelScope.launch { + playbackManager.restoreState( + PlaybackStateDatabase.getInstance(application), false) } } is DelayedAction.ShuffleAll -> shuffleAll() @@ -273,7 +272,7 @@ class PlaybackViewModel(application: Application) : fun tryRestorePlaybackState(onDone: (Boolean) -> Unit) { viewModelScope.launch { val restored = - playbackManager.restoreState(PlaybackStateDatabase.getInstance(application)) + playbackManager.restoreState(PlaybackStateDatabase.getInstance(application), true) onDone(restored) } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 341e6702b..91763ea8f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -357,36 +357,37 @@ class PlaybackStateManager private constructor() { // --- PERSISTENCE FUNCTIONS --- /** Restore the state from the [database]. Returns if a state was restored. */ - suspend fun restoreState(database: PlaybackStateDatabase): Boolean { + suspend fun restoreState(database: PlaybackStateDatabase, force: Boolean): Boolean { + if (isInitialized && !force) { + return false + } + val library = musicStore.library ?: return false val state = withContext(Dispatchers.IO) { database.read(library) } synchronized(this) { - val exists = - if (state != null && !isInitialized) { - // Continuing playback while also possibly doing drastic state updates is - // a bad idea, so pause. - isPlaying = false + if (state != null && (!isInitialized || force)) { + // Continuing playback while also possibly doing drastic state updates is + // a bad idea, so pause. + isPlaying = false - index = state.index - parent = state.parent - _queue = state.queue.toMutableList() - repeatMode = state.repeatMode - isShuffled = state.isShuffled + index = state.index + parent = state.parent + _queue = state.queue.toMutableList() + repeatMode = state.repeatMode + isShuffled = state.isShuffled - notifyNewPlayback() - seekTo(state.positionMs) - notifyRepeatModeChanged() - notifyShuffledChanged() + notifyNewPlayback() + seekTo(state.positionMs) + notifyRepeatModeChanged() + notifyShuffledChanged() - true - } else { - false - } + isInitialized = true - isInitialized = true - - return exists + return true + } else { + return false + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/AuxioSheetBehavior.kt b/app/src/main/java/org/oxycblt/auxio/ui/AuxioSheetBehavior.kt index 32f0efd4e..79e2c6a56 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/AuxioSheetBehavior.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/AuxioSheetBehavior.kt @@ -31,7 +31,8 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.util.* /** - * Implements the fundamental bottom sheet attributes used across the entire app. + * Implements a reasonable enough skeleton around BottomSheetBehavior (Excluding auxio extensions in + * the vendored code because I course I have to) for normal use without absurd bugs. * @author OxygenCobalt */ abstract class AuxioSheetBehavior(context: Context, attributeSet: AttributeSet?) : @@ -86,6 +87,10 @@ abstract class AuxioSheetBehavior(context: Context, attributeSet: Attr setup = true } + // Sometimes CoordinatorLayout tries to be "hElpfUl" and just does not dispatch window + // insets sometimes. Ensure that we get them. + child.requestApplyInsets() + return layout } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetContentBehavior.kt b/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetContentBehavior.kt index 7bde30601..a2a118584 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetContentBehavior.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/BottomSheetContentBehavior.kt @@ -120,8 +120,6 @@ class BottomSheetContentBehavior(context: Context, attributeSet: Attri setup = true } - child.requestApplyInsets() - return true } diff --git a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt index 87f686c03..76aada3d0 100644 --- a/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt +++ b/app/src/main/java/org/oxycblt/auxio/widgets/WidgetComponent.kt @@ -98,7 +98,7 @@ class WidgetComponent(private val context: Context) : // limit, which is the size of an RGB_8888 bitmap 1.5x the screen size. When // enabling rounded corners, we further reduce it by a factor of 8 to get 16-dp // rounded corners, whereas we only downsize it by 2 when there is rounded - // corners just to ensure that we do not hit the memory limit. + // corners just to ensure that we *really* do not hit the memory limit. val metrics = context.resources.displayMetrics val sw = metrics.widthPixels val sh = metrics.heightPixels