Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +35 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,11 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.PointF import android.media.AudioAttributes import android.os.VibrationEffect import android.os.Vibrator import android.util.AttributeSet import android.util.MathUtils import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils Loading @@ -31,15 +35,26 @@ import com.android.systemui.statusbar.charging.RippleShader private const val RIPPLE_ANIMATION_DURATION: Long = 1533 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 * launcher. */ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { private val vibrator: Vibrator? = context?.getSystemService(Vibrator::class.java) private var rippleInProgress: Boolean = false private val rippleShader = RippleShader() private val ripplePaint = Paint() private val rippleVibrationEffect = createVibrationEffect(vibrator) private val rippleVibrationAttrs = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build() init { rippleShader.color = 0xffffffff.toInt() // default color Loading Loading @@ -95,6 +110,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at visibility = GONE } }) vibrate() animatorSet.start() visibility = VISIBLE rippleInProgress = true Loading @@ -108,4 +124,23 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at // draw over the entire screen canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), ripplePaint) } private fun vibrate() { if (rippleVibrationEffect != null) { 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() } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +35 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,11 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.PointF import android.media.AudioAttributes import android.os.VibrationEffect import android.os.Vibrator import android.util.AttributeSet import android.util.MathUtils import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils Loading @@ -31,15 +35,26 @@ import com.android.systemui.statusbar.charging.RippleShader private const val RIPPLE_ANIMATION_DURATION: Long = 1533 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 * launcher. */ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { private val vibrator: Vibrator? = context?.getSystemService(Vibrator::class.java) private var rippleInProgress: Boolean = false private val rippleShader = RippleShader() private val ripplePaint = Paint() private val rippleVibrationEffect = createVibrationEffect(vibrator) private val rippleVibrationAttrs = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build() init { rippleShader.color = 0xffffffff.toInt() // default color Loading Loading @@ -95,6 +110,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at visibility = GONE } }) vibrate() animatorSet.start() visibility = VISIBLE rippleInProgress = true Loading @@ -108,4 +124,23 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at // draw over the entire screen canvas?.drawRect(0f, 0f, width.toFloat(), height.toFloat(), ripplePaint) } private fun vibrate() { if (rippleVibrationEffect != null) { 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() } }