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 java.io.PrintWriter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Provider
|
import javax.inject.Provider
|
||||||
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Controls the ripple effect that shows when authentication is successful.
|
* Controls the ripple effect that shows when authentication is successful.
|
||||||
@@ -57,6 +58,7 @@ class AuthRippleController @Inject constructor(
|
|||||||
private val bypassController: KeyguardBypassController,
|
private val bypassController: KeyguardBypassController,
|
||||||
private val biometricUnlockController: BiometricUnlockController,
|
private val biometricUnlockController: BiometricUnlockController,
|
||||||
private val udfpsControllerProvider: Provider<UdfpsController>,
|
private val udfpsControllerProvider: Provider<UdfpsController>,
|
||||||
|
private val statusBarStateController: StatusBarStateController,
|
||||||
rippleView: AuthRippleView?
|
rippleView: AuthRippleView?
|
||||||
) : ViewController<AuthRippleView>(rippleView) {
|
) : ViewController<AuthRippleView>(rippleView) {
|
||||||
var fingerprintSensorLocation: PointF? = null
|
var fingerprintSensorLocation: PointF? = null
|
||||||
@@ -64,8 +66,11 @@ class AuthRippleController @Inject constructor(
|
|||||||
private var circleReveal: LightRevealEffect? = null
|
private var circleReveal: LightRevealEffect? = null
|
||||||
|
|
||||||
private var udfpsController: UdfpsController? = null
|
private var udfpsController: UdfpsController? = null
|
||||||
|
|
||||||
private var dwellScale = 2f
|
private var dwellScale = 2f
|
||||||
private var expandedDwellScale = 2.5f
|
private var expandedDwellScale = 2.5f
|
||||||
|
private var aodDwellScale = 1.9f
|
||||||
|
private var aodExpandedDwellScale = 2.3f
|
||||||
private var udfpsRadius: Float = -1f
|
private var udfpsRadius: Float = -1f
|
||||||
|
|
||||||
override fun onInit() {
|
override fun onInit() {
|
||||||
@@ -163,6 +168,22 @@ class AuthRippleController @Inject constructor(
|
|||||||
Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
|
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 =
|
private val keyguardUpdateMonitorCallback =
|
||||||
object : KeyguardUpdateMonitorCallback() {
|
object : KeyguardUpdateMonitorCallback() {
|
||||||
override fun onBiometricAuthenticated(
|
override fun onBiometricAuthenticated(
|
||||||
@@ -204,10 +225,7 @@ class AuthRippleController @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
mView.setSensorLocation(fingerprintSensorLocation!!)
|
mView.setSensorLocation(fingerprintSensorLocation!!)
|
||||||
mView.startDwellRipple(
|
showDwellRipple()
|
||||||
/* startRadius */ udfpsRadius,
|
|
||||||
/* endRadius */ udfpsRadius * dwellScale,
|
|
||||||
/* expandedRadius */ udfpsRadius * expandedDwellScale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFingerUp() {
|
override fun onFingerUp() {
|
||||||
@@ -234,14 +252,18 @@ class AuthRippleController @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class AuthRippleCommand : Command {
|
inner class AuthRippleCommand : Command {
|
||||||
fun printDwellInfo(pw: PrintWriter) {
|
fun printLockScreenDwellInfo(pw: PrintWriter) {
|
||||||
pw.println("dwell ripple: " +
|
pw.println("lock screen dwell ripple: " +
|
||||||
"\n\tsensorLocation=$fingerprintSensorLocation" +
|
"\n\tsensorLocation=$fingerprintSensorLocation" +
|
||||||
"\n\tdwellScale=$dwellScale" +
|
"\n\tdwellScale=$dwellScale" +
|
||||||
"\n\tdwellAlpha=${mView.dwellAlpha}, " +
|
"\n\tdwellExpand=$expandedDwellScale")
|
||||||
"duration=${mView.dwellAlphaDuration}" +
|
}
|
||||||
"\n\tdwellExpand=$expandedDwellScale" +
|
|
||||||
"\n\t(crash systemui to reset to default)")
|
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>) {
|
override fun execute(pw: PrintWriter, args: List<String>) {
|
||||||
@@ -249,40 +271,12 @@ class AuthRippleController @Inject constructor(
|
|||||||
invalidCommand(pw)
|
invalidCommand(pw)
|
||||||
} else {
|
} else {
|
||||||
when (args[0]) {
|
when (args[0]) {
|
||||||
"dwellScale" -> {
|
"dwell" -> {
|
||||||
if (args.size > 1 && args[1].toFloatOrNull() != null) {
|
showDwellRipple()
|
||||||
dwellScale = args[1].toFloat()
|
if (statusBarStateController.isDozing) {
|
||||||
printDwellInfo(pw)
|
printAodDwellInfo(pw)
|
||||||
} else {
|
} else {
|
||||||
pw.println("expected float argument <dwellScale>")
|
printLockScreenDwellInfo(pw)
|
||||||
}
|
|
||||||
}
|
|
||||||
"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>")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"fingerprint" -> {
|
"fingerprint" -> {
|
||||||
@@ -313,9 +307,7 @@ class AuthRippleController @Inject constructor(
|
|||||||
override fun help(pw: PrintWriter) {
|
override fun help(pw: PrintWriter) {
|
||||||
pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
|
pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
|
||||||
pw.println("Available commands:")
|
pw.println("Available commands:")
|
||||||
pw.println(" dwellScale <200ms_scale: float>")
|
pw.println(" dwell")
|
||||||
pw.println(" dwellAlpha <alpha: float> <duration : long>")
|
|
||||||
pw.println(" dwellExpand <expanded_scale: float>")
|
|
||||||
pw.println(" fingerprint")
|
pw.println(" fingerprint")
|
||||||
pw.println(" face")
|
pw.println(" face")
|
||||||
pw.println(" custom <x-location: int> <y-location: int>")
|
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) {
|
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
|
||||||
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
|
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 alphaInDuration: Long = 0
|
||||||
private var unlockedRippleInProgress: Boolean = false
|
private var unlockedRippleInProgress: Boolean = false
|
||||||
private val rippleShader = RippleShader()
|
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
|
* Ripple that moves animates from an outer ripple ring of
|
||||||
* startRadius => endRadius => expandedRadius
|
* 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) {
|
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -153,12 +164,13 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
|||||||
val endInitialDwellProgress = endRadius / radius / 4f
|
val endInitialDwellProgress = endRadius / radius / 4f
|
||||||
val endExpandDwellProgress = expandedRadius / radius / 4f
|
val endExpandDwellProgress = expandedRadius / radius / 4f
|
||||||
|
|
||||||
val pulseOutEndAlpha = (255 * dwellAlpha).toInt()
|
val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
|
||||||
val expandDwellEndAlpha = kotlin.math.min((255 * (dwellAlpha + .25f)).toInt(), 255)
|
val pulseOutEndAlpha = (255 * alpha).toInt()
|
||||||
|
val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
|
||||||
val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
|
val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
|
||||||
endInitialDwellProgress).apply {
|
endInitialDwellProgress).apply {
|
||||||
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||||
duration = dwellPulseDuration
|
duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
|
||||||
addUpdateListener { animator ->
|
addUpdateListener { animator ->
|
||||||
val now = animator.currentPlayTime
|
val now = animator.currentPlayTime
|
||||||
rippleShader.progress = animator.animatedValue as Float
|
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 {
|
val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
|
||||||
interpolator = Interpolators.LINEAR
|
interpolator = Interpolators.LINEAR
|
||||||
duration = dwellAlphaDuration
|
duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
|
||||||
addUpdateListener { animator ->
|
addUpdateListener { animator ->
|
||||||
rippleShader.color = ColorUtils.setAlphaComponent(
|
rippleShader.color = ColorUtils.setAlphaComponent(
|
||||||
rippleShader.color,
|
rippleShader.color,
|
||||||
@@ -184,7 +196,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
|||||||
val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
|
val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
|
||||||
endExpandDwellProgress).apply {
|
endExpandDwellProgress).apply {
|
||||||
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||||
duration = dwellExpandDuration
|
duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
|
||||||
addUpdateListener { animator ->
|
addUpdateListener { animator ->
|
||||||
val now = animator.currentPlayTime
|
val now = animator.currentPlayTime
|
||||||
rippleShader.progress = animator.animatedValue as Float
|
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)
|
val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
|
||||||
.apply {
|
.apply {
|
||||||
interpolator = Interpolators.LINEAR
|
interpolator = Interpolators.LINEAR
|
||||||
duration = dwellExpandDuration
|
duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
|
||||||
addUpdateListener { animator ->
|
addUpdateListener { animator ->
|
||||||
rippleShader.color = ColorUtils.setAlphaComponent(
|
rippleShader.color = ColorUtils.setAlphaComponent(
|
||||||
rippleShader.color,
|
rippleShader.color,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
|
|||||||
import com.android.keyguard.KeyguardUpdateMonitor
|
import com.android.keyguard.KeyguardUpdateMonitor
|
||||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||||
import com.android.systemui.statusbar.NotificationShadeWindowController
|
import com.android.systemui.statusbar.NotificationShadeWindowController
|
||||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||||
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
import com.android.systemui.statusbar.phone.BiometricUnlockController
|
||||||
@@ -59,6 +60,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
|
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
|
||||||
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
|
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
|
||||||
@Mock private lateinit var udfpsController: UdfpsController
|
@Mock private lateinit var udfpsController: UdfpsController
|
||||||
|
@Mock private lateinit var statusBarStateController: StatusBarStateController
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -76,6 +78,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
|||||||
bypassController,
|
bypassController,
|
||||||
biometricUnlockController,
|
biometricUnlockController,
|
||||||
udfpsControllerProvider,
|
udfpsControllerProvider,
|
||||||
|
statusBarStateController,
|
||||||
rippleView
|
rippleView
|
||||||
)
|
)
|
||||||
controller.init()
|
controller.init()
|
||||||
|
|||||||
Reference in New Issue
Block a user