Update dwell ripple parameters
Targeted effect: * Immediate visual response on touch down at sufficiently visible radius for variable finger sizes. * Then decelerate radius expansion to convey tension / elicit force and constrain at limited max radius past first stage. * Bump values on non-AOD surface to improve visibility with more variable backgrounds. Test: `adb shell cmd statusbar auth-ripple dwell` expands ripple (but doesn't animate the retract) Test: manual, atest SystemUITests Bug: 198864174 Change-Id: I1fb2e456e89a824b1b749150e3d82c6bc3223f47
This commit is contained in:
@@ -40,6 +40,7 @@ import com.android.systemui.util.ViewController
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
|
||||
/***
|
||||
* Controls the ripple effect that shows when authentication is successful.
|
||||
@@ -57,6 +58,7 @@ class AuthRippleController @Inject constructor(
|
||||
private val bypassController: KeyguardBypassController,
|
||||
private val biometricUnlockController: BiometricUnlockController,
|
||||
private val udfpsControllerProvider: Provider<UdfpsController>,
|
||||
private val statusBarStateController: StatusBarStateController,
|
||||
rippleView: AuthRippleView?
|
||||
) : ViewController<AuthRippleView>(rippleView) {
|
||||
var fingerprintSensorLocation: PointF? = null
|
||||
@@ -64,8 +66,11 @@ class AuthRippleController @Inject constructor(
|
||||
private var circleReveal: LightRevealEffect? = null
|
||||
|
||||
private var udfpsController: UdfpsController? = null
|
||||
|
||||
private var dwellScale = 2f
|
||||
private var expandedDwellScale = 2.5f
|
||||
private var aodDwellScale = 1.9f
|
||||
private var aodExpandedDwellScale = 2.3f
|
||||
private var udfpsRadius: Float = -1f
|
||||
|
||||
override fun onInit() {
|
||||
@@ -163,6 +168,22 @@ class AuthRippleController @Inject constructor(
|
||||
Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
|
||||
}
|
||||
|
||||
private fun showDwellRipple() {
|
||||
if (statusBarStateController.isDozing) {
|
||||
mView.startDwellRipple(
|
||||
/* startRadius */ udfpsRadius,
|
||||
/* endRadius */ udfpsRadius * aodDwellScale,
|
||||
/* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
|
||||
/* isDozing */ true)
|
||||
} else {
|
||||
mView.startDwellRipple(
|
||||
/* startRadius */ udfpsRadius,
|
||||
/* endRadius */ udfpsRadius * dwellScale,
|
||||
/* expandedRadius */ udfpsRadius * expandedDwellScale,
|
||||
/* isDozing */ false)
|
||||
}
|
||||
}
|
||||
|
||||
private val keyguardUpdateMonitorCallback =
|
||||
object : KeyguardUpdateMonitorCallback() {
|
||||
override fun onBiometricAuthenticated(
|
||||
@@ -204,10 +225,7 @@ class AuthRippleController @Inject constructor(
|
||||
}
|
||||
|
||||
mView.setSensorLocation(fingerprintSensorLocation!!)
|
||||
mView.startDwellRipple(
|
||||
/* startRadius */ udfpsRadius,
|
||||
/* endRadius */ udfpsRadius * dwellScale,
|
||||
/* expandedRadius */ udfpsRadius * expandedDwellScale)
|
||||
showDwellRipple()
|
||||
}
|
||||
|
||||
override fun onFingerUp() {
|
||||
@@ -234,14 +252,18 @@ class AuthRippleController @Inject constructor(
|
||||
}
|
||||
|
||||
inner class AuthRippleCommand : Command {
|
||||
fun printDwellInfo(pw: PrintWriter) {
|
||||
pw.println("dwell ripple: " +
|
||||
fun printLockScreenDwellInfo(pw: PrintWriter) {
|
||||
pw.println("lock screen dwell ripple: " +
|
||||
"\n\tsensorLocation=$fingerprintSensorLocation" +
|
||||
"\n\tdwellScale=$dwellScale" +
|
||||
"\n\tdwellAlpha=${mView.dwellAlpha}, " +
|
||||
"duration=${mView.dwellAlphaDuration}" +
|
||||
"\n\tdwellExpand=$expandedDwellScale" +
|
||||
"\n\t(crash systemui to reset to default)")
|
||||
"\n\tdwellExpand=$expandedDwellScale")
|
||||
}
|
||||
|
||||
fun printAodDwellInfo(pw: PrintWriter) {
|
||||
pw.println("aod dwell ripple: " +
|
||||
"\n\tsensorLocation=$fingerprintSensorLocation" +
|
||||
"\n\tdwellScale=$aodDwellScale" +
|
||||
"\n\tdwellExpand=$aodExpandedDwellScale")
|
||||
}
|
||||
|
||||
override fun execute(pw: PrintWriter, args: List<String>) {
|
||||
@@ -249,40 +271,12 @@ class AuthRippleController @Inject constructor(
|
||||
invalidCommand(pw)
|
||||
} else {
|
||||
when (args[0]) {
|
||||
"dwellScale" -> {
|
||||
if (args.size > 1 && args[1].toFloatOrNull() != null) {
|
||||
dwellScale = args[1].toFloat()
|
||||
printDwellInfo(pw)
|
||||
"dwell" -> {
|
||||
showDwellRipple()
|
||||
if (statusBarStateController.isDozing) {
|
||||
printAodDwellInfo(pw)
|
||||
} else {
|
||||
pw.println("expected float argument <dwellScale>")
|
||||
}
|
||||
}
|
||||
"dwellAlpha" -> {
|
||||
if (args.size > 2 && args[1].toFloatOrNull() != null &&
|
||||
args[2].toLongOrNull() != null) {
|
||||
mView.dwellAlpha = args[1].toFloat()
|
||||
if (args[2].toFloat() > 200L) {
|
||||
pw.println("alpha animation duration must be less than 200ms.")
|
||||
}
|
||||
mView.dwellAlphaDuration = kotlin.math.min(args[2].toLong(), 200L)
|
||||
printDwellInfo(pw)
|
||||
} else {
|
||||
pw.println("expected two float arguments:" +
|
||||
" <dwellAlpha> <dwellAlphaDuration>")
|
||||
}
|
||||
}
|
||||
"dwellExpand" -> {
|
||||
if (args.size > 1 && args[1].toFloatOrNull() != null) {
|
||||
val expandedScale = args[1].toFloat()
|
||||
if (expandedScale <= dwellScale) {
|
||||
pw.println("invalid expandedScale. must be greater than " +
|
||||
"dwellScale=$dwellScale, but given $expandedScale")
|
||||
} else {
|
||||
expandedDwellScale = expandedScale
|
||||
}
|
||||
printDwellInfo(pw)
|
||||
} else {
|
||||
pw.println("expected float argument <expandedScale>")
|
||||
printLockScreenDwellInfo(pw)
|
||||
}
|
||||
}
|
||||
"fingerprint" -> {
|
||||
@@ -313,9 +307,7 @@ class AuthRippleController @Inject constructor(
|
||||
override fun help(pw: PrintWriter) {
|
||||
pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
|
||||
pw.println("Available commands:")
|
||||
pw.println(" dwellScale <200ms_scale: float>")
|
||||
pw.println(" dwellAlpha <alpha: float> <duration : long>")
|
||||
pw.println(" dwellExpand <expanded_scale: float>")
|
||||
pw.println(" dwell")
|
||||
pw.println(" fingerprint")
|
||||
pw.println(" face")
|
||||
pw.println(" custom <x-location: int> <y-location: int>")
|
||||
|
||||
@@ -45,12 +45,18 @@ private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
|
||||
*/
|
||||
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
|
||||
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
|
||||
private val dwellPulseDuration = 200L
|
||||
var dwellAlphaDuration = dwellPulseDuration
|
||||
private val dwellExpandDuration = 1200L - dwellPulseDuration
|
||||
private val retractDuration = 400L
|
||||
|
||||
var dwellAlpha: Float = .5f
|
||||
private val dwellPulseDuration = 50L
|
||||
private val dwellAlphaDuration = dwellPulseDuration
|
||||
private val dwellAlpha: Float = 1f
|
||||
private val dwellExpandDuration = 1200L - dwellPulseDuration
|
||||
|
||||
private val aodDwellPulseDuration = 50L
|
||||
private var aodDwellAlphaDuration = aodDwellPulseDuration
|
||||
private var aodDwellAlpha: Float = .8f
|
||||
private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
|
||||
|
||||
private val retractDuration = 400L
|
||||
private var alphaInDuration: Long = 0
|
||||
private var unlockedRippleInProgress: Boolean = false
|
||||
private val rippleShader = RippleShader()
|
||||
@@ -142,7 +148,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
||||
* Ripple that moves animates from an outer ripple ring of
|
||||
* startRadius => endRadius => expandedRadius
|
||||
*/
|
||||
fun startDwellRipple(startRadius: Float, endRadius: Float, expandedRadius: Float) {
|
||||
fun startDwellRipple(
|
||||
startRadius: Float,
|
||||
endRadius: Float,
|
||||
expandedRadius: Float,
|
||||
isDozing: Boolean
|
||||
) {
|
||||
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
|
||||
return
|
||||
}
|
||||
@@ -153,12 +164,13 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
||||
val endInitialDwellProgress = endRadius / radius / 4f
|
||||
val endExpandDwellProgress = expandedRadius / radius / 4f
|
||||
|
||||
val pulseOutEndAlpha = (255 * dwellAlpha).toInt()
|
||||
val expandDwellEndAlpha = kotlin.math.min((255 * (dwellAlpha + .25f)).toInt(), 255)
|
||||
val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
|
||||
val pulseOutEndAlpha = (255 * alpha).toInt()
|
||||
val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
|
||||
val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
|
||||
endInitialDwellProgress).apply {
|
||||
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||
duration = dwellPulseDuration
|
||||
duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
|
||||
addUpdateListener { animator ->
|
||||
val now = animator.currentPlayTime
|
||||
rippleShader.progress = animator.animatedValue as Float
|
||||
@@ -170,7 +182,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
||||
|
||||
val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
|
||||
interpolator = Interpolators.LINEAR
|
||||
duration = dwellAlphaDuration
|
||||
duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
|
||||
addUpdateListener { animator ->
|
||||
rippleShader.color = ColorUtils.setAlphaComponent(
|
||||
rippleShader.color,
|
||||
@@ -184,7 +196,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
||||
val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
|
||||
endExpandDwellProgress).apply {
|
||||
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||
duration = dwellExpandDuration
|
||||
duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
|
||||
addUpdateListener { animator ->
|
||||
val now = animator.currentPlayTime
|
||||
rippleShader.progress = animator.animatedValue as Float
|
||||
@@ -197,7 +209,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
||||
val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
|
||||
.apply {
|
||||
interpolator = Interpolators.LINEAR
|
||||
duration = dwellExpandDuration
|
||||
duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
|
||||
addUpdateListener { animator ->
|
||||
rippleShader.color = ColorUtils.setAlphaComponent(
|
||||
rippleShader.color,
|
||||
|
||||
@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
||||
@@ -59,6 +60,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
||||
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
|
||||
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
|
||||
@Mock private lateinit var udfpsController: UdfpsController
|
||||
@Mock private lateinit var statusBarStateController: StatusBarStateController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -76,6 +78,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
||||
bypassController,
|
||||
biometricUnlockController,
|
||||
udfpsControllerProvider,
|
||||
statusBarStateController,
|
||||
rippleView
|
||||
)
|
||||
controller.init()
|
||||
|
||||
Reference in New Issue
Block a user