1 /* <lambda>null2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.biometrics 17 18 import android.content.Context 19 import android.graphics.Canvas 20 import android.graphics.Color 21 import android.graphics.Paint 22 import android.graphics.PointF 23 import android.graphics.Rect 24 import android.graphics.RectF 25 import android.util.AttributeSet 26 import android.util.Log 27 import android.view.MotionEvent 28 import android.widget.FrameLayout 29 import com.android.systemui.R 30 import com.android.systemui.doze.DozeReceiver 31 32 private const val TAG = "UdfpsView" 33 34 /** 35 * The main view group containing all UDFPS animations. 36 */ 37 class UdfpsView( 38 context: Context, 39 attrs: AttributeSet? 40 ) : FrameLayout(context, attrs), DozeReceiver { 41 42 // Use expanded overlay when feature flag is true, set by UdfpsViewController 43 var useExpandedOverlay: Boolean = false 44 45 // sensorRect may be bigger than the sensor. True sensor dimensions are defined in 46 // overlayParams.sensorBounds 47 var sensorRect = Rect() 48 private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null 49 private val debugTextPaint = Paint().apply { 50 isAntiAlias = true 51 color = Color.BLUE 52 textSize = 32f 53 } 54 55 private val sensorTouchAreaCoefficient: Float = 56 context.theme.obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0, 0).use { a -> 57 require(a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { 58 "UdfpsView must contain sensorTouchAreaCoefficient" 59 } 60 a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f) 61 } 62 63 /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ 64 var animationViewController: UdfpsAnimationViewController<*>? = null 65 66 /** Parameters that affect the position and size of the overlay. */ 67 var overlayParams = UdfpsOverlayParams() 68 69 /** Debug message. */ 70 var debugMessage: String? = null 71 set(value) { 72 field = value 73 postInvalidate() 74 } 75 76 /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */ 77 var isDisplayConfigured: Boolean = false 78 private set 79 80 fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) { 81 mUdfpsDisplayMode = udfpsDisplayModeProvider 82 } 83 84 // Don't propagate any touch events to the child views. 85 override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { 86 return (animationViewController == null || !animationViewController!!.shouldPauseAuth()) 87 } 88 89 override fun dozeTimeTick() { 90 animationViewController?.dozeTimeTick() 91 } 92 93 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 94 super.onLayout(changed, left, top, right, bottom) 95 96 val paddingX = animationViewController?.paddingX ?: 0 97 val paddingY = animationViewController?.paddingY ?: 0 98 99 // Updates sensor rect in relation to the overlay view 100 if (useExpandedOverlay) { 101 animationViewController?.onSensorRectUpdated(RectF(sensorRect)) 102 } else { 103 sensorRect.set( 104 paddingX, 105 paddingY, 106 (overlayParams.sensorBounds.width() + paddingX), 107 (overlayParams.sensorBounds.height() + paddingY) 108 ) 109 110 animationViewController?.onSensorRectUpdated(RectF(sensorRect)) 111 } 112 } 113 114 override fun onAttachedToWindow() { 115 super.onAttachedToWindow() 116 Log.v(TAG, "onAttachedToWindow") 117 } 118 119 override fun onDetachedFromWindow() { 120 super.onDetachedFromWindow() 121 Log.v(TAG, "onDetachedFromWindow") 122 } 123 124 override fun onDraw(canvas: Canvas) { 125 super.onDraw(canvas) 126 if (!isDisplayConfigured) { 127 if (!debugMessage.isNullOrEmpty()) { 128 canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint) 129 } 130 } 131 } 132 133 fun isWithinSensorArea(x: Float, y: Float): Boolean { 134 // The X and Y coordinates of the sensor's center. 135 val translation = animationViewController?.touchTranslation ?: PointF(0f, 0f) 136 val cx = sensorRect.centerX() + translation.x 137 val cy = sensorRect.centerY() + translation.y 138 // Radii along the X and Y axes. 139 val rx = (sensorRect.right - sensorRect.left) / 2.0f 140 val ry = (sensorRect.bottom - sensorRect.top) / 2.0f 141 142 return x > cx - rx * sensorTouchAreaCoefficient && 143 x < cx + rx * sensorTouchAreaCoefficient && 144 y > cy - ry * sensorTouchAreaCoefficient && 145 y < cy + ry * sensorTouchAreaCoefficient && 146 !(animationViewController?.shouldPauseAuth() ?: false) 147 } 148 149 fun configureDisplay(onDisplayConfigured: Runnable) { 150 isDisplayConfigured = true 151 animationViewController?.onDisplayConfiguring() 152 mUdfpsDisplayMode?.enable(onDisplayConfigured) 153 } 154 155 fun unconfigureDisplay() { 156 isDisplayConfigured = false 157 animationViewController?.onDisplayUnconfigured() 158 mUdfpsDisplayMode?.disable(null /* onDisabled */) 159 } 160 } 161