Merge "Quick affordance gesture polish and haptics." into tm-qpr-dev am: 1746734f5d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20808866

Change-Id: Ica48ca6117919ecf9e392874428df6c57c3a452e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ale Nijamkin
2022-12-22 21:18:55 +00:00
committed by Automerger Merge Worker
4 changed files with 129 additions and 24 deletions

View File

@@ -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 &amp; 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>

View File

@@ -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
@@ -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
@@ -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)
@@ -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(
@@ -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 =
@@ -239,6 +270,7 @@ object KeyguardBottomAreaViewBinder {
viewModel: KeyguardQuickAffordanceViewModel,
falsingManager: FalsingManager?,
messageDisplayer: (Int) -> Unit,
vibratorHelper: VibratorHelper?,
) {
if (!viewModel.isVisible) {
view.isVisible = false
@@ -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)))
}
@@ -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()
@@ -376,25 +411,38 @@ object KeyguardBottomAreaViewBinder {
true
}
MotionEvent.ACTION_UP -> {
if (System.currentTimeMillis() - downTimestamp < longPressDurationMs) {
messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
val shakeAnimator =
ObjectAnimator.ofFloat(
view,
"translationX",
0f,
view.context.resources
.getDimensionPixelSize(
R.dimen.keyguard_affordance_shake_amplitude
cancel(
onAnimationEnd =
if (System.currentTimeMillis() - downTimestamp < longPressDurationMs) {
Runnable {
messageDisplayer.invoke(
R.string.keyguard_affordance_press_too_short
)
.toFloat(),
0f,
)
shakeAnimator.duration = 300
shakeAnimator.interpolator = CycleInterpolator(5f)
shakeAnimator.start()
}
cancel()
val amplitude =
view.context.resources
.getDimensionPixelSize(
R.dimen.keyguard_affordance_shake_amplitude
)
.toFloat()
val shakeAnimator =
ObjectAnimator.ofFloat(
view,
"translationX",
-amplitude / 2,
amplitude / 2,
)
shakeAnimator.duration =
ShakeAnimationDuration.inWholeMilliseconds
shakeAnimator.interpolator =
CycleInterpolator(ShakeAnimationCycles)
shakeAnimator.start()
vibratorHelper?.vibrate(Vibrations.Shake)
}
} else {
null
}
)
true
}
MotionEvent.ACTION_CANCEL -> {
@@ -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 {
@@ -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()
}
}

View File

@@ -1400,8 +1400,8 @@ public final class NotificationPanelViewController implements Dumpable {
mFalsingManager,
mLockIconViewController,
stringResourceId ->
mKeyguardIndicationController.showTransientIndication(stringResourceId)
);
mKeyguardIndicationController.showTransientIndication(stringResourceId),
mVibratorHelper);
}
@VisibleForTesting

View File

@@ -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
@@ -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)
}