diff options
15 files changed, 216 insertions, 26 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt index d4f3b5b6d6a6..28a12f813cf5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt @@ -78,9 +78,7 @@ fun ColumnVolumeSliders( ) { require(viewModels.isNotEmpty()) Column(modifier = modifier) { - Box( - modifier = Modifier.fillMaxWidth(), - ) { + Box(modifier = Modifier.fillMaxWidth()) { val sliderViewModel: SliderViewModel = viewModels.first() val sliderState by viewModels.first().slider.collectAsStateWithLifecycle() val sliderPadding by topSliderPadding(isExpandable) @@ -94,6 +92,7 @@ fun ColumnVolumeSliders( onValueChangeFinished = { sliderViewModel.onValueChangeFinished() }, onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, sliderColors = sliderColors, + hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory, ) ExpandButton( @@ -143,6 +142,7 @@ fun ColumnVolumeSliders( onValueChangeFinished = { sliderViewModel.onValueChangeFinished() }, onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, sliderColors = sliderColors, + hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory, ) } } @@ -181,7 +181,7 @@ private fun ExpandButton( colors = IconButtonDefaults.filledIconButtonColors( containerColor = sliderColors.indicatorColor, - contentColor = sliderColors.iconColor + contentColor = sliderColors.iconColor, ), ) { Icon( @@ -211,9 +211,7 @@ private fun enterTransition(index: Int, totalCount: Int): EnterTransition { animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay), clip = false, ) + - fadeIn( - animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay), - ) + fadeIn(animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay)) } private fun exitTransition(index: Int, totalCount: Int): ExitTransition { @@ -286,6 +284,6 @@ private fun topSliderPadding(isExpandable: Boolean): State<Dp> { 0.dp }, animationSpec = animationSpec, - label = "TopVolumeSliderPadding" + label = "TopVolumeSliderPadding", ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt index d15430faa0a0..a0e46d51c73a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt @@ -49,6 +49,7 @@ fun GridVolumeSliders( onValueChangeFinished = { sliderViewModel.onValueChangeFinished() }, onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, sliderColors = sliderColors, + hapticsViewModelFactory = sliderViewModel.hapticsViewModelFactory, ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index a23bb67215b5..eb79b906e5f8 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -22,6 +22,8 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.size @@ -46,9 +48,14 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors +import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.compose.modifiers.sysuiResTag +import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig +import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel +import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState @Composable @@ -59,8 +66,40 @@ fun VolumeSlider( onIconTapped: () -> Unit, modifier: Modifier = Modifier, sliderColors: PlatformSliderColors, + hapticsViewModelFactory: SliderHapticsViewModel.Factory, ) { val value by valueState(state) + val interactionSource = remember { MutableInteractionSource() } + val sliderStepSize = 1f / (state.valueRange.endInclusive - state.valueRange.start) + val hapticsViewModel: SliderHapticsViewModel? = + if (Flags.hapticsForComposeSliders()) { + rememberViewModel(traceName = "SliderHapticsViewModel") { + hapticsViewModelFactory.create( + interactionSource, + state.valueRange, + Orientation.Horizontal, + SliderHapticFeedbackConfig( + lowerBookendScale = 0.2f, + progressBasedDragMinScale = 0.2f, + progressBasedDragMaxScale = 0.5f, + deltaProgressForDragThreshold = 0f, + additionalVelocityMaxBump = 0.2f, + maxVelocityToScale = 0.1f, /* slider progress(from 0 to 1) per sec */ + sliderStepSize = sliderStepSize, + ), + SeekableSliderTrackerConfig( + lowerBookendThreshold = 0f, + upperBookendThreshold = 1f, + ), + ) + } + } else { + null + } + + // Perform haptics due to UI composition + hapticsViewModel?.onValueChange(value) + PlatformSlider( modifier = modifier.sysuiResTag(state.label).clearAndSetSemantics { @@ -94,7 +133,7 @@ fun VolumeSlider( val newValue = (value + targetDirection * state.a11yStep).coerceIn( state.valueRange.start, - state.valueRange.endInclusive + state.valueRange.endInclusive, ) onValueChange(newValue) true @@ -102,16 +141,18 @@ fun VolumeSlider( }, value = value, valueRange = state.valueRange, - onValueChange = onValueChange, - onValueChangeFinished = onValueChangeFinished, + onValueChange = { newValue -> + hapticsViewModel?.addVelocityDataPoint(newValue) + onValueChange(newValue) + }, + onValueChangeFinished = { + hapticsViewModel?.onValueChangeEnded() + onValueChangeFinished?.invoke() + }, enabled = state.isEnabled, icon = { state.icon?.let { - SliderIcon( - icon = it, - onIconTapped = onIconTapped, - isTappable = state.isMutable, - ) + SliderIcon(icon = it, onIconTapped = onIconTapped, isTappable = state.isMutable) } }, colors = sliderColors, @@ -128,7 +169,8 @@ fun VolumeSlider( disabledMessage = state.disabledMessage, ) } - } + }, + interactionSource = interactionSource, ) } @@ -150,14 +192,14 @@ private fun SliderIcon( icon: Icon, onIconTapped: () -> Unit, isTappable: Boolean, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val boxModifier = if (isTappable) { modifier.clickable( onClick = onIconTapped, interactionSource = null, - indication = null + indication = null, ) } else { modifier diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt index 3467382df4da..75fd56658d9d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt @@ -47,7 +47,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope - private val config = SliderHapticFeedbackConfig() + private var config = SliderHapticFeedbackConfig() private val dragVelocityProvider = SliderDragVelocityProvider { config.maxVelocityToScale } @@ -227,6 +227,72 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtProgress_forDiscreteSlider_playsTick() = + with(kosmos) { + config = SliderHapticFeedbackConfig(sliderStepSize = 0.2f) + sliderHapticFeedbackProvider = + SliderHapticFeedbackProvider( + vibratorHelper, + msdlPlayer, + dragVelocityProvider, + config, + kosmos.fakeSystemClock, + ) + + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) + val tick = + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, expectedScale) + .compose() + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN called to play haptics + sliderHapticFeedbackProvider.onProgress(progress) + + // THEN the correct composition only plays once + assertEquals(expected = 1, vibratorHelper.timesVibratedWithEffect(tick)) + } + + @Test + @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) + fun playHapticAtProgress_forDiscreteSlider_playsDiscreteSliderToken() = + with(kosmos) { + config = SliderHapticFeedbackConfig(sliderStepSize = 0.2f) + sliderHapticFeedbackProvider = + SliderHapticFeedbackProvider( + vibratorHelper, + msdlPlayer, + dragVelocityProvider, + config, + kosmos.fakeSystemClock, + ) + + // GIVEN max velocity and slider progress + val progress = 1f + val expectedScale = + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) + val expectedProperties = + InteractionProperties.DynamicVibrationScale(expectedScale, pipeliningAttributes) + + // GIVEN system running for 1s + fakeSystemClock.advanceTime(1000) + + // WHEN called to play haptics + sliderHapticFeedbackProvider.onProgress(progress) + + // THEN the correct token plays once + assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.DRAG_INDICATOR_DISCRETE) + assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(expectedProperties) + assertThat(msdlPlayer.getHistory().size).isEqualTo(1) + } + + @Test @EnableFlags(Flags.FLAG_MSDL_FEEDBACK) fun playHapticAtProgress_onQuickSuccession_playsContinuousDragTokenOnce() = with(kosmos) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt index f80b36a10dc2..d3071f87f744 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt @@ -28,6 +28,7 @@ import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor @@ -73,6 +74,7 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { kosmos.zenModeInteractor, kosmos.uiEventLogger, kosmos.volumePanelLogger, + kosmos.sliderHapticsViewModelFactory, ) } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt index 24dd04dbadd2..da124de358a8 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt @@ -47,4 +47,6 @@ data class SliderHapticFeedbackConfig( @FloatRange(from = 0.0, to = 1.0) val lowerBookendScale: Float = 0.05f, /** Exponent for power function compensation */ @FloatRange(from = 0.0, fromInclusive = false) val exponent: Float = 1f / 0.89f, + /** The step-size that defines the slider quantization. Zero represents a continuous slider */ + @FloatRange(from = 0.0) val sliderStepSize: Float = 0f, ) diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt index bc4f531b8b81..de6697bd8fea 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt @@ -30,6 +30,7 @@ import com.google.android.msdl.domain.MSDLPlayer import kotlin.math.abs import kotlin.math.min import kotlin.math.pow +import kotlin.math.round /** * Listener of slider events that triggers haptic feedback. @@ -124,14 +125,45 @@ class SliderHapticFeedbackProvider( val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress) if (deltaProgress < config.deltaProgressForDragThreshold) return + // Check if the progress is a discrete step so haptics can be delivered + if ( + config.sliderStepSize > 0 && + !normalizedSliderProgress.isDiscreteStep(config.sliderStepSize) + ) { + return + } + val powerScale = scaleOnDragTexture(absoluteVelocity, normalizedSliderProgress) // Deliver haptic feedback - performContinuousSliderDragVibration(powerScale) + when { + config.sliderStepSize == 0f -> performContinuousSliderDragVibration(powerScale) + config.sliderStepSize > 0f -> performDiscreteSliderDragVibration(powerScale) + } dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } + private fun Float.isDiscreteStep(stepSize: Float, epsilon: Float = 0.001f): Boolean { + if (stepSize <= 0f) return false + val division = this / stepSize + return abs(division - round(division)) < epsilon + } + + private fun performDiscreteSliderDragVibration(scale: Float) { + if (Flags.msdlFeedback()) { + val properties = + InteractionProperties.DynamicVibrationScale(scale, VIBRATION_ATTRIBUTES_PIPELINING) + msdlPlayer.playToken(MSDLToken.DRAG_INDICATOR_DISCRETE, properties) + } else { + val effect = + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, scale) + .compose() + vibratorHelper.vibrate(effect, VIBRATION_ATTRIBUTES_PIPELINING) + } + } + private fun performContinuousSliderDragVibration(scale: Float) { if (Flags.msdlFeedback()) { val properties = diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt new file mode 100644 index 000000000000..033d55cc9b61 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderQuantization.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics.slider + +interface SliderQuantization { + /** What is the step size between discrete steps of the slider */ + val stepSize: Float + + data class Continuous(override val stepSize: Float = Float.MIN_VALUE) : SliderQuantization + + data class Discrete(override val stepSize: Float) : SliderQuantization +} diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt index de242597f463..7fa83c64d5eb 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt @@ -143,18 +143,20 @@ constructor( SliderEventType.STARTED_TRACKING_TOUCH -> { startingProgress = normalized currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER + sliderStateProducer.onProgressChanged(true, normalized) } SliderEventType.PROGRESS_CHANGE_BY_USER -> { - velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset()) + addVelocityDataPoint(value) currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER sliderStateProducer.onProgressChanged(true, normalized) } SliderEventType.STARTED_TRACKING_PROGRAM -> { startingProgress = normalized currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM + sliderStateProducer.onProgressChanged(false, normalized) } SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> { - velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset()) + addVelocityDataPoint(value) currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM sliderStateProducer.onProgressChanged(false, normalized) } @@ -162,6 +164,11 @@ constructor( } } + fun addVelocityDataPoint(value: Float) { + val normalized = value.normalize() + velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset()) + } + fun onValueChangeEnded() { when (currentSliderEventType) { SliderEventType.STARTED_TRACKING_PROGRAM, @@ -174,8 +181,10 @@ constructor( velocityTracker.resetTracking() } + private fun ClosedFloatingPointRange<Float>.length(): Float = endInclusive - start + private fun Float.normalize(): Float = - (this / (sliderRange.endInclusive - sliderRange.start)).coerceIn(0f, 1f) + ((this - sliderRange.start) / sliderRange.length()).coerceIn(0f, 1f) private fun Float.toOffset(): Offset = when (orientation) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 639b46a64fd1..bd4c4639e57a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -2698,7 +2698,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, /* velocityAxis= */ MotionEvent.AXIS_Y, /* upperBookendScale= */ 1f, /* lowerBookendScale= */ 0.05f, - /* exponent= */ 1f / 0.89f); + /* exponent= */ 1f / 0.89f, + /* sliderStepSize = */ 0f); private static final SeekableSliderTrackerConfig sSliderTrackerConfig = new SeekableSliderTrackerConfig( /* waitTimeMillis= */100, diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index 1f44451f0cb4..68f789600dd0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -28,6 +28,7 @@ import com.android.settingslib.volume.shared.model.AudioStream import com.android.settingslib.volume.shared.model.AudioStreamModel import com.android.settingslib.volume.shared.model.RingerMode import com.android.systemui.common.shared.model.Icon +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.modes.shared.ModesUiIcons import com.android.systemui.res.R import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor @@ -61,6 +62,7 @@ constructor( private val zenModeInteractor: ZenModeInteractor, private val uiEventLogger: UiEventLogger, private val volumePanelLogger: VolumePanelLogger, + override val hapticsViewModelFactory: SliderHapticsViewModel.Factory, ) : SliderViewModel { private val volumeChanges = MutableStateFlow<Int?>(null) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt index bb849cb1333e..8efe915abda2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.content.Context import android.media.session.MediaController.PlaybackInfo import com.android.systemui.common.shared.model.Icon +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.res.R import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession @@ -40,6 +41,7 @@ constructor( @Assisted private val coroutineScope: CoroutineScope, private val context: Context, private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor, + override val hapticsViewModelFactory: SliderHapticsViewModel.Factory, ) : SliderViewModel { override val slider: StateFlow<SliderState> = diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt index 7ded8c5c9fc1..9c1783b99f78 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import kotlinx.coroutines.flow.StateFlow /** Controls the behaviour of a volume slider. */ @@ -23,6 +24,8 @@ interface SliderViewModel { val slider: StateFlow<SliderState> + val hapticsViewModelFactory: SliderHapticsViewModel.Factory + fun onValueChanged(state: SliderState, newValue: Float) fun onValueChangeFinished() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt index 55f0a28d0135..a78670d7f1cc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.content.applicationContext import com.android.internal.logging.uiEventLogger +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.volume.domain.interactor.audioVolumeInteractor @@ -40,6 +41,7 @@ val Kosmos.audioStreamSliderViewModelFactory by zenModeInteractor, uiEventLogger, volumePanelLogger, + sliderHapticsViewModelFactory, ) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt index f0cb2cd904ca..abd4235143f1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.content.applicationContext +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.Kosmos import com.android.systemui.volume.mediaDeviceSessionInteractor import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession @@ -27,13 +28,14 @@ val Kosmos.castVolumeSliderViewModelFactory by object : CastVolumeSliderViewModel.Factory { override fun create( session: MediaDeviceSession, - coroutineScope: CoroutineScope + coroutineScope: CoroutineScope, ): CastVolumeSliderViewModel { return CastVolumeSliderViewModel( session, coroutineScope, applicationContext, mediaDeviceSessionInteractor, + sliderHapticsViewModelFactory, ) } } |