Loading packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt +2 −0 Original line number Original line Diff line number Diff line Loading @@ -42,4 +42,6 @@ data class SliderHapticFeedbackConfig( @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f, @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f, /** Vibration scale at the lower bookend of the slider */ /** Vibration scale at the lower bookend of the slider */ @FloatRange(from = 0.0, to = 1.0) val lowerBookendScale: Float = 0.05f, @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, ) ) packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt +45 −15 Original line number Original line Diff line number Diff line Loading @@ -21,9 +21,11 @@ import android.os.VibrationEffect import android.view.VelocityTracker import android.view.VelocityTracker import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator import androidx.annotation.FloatRange import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.VibratorHelper import kotlin.math.abs import kotlin.math.abs import kotlin.math.min import kotlin.math.min import kotlin.math.pow /** /** * Listener of slider events that triggers haptic feedback. * Listener of slider events that triggers haptic feedback. Loading Loading @@ -63,18 +65,29 @@ class SliderHapticFeedbackProvider( * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. */ */ private fun vibrateOnEdgeCollision(absoluteVelocity: Float) { private fun vibrateOnEdgeCollision(absoluteVelocity: Float) { val powerScale = scaleOnEdgeCollision(absoluteVelocity) val vibration = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, powerScale) .compose() vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) } /** * Get the velocity-based scale at the bookends * * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. * @return The power scale for the vibration. */ @VisibleForTesting fun scaleOnEdgeCollision(absoluteVelocity: Float): Float { val velocityInterpolated = val velocityInterpolated = velocityAccelerateInterpolator.getInterpolation( velocityAccelerateInterpolator.getInterpolation( min(absoluteVelocity / config.maxVelocityToScale, 1f) min(absoluteVelocity / config.maxVelocityToScale, 1f) ) ) val bookendScaleRange = config.upperBookendScale - config.lowerBookendScale val bookendScaleRange = config.upperBookendScale - config.lowerBookendScale val bookendsHitScale = bookendScaleRange * velocityInterpolated + config.lowerBookendScale val bookendsHitScale = bookendScaleRange * velocityInterpolated + config.lowerBookendScale return bookendsHitScale.pow(config.exponent) val vibration = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, bookendsHitScale) .compose() vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) } } /** /** Loading @@ -96,6 +109,31 @@ class SliderHapticFeedbackProvider( val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress) val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress) if (deltaProgress < config.deltaProgressForDragThreshold) return if (deltaProgress < config.deltaProgressForDragThreshold) return val powerScale = scaleOnDragTexture(absoluteVelocity, normalizedSliderProgress) // Trigger the vibration composition val composition = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, powerScale) } vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } /** * Get the scale of the drag texture vibration. * * @param[absoluteVelocity] Absolute velocity of the handle. * @param[normalizedSliderProgress] Progress of the slider handled normalized to the range from * 0F to 1F (inclusive). * @return the scale of the vibration. */ @VisibleForTesting fun scaleOnDragTexture( absoluteVelocity: Float, @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float ): Float { val velocityInterpolated = val velocityInterpolated = velocityAccelerateInterpolator.getInterpolation( velocityAccelerateInterpolator.getInterpolation( min(absoluteVelocity / config.maxVelocityToScale, 1f) min(absoluteVelocity / config.maxVelocityToScale, 1f) Loading @@ -113,15 +151,7 @@ class SliderHapticFeedbackProvider( // Total scale // Total scale val scale = positionBasedScale + velocityBasedScale val scale = positionBasedScale + velocityBasedScale return scale.pow(config.exponent) // Trigger the vibration composition val composition = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scale) } vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } } override fun onHandleAcquiredByTouch() {} override fun onHandleAcquiredByTouch() {} Loading packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +22 −26 Original line number Original line Diff line number Diff line Loading @@ -71,7 +71,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -86,7 +86,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale) ) ) .compose() .compose() Loading @@ -102,7 +102,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -117,7 +117,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -132,7 +132,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() { fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading Loading @@ -203,7 +207,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() { fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading @@ -212,7 +220,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -232,7 +240,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() { fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading @@ -241,7 +253,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading Loading @@ -289,28 +301,12 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) } } private fun scaleAtBookends(velocity: Float): Float { val range = config.upperBookendScale - config.lowerBookendScale val interpolatedVelocity = velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale) return interpolatedVelocity * range + config.lowerBookendScale } private fun scaleAtProgressChange(velocity: Float, progress: Float): Float { val range = config.progressBasedDragMaxScale - config.progressBasedDragMinScale val interpolatedVelocity = velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale) val interpolatedProgress = progressInterpolator.getInterpolation(progress) val bump = interpolatedVelocity * config.additionalVelocityMaxBump return interpolatedProgress * range + config.progressBasedDragMinScale + bump } private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect { private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect { val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive( ticks.addPrimitive( VibrationEffect.Composition.PRIMITIVE_LOW_TICK, VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scaleAtProgressChange(velocity, progress) sliderHapticFeedbackProvider.scaleOnDragTexture(velocity, progress), ) ) } } return ticks.compose() return ticks.compose() Loading Loading
packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt +2 −0 Original line number Original line Diff line number Diff line Loading @@ -42,4 +42,6 @@ data class SliderHapticFeedbackConfig( @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f, @FloatRange(from = 0.0, to = 1.0) val upperBookendScale: Float = 1f, /** Vibration scale at the lower bookend of the slider */ /** Vibration scale at the lower bookend of the slider */ @FloatRange(from = 0.0, to = 1.0) val lowerBookendScale: Float = 0.05f, @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, ) )
packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt +45 −15 Original line number Original line Diff line number Diff line Loading @@ -21,9 +21,11 @@ import android.os.VibrationEffect import android.view.VelocityTracker import android.view.VelocityTracker import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator import androidx.annotation.FloatRange import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.VibratorHelper import kotlin.math.abs import kotlin.math.abs import kotlin.math.min import kotlin.math.min import kotlin.math.pow /** /** * Listener of slider events that triggers haptic feedback. * Listener of slider events that triggers haptic feedback. Loading Loading @@ -63,18 +65,29 @@ class SliderHapticFeedbackProvider( * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. */ */ private fun vibrateOnEdgeCollision(absoluteVelocity: Float) { private fun vibrateOnEdgeCollision(absoluteVelocity: Float) { val powerScale = scaleOnEdgeCollision(absoluteVelocity) val vibration = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, powerScale) .compose() vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) } /** * Get the velocity-based scale at the bookends * * @param[absoluteVelocity] Velocity of the handle when it reached the bookend. * @return The power scale for the vibration. */ @VisibleForTesting fun scaleOnEdgeCollision(absoluteVelocity: Float): Float { val velocityInterpolated = val velocityInterpolated = velocityAccelerateInterpolator.getInterpolation( velocityAccelerateInterpolator.getInterpolation( min(absoluteVelocity / config.maxVelocityToScale, 1f) min(absoluteVelocity / config.maxVelocityToScale, 1f) ) ) val bookendScaleRange = config.upperBookendScale - config.lowerBookendScale val bookendScaleRange = config.upperBookendScale - config.lowerBookendScale val bookendsHitScale = bookendScaleRange * velocityInterpolated + config.lowerBookendScale val bookendsHitScale = bookendScaleRange * velocityInterpolated + config.lowerBookendScale return bookendsHitScale.pow(config.exponent) val vibration = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, bookendsHitScale) .compose() vibratorHelper.vibrate(vibration, VIBRATION_ATTRIBUTES_PIPELINING) } } /** /** Loading @@ -96,6 +109,31 @@ class SliderHapticFeedbackProvider( val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress) val deltaProgress = abs(normalizedSliderProgress - dragTextureLastProgress) if (deltaProgress < config.deltaProgressForDragThreshold) return if (deltaProgress < config.deltaProgressForDragThreshold) return val powerScale = scaleOnDragTexture(absoluteVelocity, normalizedSliderProgress) // Trigger the vibration composition val composition = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, powerScale) } vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } /** * Get the scale of the drag texture vibration. * * @param[absoluteVelocity] Absolute velocity of the handle. * @param[normalizedSliderProgress] Progress of the slider handled normalized to the range from * 0F to 1F (inclusive). * @return the scale of the vibration. */ @VisibleForTesting fun scaleOnDragTexture( absoluteVelocity: Float, @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float ): Float { val velocityInterpolated = val velocityInterpolated = velocityAccelerateInterpolator.getInterpolation( velocityAccelerateInterpolator.getInterpolation( min(absoluteVelocity / config.maxVelocityToScale, 1f) min(absoluteVelocity / config.maxVelocityToScale, 1f) Loading @@ -113,15 +151,7 @@ class SliderHapticFeedbackProvider( // Total scale // Total scale val scale = positionBasedScale + velocityBasedScale val scale = positionBasedScale + velocityBasedScale return scale.pow(config.exponent) // Trigger the vibration composition val composition = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scale) } vibratorHelper.vibrate(composition.compose(), VIBRATION_ATTRIBUTES_PIPELINING) dragTextureLastTime = currentTime dragTextureLastProgress = normalizedSliderProgress } } override fun onHandleAcquiredByTouch() {} override fun onHandleAcquiredByTouch() {} Loading
packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +22 −26 Original line number Original line Diff line number Diff line Loading @@ -71,7 +71,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -86,7 +86,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale) ) ) .compose() .compose() Loading @@ -102,7 +102,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -117,7 +117,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -132,7 +132,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() { fun playHapticAtProgress_onQuickSuccession_playsLowTicksOnce() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading Loading @@ -203,7 +207,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() { fun playHapticAtLowerBookend_afterPlayingAtProgress_playsTwice() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading @@ -212,7 +220,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading @@ -232,7 +240,11 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() { fun playHapticAtUpperBookend_afterPlayingAtProgress_playsTwice() { // GIVEN max velocity and slider progress // GIVEN max velocity and slider progress val progress = 1f val progress = 1f val expectedScale = scaleAtProgressChange(config.maxVelocityToScale.toFloat(), progress) val expectedScale = sliderHapticFeedbackProvider.scaleOnDragTexture( config.maxVelocityToScale, progress, ) val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) Loading @@ -241,7 +253,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() VibrationEffect.startComposition() .addPrimitive( .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, VibrationEffect.Composition.PRIMITIVE_CLICK, scaleAtBookends(config.maxVelocityToScale) sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) ) .compose() .compose() Loading Loading @@ -289,28 +301,12 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) assertEquals(-1f, sliderHapticFeedbackProvider.dragTextureLastProgress) } } private fun scaleAtBookends(velocity: Float): Float { val range = config.upperBookendScale - config.lowerBookendScale val interpolatedVelocity = velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale) return interpolatedVelocity * range + config.lowerBookendScale } private fun scaleAtProgressChange(velocity: Float, progress: Float): Float { val range = config.progressBasedDragMaxScale - config.progressBasedDragMinScale val interpolatedVelocity = velocityInterpolator.getInterpolation(velocity / config.maxVelocityToScale) val interpolatedProgress = progressInterpolator.getInterpolation(progress) val bump = interpolatedVelocity * config.additionalVelocityMaxBump return interpolatedProgress * range + config.progressBasedDragMinScale + bump } private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect { private fun generateTicksComposition(velocity: Float, progress: Float): VibrationEffect { val ticks = VibrationEffect.startComposition() val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { repeat(config.numberOfLowTicks) { ticks.addPrimitive( ticks.addPrimitive( VibrationEffect.Composition.PRIMITIVE_LOW_TICK, VibrationEffect.Composition.PRIMITIVE_LOW_TICK, scaleAtProgressChange(velocity, progress) sliderHapticFeedbackProvider.scaleOnDragTexture(velocity, progress), ) ) } } return ticks.compose() return ticks.compose() Loading