summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt144
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt3
4 files changed, 129 insertions, 24 deletions
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ce3084c6046c..2eb58b9c98ec 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2870,7 +2870,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>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index ae8edfece4cb..b19795c38579 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -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,22 +123,48 @@ 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(
view = endButton,
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 =
if (isVisible) {
@@ -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()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 31543dfcb540..3e9d89a18c37 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1374,8 +1374,8 @@ public final class NotificationPanelViewController implements Dumpable {
mFalsingManager,
mLockIconViewController,
stringResourceId ->
- mKeyguardIndicationController.showTransientIndication(stringResourceId)
- );
+ mKeyguardIndicationController.showTransientIndication(stringResourceId),
+ mVibratorHelper);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
index 2ce116394236..e4227dce94e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.kt
@@ -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)
}