Loading packages/SystemUI/res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -2751,7 +2751,7 @@ Error message shown when a button should be pressed and held to activate it, usually shown when the user attempted to tap the button or held it for too short a time. [CHAR LIMIT=32]. --> <string name="keyguard_affordance_press_too_short">Press and hold to activate</string> <string name="keyguard_affordance_press_too_short">Touch & hold to open</string> <!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] --> <string name="rear_display_bottom_sheet_cancel">Cancel</string> Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +123 −21 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.graphics.drawable.Animatable2 import android.os.VibrationEffect import android.util.Size import android.util.TypedValue import android.view.MotionEvent Loading @@ -43,8 +44,11 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.kotlin.pairwise import kotlin.math.pow import kotlin.math.sqrt import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine Loading Loading @@ -93,6 +97,7 @@ object KeyguardBottomAreaViewBinder { view: ViewGroup, viewModel: KeyguardBottomAreaViewModel, falsingManager: FalsingManager?, vibratorHelper: VibratorHelper?, messageDisplayer: (Int) -> Unit, ): Binding { val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area) Loading @@ -118,10 +123,23 @@ object KeyguardBottomAreaViewBinder { viewModel = buttonModel, falsingManager = falsingManager, messageDisplayer = messageDisplayer, vibratorHelper = vibratorHelper, ) } } launch { viewModel.startButton .map { it.isActivated } .pairwise() .collect { (prev, next) -> when { !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated) prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated) } } } launch { viewModel.endButton.collect { buttonModel -> updateButton( Loading @@ -129,10 +147,23 @@ object KeyguardBottomAreaViewBinder { viewModel = buttonModel, falsingManager = falsingManager, messageDisplayer = messageDisplayer, vibratorHelper = vibratorHelper, ) } } launch { viewModel.endButton .map { it.isActivated } .pairwise() .collect { (prev, next) -> when { !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated) prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated) } } } launch { viewModel.isOverlayContainerVisible.collect { isVisible -> overlayContainer.visibility = Loading Loading @@ -239,6 +270,7 @@ object KeyguardBottomAreaViewBinder { viewModel: KeyguardQuickAffordanceViewModel, falsingManager: FalsingManager?, messageDisplayer: (Int) -> Unit, vibratorHelper: VibratorHelper?, ) { if (!viewModel.isVisible) { view.isVisible = false Loading Loading @@ -312,7 +344,9 @@ object KeyguardBottomAreaViewBinder { view.isClickable = viewModel.isClickable if (viewModel.isClickable) { if (viewModel.useLongPress) { view.setOnTouchListener(OnTouchListener(view, viewModel, messageDisplayer)) view.setOnTouchListener( OnTouchListener(view, viewModel, messageDisplayer, vibratorHelper) ) } else { view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager))) } Loading @@ -328,6 +362,7 @@ object KeyguardBottomAreaViewBinder { private val view: View, private val viewModel: KeyguardQuickAffordanceViewModel, private val messageDisplayer: (Int) -> Unit, private val vibratorHelper: VibratorHelper?, ) : View.OnTouchListener { private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong() Loading Loading @@ -376,25 +411,38 @@ object KeyguardBottomAreaViewBinder { true } MotionEvent.ACTION_UP -> { cancel( onAnimationEnd = if (System.currentTimeMillis() - downTimestamp < longPressDurationMs) { messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short) val shakeAnimator = ObjectAnimator.ofFloat( view, "translationX", 0f, Runnable { messageDisplayer.invoke( R.string.keyguard_affordance_press_too_short ) val amplitude = view.context.resources .getDimensionPixelSize( R.dimen.keyguard_affordance_shake_amplitude ) .toFloat(), 0f, .toFloat() val shakeAnimator = ObjectAnimator.ofFloat( view, "translationX", -amplitude / 2, amplitude / 2, ) shakeAnimator.duration = 300 shakeAnimator.interpolator = CycleInterpolator(5f) shakeAnimator.duration = ShakeAnimationDuration.inWholeMilliseconds shakeAnimator.interpolator = CycleInterpolator(ShakeAnimationCycles) shakeAnimator.start() vibratorHelper?.vibrate(Vibrations.Shake) } cancel() } else { null } ) true } MotionEvent.ACTION_CANCEL -> { Loading @@ -405,11 +453,11 @@ object KeyguardBottomAreaViewBinder { } } private fun cancel() { private fun cancel(onAnimationEnd: Runnable? = null) { downTimestamp = 0L longPressAnimator?.cancel() longPressAnimator = null view.animate().scaleX(1f).scaleY(1f) view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd) } companion object { Loading Loading @@ -461,4 +509,58 @@ object KeyguardBottomAreaViewBinder { val indicationTextSizePx: Int, val buttonSizePx: Size, ) private val ShakeAnimationDuration = 300.milliseconds private val ShakeAnimationCycles = 5f object Vibrations { private const val SmallVibrationScale = 0.3f private const val BigVibrationScale = 0.6f val Shake = VibrationEffect.startComposition() .apply { val vibrationDelayMs = (ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2)) .toInt() val vibrationCount = ShakeAnimationCycles.toInt() * 2 repeat(vibrationCount) { addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, SmallVibrationScale, vibrationDelayMs, ) } } .compose() val Activated = VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0, ) .addPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.1f, 0, ) .compose() val Deactivated = VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0, ) .addPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.1f, 0, ) .compose() } } packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +2 −2 Original line number Diff line number Diff line Loading @@ -1400,8 +1400,8 @@ public final class NotificationPanelViewController implements Dumpable { mFalsingManager, mLockIconViewController, stringResourceId -> mKeyguardIndicationController.showTransientIndication(stringResourceId) ); mKeyguardIndicationController.showTransientIndication(stringResourceId), mVibratorHelper); } @VisibleForTesting Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt +3 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper /** * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI Loading Loading @@ -65,12 +66,14 @@ constructor( falsingManager: FalsingManager? = null, lockIconViewController: LockIconViewController? = null, messageDisplayer: MessageDisplayer? = null, vibratorHelper: VibratorHelper? = null, ) { binding = bind( this, viewModel, falsingManager, vibratorHelper, ) { messageDisplayer?.display(it) } Loading Loading
packages/SystemUI/res/values/strings.xml +1 −1 Original line number Diff line number Diff line Loading @@ -2751,7 +2751,7 @@ Error message shown when a button should be pressed and held to activate it, usually shown when the user attempted to tap the button or held it for too short a time. [CHAR LIMIT=32]. --> <string name="keyguard_affordance_press_too_short">Press and hold to activate</string> <string name="keyguard_affordance_press_too_short">Touch & hold to open</string> <!-- Text for education page of cancel button to hide the page. [CHAR_LIMIT=NONE] --> <string name="rear_display_bottom_sheet_cancel">Cancel</string> Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +123 −21 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.binder import android.annotation.SuppressLint import android.graphics.drawable.Animatable2 import android.os.VibrationEffect import android.util.Size import android.util.TypedValue import android.view.MotionEvent Loading @@ -43,8 +44,11 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.kotlin.pairwise import kotlin.math.pow import kotlin.math.sqrt import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine Loading Loading @@ -93,6 +97,7 @@ object KeyguardBottomAreaViewBinder { view: ViewGroup, viewModel: KeyguardBottomAreaViewModel, falsingManager: FalsingManager?, vibratorHelper: VibratorHelper?, messageDisplayer: (Int) -> Unit, ): Binding { val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area) Loading @@ -118,10 +123,23 @@ object KeyguardBottomAreaViewBinder { viewModel = buttonModel, falsingManager = falsingManager, messageDisplayer = messageDisplayer, vibratorHelper = vibratorHelper, ) } } launch { viewModel.startButton .map { it.isActivated } .pairwise() .collect { (prev, next) -> when { !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated) prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated) } } } launch { viewModel.endButton.collect { buttonModel -> updateButton( Loading @@ -129,10 +147,23 @@ object KeyguardBottomAreaViewBinder { viewModel = buttonModel, falsingManager = falsingManager, messageDisplayer = messageDisplayer, vibratorHelper = vibratorHelper, ) } } launch { viewModel.endButton .map { it.isActivated } .pairwise() .collect { (prev, next) -> when { !prev && next -> vibratorHelper?.vibrate(Vibrations.Activated) prev && !next -> vibratorHelper?.vibrate(Vibrations.Deactivated) } } } launch { viewModel.isOverlayContainerVisible.collect { isVisible -> overlayContainer.visibility = Loading Loading @@ -239,6 +270,7 @@ object KeyguardBottomAreaViewBinder { viewModel: KeyguardQuickAffordanceViewModel, falsingManager: FalsingManager?, messageDisplayer: (Int) -> Unit, vibratorHelper: VibratorHelper?, ) { if (!viewModel.isVisible) { view.isVisible = false Loading Loading @@ -312,7 +344,9 @@ object KeyguardBottomAreaViewBinder { view.isClickable = viewModel.isClickable if (viewModel.isClickable) { if (viewModel.useLongPress) { view.setOnTouchListener(OnTouchListener(view, viewModel, messageDisplayer)) view.setOnTouchListener( OnTouchListener(view, viewModel, messageDisplayer, vibratorHelper) ) } else { view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager))) } Loading @@ -328,6 +362,7 @@ object KeyguardBottomAreaViewBinder { private val view: View, private val viewModel: KeyguardQuickAffordanceViewModel, private val messageDisplayer: (Int) -> Unit, private val vibratorHelper: VibratorHelper?, ) : View.OnTouchListener { private val longPressDurationMs = ViewConfiguration.getLongPressTimeout().toLong() Loading Loading @@ -376,25 +411,38 @@ object KeyguardBottomAreaViewBinder { true } MotionEvent.ACTION_UP -> { cancel( onAnimationEnd = if (System.currentTimeMillis() - downTimestamp < longPressDurationMs) { messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short) val shakeAnimator = ObjectAnimator.ofFloat( view, "translationX", 0f, Runnable { messageDisplayer.invoke( R.string.keyguard_affordance_press_too_short ) val amplitude = view.context.resources .getDimensionPixelSize( R.dimen.keyguard_affordance_shake_amplitude ) .toFloat(), 0f, .toFloat() val shakeAnimator = ObjectAnimator.ofFloat( view, "translationX", -amplitude / 2, amplitude / 2, ) shakeAnimator.duration = 300 shakeAnimator.interpolator = CycleInterpolator(5f) shakeAnimator.duration = ShakeAnimationDuration.inWholeMilliseconds shakeAnimator.interpolator = CycleInterpolator(ShakeAnimationCycles) shakeAnimator.start() vibratorHelper?.vibrate(Vibrations.Shake) } cancel() } else { null } ) true } MotionEvent.ACTION_CANCEL -> { Loading @@ -405,11 +453,11 @@ object KeyguardBottomAreaViewBinder { } } private fun cancel() { private fun cancel(onAnimationEnd: Runnable? = null) { downTimestamp = 0L longPressAnimator?.cancel() longPressAnimator = null view.animate().scaleX(1f).scaleY(1f) view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd) } companion object { Loading Loading @@ -461,4 +509,58 @@ object KeyguardBottomAreaViewBinder { val indicationTextSizePx: Int, val buttonSizePx: Size, ) private val ShakeAnimationDuration = 300.milliseconds private val ShakeAnimationCycles = 5f object Vibrations { private const val SmallVibrationScale = 0.3f private const val BigVibrationScale = 0.6f val Shake = VibrationEffect.startComposition() .apply { val vibrationDelayMs = (ShakeAnimationDuration.inWholeMilliseconds / (ShakeAnimationCycles * 2)) .toInt() val vibrationCount = ShakeAnimationCycles.toInt() * 2 repeat(vibrationCount) { addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, SmallVibrationScale, vibrationDelayMs, ) } } .compose() val Activated = VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0, ) .addPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.1f, 0, ) .compose() val Deactivated = VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_TICK, BigVibrationScale, 0, ) .addPrimitive( VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.1f, 0, ) .compose() } }
packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +2 −2 Original line number Diff line number Diff line Loading @@ -1400,8 +1400,8 @@ public final class NotificationPanelViewController implements Dumpable { mFalsingManager, mLockIconViewController, stringResourceId -> mKeyguardIndicationController.showTransientIndication(stringResourceId) ); mKeyguardIndicationController.showTransientIndication(stringResourceId), mVibratorHelper); } @VisibleForTesting Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt +3 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardBottomAreaViewBinder.bind import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.VibratorHelper /** * Renders the bottom area of the lock-screen. Concerned primarily with the quick affordance UI Loading Loading @@ -65,12 +66,14 @@ constructor( falsingManager: FalsingManager? = null, lockIconViewController: LockIconViewController? = null, messageDisplayer: MessageDisplayer? = null, vibratorHelper: VibratorHelper? = null, ) { binding = bind( this, viewModel, falsingManager, vibratorHelper, ) { messageDisplayer?.display(it) } Loading