diff options
3 files changed, 66 insertions, 8 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt index 8751aa31f237..17423e235af6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt @@ -94,6 +94,8 @@ import androidx.compose.ui.graphics.drawscope.clipRect import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.Layout @@ -663,11 +665,20 @@ private fun ContentScope.Navigation( if (isSeekBarVisible) { // To allow the seek bar slider to fade in and out, it's tagged as an element. Element(key = Media.Elements.SeekBarSlider, modifier = Modifier.weight(1f)) { + val sliderDragDelta = remember { + // Not a mutableStateOf - this is never accessed in composition and + // using an anonymous object avoids generics boxing of inline Offset. + object { + var value = Offset.Zero + } + } Slider( interactionSource = interactionSource, value = viewModel.progress, onValueChange = { progress -> viewModel.onScrubChange(progress) }, - onValueChangeFinished = { viewModel.onScrubFinished() }, + onValueChangeFinished = { + viewModel.onScrubFinished(sliderDragDelta.value) + }, colors = colors, thumb = { SeekBarThumb(interactionSource = interactionSource, colors = colors) @@ -681,9 +692,43 @@ private fun ContentScope.Navigation( ) }, modifier = - Modifier.fillMaxWidth().clearAndSetSemantics { - contentDescription = viewModel.contentDescription - }, + Modifier.fillMaxWidth() + .clearAndSetSemantics { + contentDescription = viewModel.contentDescription + } + .pointerInput(Unit) { + // Track and report the drag delta to the view-model so it + // can + // decide whether to accept the next onValueChangeFinished + // or + // reject it if the drag was overly vertical. + awaitPointerEventScope { + var down: PointerInputChange? = null + while (true) { + val event = + awaitPointerEvent(PointerEventPass.Initial) + when (event.type) { + PointerEventType.Press -> { + // A new gesture has begun. Record the + // initial + // down input change. + down = event.changes.last() + } + + PointerEventType.Move -> { + // The pointer has moved. If it's the same + // pointer as the latest down, calculate and + // report the drag delta. + val change = event.changes.last() + if (change.id == down?.id) { + sliderDragDelta.value = + change.position - down.position + } + } + } + } + } + }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt index ca7334341c87..a3689926e937 100644 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaNavigationViewModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.remedia.ui.viewmodel import androidx.annotation.FloatRange +import androidx.compose.ui.geometry.Offset /** * Models UI state for the navigation component of the UI (potentially containing the seek bar and @@ -58,7 +59,7 @@ sealed interface MediaNavigationViewModel { * A callback to invoke once the user finishes "scrubbing" (e.g. stopped moving the thumb of * the seek bar). The position/progress should be committed. */ - val onScrubFinished: () -> Unit, + val onScrubFinished: (delta: Offset) -> Unit, /** Accessibility string to attach to the seekbar UI element. */ val contentDescription: String, ) : MediaNavigationViewModel diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt index 15951165a19e..19b08fa212db 100644 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt @@ -26,9 +26,9 @@ import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.ImageBitmap import com.android.systemui.classifier.Classifier -import com.android.systemui.classifier.domain.interactor.runIfNotFalseTap import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.lifecycle.ExclusiveActivatable @@ -42,6 +42,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.Locale +import kotlin.math.abs import kotlin.math.roundToLong import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.awaitCancellation @@ -114,8 +115,11 @@ constructor( isScrubbing = true seekProgress = progress }, - onScrubFinished = { - if (!falsingSystem.isFalseTouch(Classifier.MEDIA_SEEKBAR)) { + onScrubFinished = { dragDelta -> + if ( + dragDelta.isHorizontal() && + !falsingSystem.isFalseTouch(Classifier.MEDIA_SEEKBAR) + ) { interactor.seek( sessionKey = session.key, to = (seekProgress * session.durationMs).roundToLong(), @@ -346,6 +350,14 @@ constructor( .formatMeasures(*measures.toTypedArray()) } + /** + * Returns `true` if this [Offset] is the same or larger on the horizontal axis than the + * vertical axis. + */ + private fun Offset.isHorizontal(): Boolean { + return abs(x) >= abs(y) + } + interface FalsingSystem { fun runIfNotFalseTap(@FalsingManager.Penalty penalty: Int, block: () -> Unit) |