1 /*
<lambda>null2 * Copyright (C) 2022 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
17 package com.android.systemui.decor
18
19 import android.content.Context
20 import android.util.Log
21 import android.view.DisplayCutout
22 import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM
23 import android.view.DisplayCutout.BOUNDS_POSITION_LEFT
24 import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT
25 import android.view.DisplayCutout.BOUNDS_POSITION_TOP
26 import android.view.DisplayInfo
27 import android.view.Gravity
28 import android.view.Surface
29 import android.view.View
30 import android.view.ViewGroup
31 import android.widget.FrameLayout
32 import com.android.keyguard.KeyguardUpdateMonitor
33 import com.android.systemui.FaceScanningOverlay
34 import com.android.systemui.biometrics.AuthController
35 import com.android.systemui.dagger.SysUISingleton
36 import com.android.systemui.dagger.qualifiers.Main
37 import com.android.systemui.flags.FeatureFlags
38 import com.android.systemui.log.ScreenDecorationsLogger
39 import com.android.systemui.plugins.statusbar.StatusBarStateController
40 import java.util.concurrent.Executor
41 import javax.inject.Inject
42
43 @SysUISingleton
44 class FaceScanningProviderFactory @Inject constructor(
45 private val authController: AuthController,
46 private val context: Context,
47 private val statusBarStateController: StatusBarStateController,
48 private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
49 @Main private val mainExecutor: Executor,
50 private val logger: ScreenDecorationsLogger,
51 private val featureFlags: FeatureFlags,
52 ) : DecorProviderFactory() {
53 private val display = context.display
54 private val displayInfo = DisplayInfo()
55
56 override val hasProviders: Boolean
57 get() {
58 if (authController.faceSensorLocation == null) {
59 return false
60 }
61
62 // update display info
63 display?.getDisplayInfo(displayInfo) ?: run {
64 Log.w(TAG, "display is null, can't update displayInfo")
65 }
66 return DisplayCutout.getFillBuiltInDisplayCutout(
67 context.resources, displayInfo.uniqueId)
68 }
69
70 override val providers: List<DecorProvider>
71 get() {
72 if (!hasProviders) {
73 return emptyList()
74 }
75
76 return ArrayList<DecorProvider>().also { list ->
77 // displayInfo must be updated before using it; however it will already have
78 // been updated when accessing the hasProviders field above
79 displayInfo.displayCutout?.getBoundBaseOnCurrentRotation()?.let { bounds ->
80 // Add a face scanning view for each screen orientation.
81 // Cutout drawing is updated in ScreenDecorations#updateCutout
82 for (bound in bounds) {
83 list.add(
84 FaceScanningOverlayProviderImpl(
85 bound.baseOnRotation0(displayInfo.rotation),
86 authController,
87 statusBarStateController,
88 keyguardUpdateMonitor,
89 mainExecutor,
90 logger,
91 featureFlags,
92 )
93 )
94 }
95 }
96 }
97 }
98
99 fun canShowFaceScanningAnim(): Boolean {
100 return hasProviders && keyguardUpdateMonitor.isFaceEnrolled
101 }
102
103 fun shouldShowFaceScanningAnim(): Boolean {
104 return canShowFaceScanningAnim() &&
105 (keyguardUpdateMonitor.isFaceDetectionRunning || authController.isShowing)
106 }
107 }
108
109 class FaceScanningOverlayProviderImpl(
110 override val alignedBound: Int,
111 private val authController: AuthController,
112 private val statusBarStateController: StatusBarStateController,
113 private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
114 private val mainExecutor: Executor,
115 private val logger: ScreenDecorationsLogger,
116 private val featureFlags: FeatureFlags,
117 ) : BoundDecorProvider() {
118 override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
119
onReloadResAndMeasurenull120 override fun onReloadResAndMeasure(
121 view: View,
122 reloadToken: Int,
123 @Surface.Rotation rotation: Int,
124 tintColor: Int,
125 displayUniqueId: String?
126 ) {
127 (view.layoutParams as FrameLayout.LayoutParams).let {
128 updateLayoutParams(it, rotation)
129 view.layoutParams = it
130 (view as? FaceScanningOverlay)?.let { overlay ->
131 overlay.setColor(tintColor)
132 overlay.updateConfiguration(displayUniqueId)
133 }
134 }
135 }
136
inflateViewnull137 override fun inflateView(
138 context: Context,
139 parent: ViewGroup,
140 @Surface.Rotation rotation: Int,
141 tintColor: Int
142 ): View {
143 val view = FaceScanningOverlay(
144 context,
145 alignedBound,
146 statusBarStateController,
147 keyguardUpdateMonitor,
148 mainExecutor,
149 logger,
150 authController,
151 featureFlags
152 )
153 view.id = viewId
154 view.setColor(tintColor)
155 FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
156 ViewGroup.LayoutParams.MATCH_PARENT).let {
157 updateLayoutParams(it, rotation)
158 parent.addView(view, it)
159 }
160 return view
161 }
162
updateLayoutParamsnull163 private fun updateLayoutParams(
164 layoutParams: FrameLayout.LayoutParams,
165 @Surface.Rotation rotation: Int
166 ) {
167 layoutParams.let { lp ->
168 lp.width = ViewGroup.LayoutParams.MATCH_PARENT
169 lp.height = ViewGroup.LayoutParams.MATCH_PARENT
170 logger.faceSensorLocation(authController.faceSensorLocation)
171 authController.faceSensorLocation?.y?.let { faceAuthSensorHeight ->
172 val faceScanningHeight = (faceAuthSensorHeight * 2)
173 when (rotation) {
174 Surface.ROTATION_0, Surface.ROTATION_180 ->
175 lp.height = faceScanningHeight
176 Surface.ROTATION_90, Surface.ROTATION_270 ->
177 lp.width = faceScanningHeight
178 }
179 }
180
181 lp.gravity = when (rotation) {
182 Surface.ROTATION_0 -> Gravity.TOP or Gravity.START
183 Surface.ROTATION_90 -> Gravity.LEFT or Gravity.START
184 Surface.ROTATION_180 -> Gravity.BOTTOM or Gravity.END
185 Surface.ROTATION_270 -> Gravity.RIGHT or Gravity.END
186 else -> -1 /* invalid rotation */
187 }
188 }
189 }
190 }
191
DisplayCutoutnull192 fun DisplayCutout.getBoundBaseOnCurrentRotation(): List<Int> {
193 return ArrayList<Int>().also {
194 if (!boundingRectLeft.isEmpty) {
195 it.add(BOUNDS_POSITION_LEFT)
196 }
197 if (!boundingRectTop.isEmpty) {
198 it.add(BOUNDS_POSITION_TOP)
199 }
200 if (!boundingRectRight.isEmpty) {
201 it.add(BOUNDS_POSITION_RIGHT)
202 }
203 if (!boundingRectBottom.isEmpty) {
204 it.add(BOUNDS_POSITION_BOTTOM)
205 }
206 }
207 }
208
baseOnRotation0null209 fun Int.baseOnRotation0(@DisplayCutout.BoundsPosition currentRotation: Int): Int {
210 return when (currentRotation) {
211 Surface.ROTATION_0 -> this
212 Surface.ROTATION_90 -> when (this) {
213 BOUNDS_POSITION_LEFT -> BOUNDS_POSITION_TOP
214 BOUNDS_POSITION_TOP -> BOUNDS_POSITION_RIGHT
215 BOUNDS_POSITION_RIGHT -> BOUNDS_POSITION_BOTTOM
216 else /* BOUNDS_POSITION_BOTTOM */ -> BOUNDS_POSITION_LEFT
217 }
218 Surface.ROTATION_270 -> when (this) {
219 BOUNDS_POSITION_LEFT -> BOUNDS_POSITION_BOTTOM
220 BOUNDS_POSITION_TOP -> BOUNDS_POSITION_LEFT
221 BOUNDS_POSITION_RIGHT -> BOUNDS_POSITION_TOP
222 else /* BOUNDS_POSITION_BOTTOM */ -> BOUNDS_POSITION_RIGHT
223 }
224 else /* Surface.ROTATION_180 */ -> when (this) {
225 BOUNDS_POSITION_LEFT -> BOUNDS_POSITION_RIGHT
226 BOUNDS_POSITION_TOP -> BOUNDS_POSITION_BOTTOM
227 BOUNDS_POSITION_RIGHT -> BOUNDS_POSITION_LEFT
228 else /* BOUNDS_POSITION_BOTTOM */ -> BOUNDS_POSITION_TOP
229 }
230 }
231 }
232
233 private const val TAG = "FaceScanningProvider"
234