Merge "Draw auth ripple in a circle to reduce overdraw" into sc-dev
This commit is contained in:
@@ -23,11 +23,7 @@ import android.content.Context
|
|||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.media.AudioAttributes
|
|
||||||
import android.os.VibrationEffect
|
|
||||||
import android.os.Vibrator
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.MathUtils
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.PathInterpolator
|
import android.view.animation.PathInterpolator
|
||||||
import com.android.internal.graphics.ColorUtils
|
import com.android.internal.graphics.ColorUtils
|
||||||
@@ -36,26 +32,25 @@ import com.android.systemui.statusbar.charging.RippleShader
|
|||||||
|
|
||||||
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
|
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
|
||||||
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
|
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
|
||||||
private const val RIPPLE_VIBRATION_PRIMITIVE: Int = VibrationEffect.Composition.PRIMITIVE_LOW_TICK
|
|
||||||
private const val RIPPLE_VIBRATION_SIZE: Int = 60
|
|
||||||
private const val RIPPLE_VIBRATION_SCALE_START: Float = 0.6f
|
|
||||||
private const val RIPPLE_VIBRATION_SCALE_DECAY: Float = -0.1f
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expanding ripple effect on the transition from biometric authentication success to showing
|
* Expanding ripple effect on the transition from biometric authentication success to showing
|
||||||
* launcher.
|
* launcher.
|
||||||
*/
|
*/
|
||||||
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
|
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
|
||||||
private val vibrator: Vibrator? = context?.getSystemService(Vibrator::class.java)
|
|
||||||
private var rippleInProgress: Boolean = false
|
private var rippleInProgress: Boolean = false
|
||||||
private val rippleShader = RippleShader()
|
private val rippleShader = RippleShader()
|
||||||
private val ripplePaint = Paint()
|
private val ripplePaint = Paint()
|
||||||
private val rippleVibrationEffect = createVibrationEffect(vibrator)
|
private var radius: Float = 0.0f
|
||||||
private val rippleVibrationAttrs =
|
set(value) {
|
||||||
AudioAttributes.Builder()
|
rippleShader.radius = value
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
field = value
|
||||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
}
|
||||||
.build()
|
private var origin: PointF = PointF()
|
||||||
|
set(value) {
|
||||||
|
rippleShader.origin = value
|
||||||
|
field = value
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
rippleShader.color = 0xffffffff.toInt() // default color
|
rippleShader.color = 0xffffffff.toInt() // default color
|
||||||
@@ -66,8 +61,8 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSensorLocation(location: PointF) {
|
fun setSensorLocation(location: PointF) {
|
||||||
rippleShader.origin = location
|
origin = location
|
||||||
rippleShader.radius = maxOf(location.x, location.y, width - location.x, height - location.y)
|
radius = maxOf(location.x, location.y, width - location.x, height - location.y)
|
||||||
.toFloat()
|
.toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +136,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// TODO (b/185124905): custom haptic TBD
|
|
||||||
// vibrate()
|
|
||||||
animatorSet.start()
|
animatorSet.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,26 +144,11 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas?) {
|
override fun onDraw(canvas: Canvas?) {
|
||||||
// draw over the entire screen
|
// To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
|
||||||
canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), ripplePaint)
|
// the active effect area. Values here should be kept in sync with the
|
||||||
}
|
// animation implementation in the ripple shader.
|
||||||
|
val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
|
||||||
private fun vibrate() {
|
(1 - rippleShader.progress)) * radius * 1.5f
|
||||||
if (rippleVibrationEffect != null) {
|
canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
|
||||||
vibrator?.vibrate(rippleVibrationEffect, rippleVibrationAttrs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createVibrationEffect(vibrator: Vibrator?): VibrationEffect? {
|
|
||||||
if (vibrator?.areAllPrimitivesSupported(RIPPLE_VIBRATION_PRIMITIVE) == false) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val composition = VibrationEffect.startComposition()
|
|
||||||
for (i in 0 until RIPPLE_VIBRATION_SIZE) {
|
|
||||||
val scale =
|
|
||||||
RIPPLE_VIBRATION_SCALE_START * MathUtils.exp(RIPPLE_VIBRATION_SCALE_DECAY * i)
|
|
||||||
composition.addPrimitive(RIPPLE_VIBRATION_PRIMITIVE, scale, 0 /* delay */)
|
|
||||||
}
|
|
||||||
return composition.compose()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user