• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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.animation.ValueAnimator
19 import android.graphics.PointF
20 import android.graphics.RectF
21 import com.android.systemui.Dumpable
22 import com.android.systemui.animation.Interpolators
23 import com.android.systemui.dump.DumpManager
24 import com.android.systemui.plugins.statusbar.StatusBarStateController
25 import com.android.systemui.shade.ShadeExpansionListener
26 import com.android.systemui.shade.ShadeExpansionStateManager
27 import com.android.systemui.statusbar.phone.SystemUIDialogManager
28 import com.android.systemui.util.ViewController
29 import java.io.PrintWriter
30 
31 /**
32  * Handles:
33  * 1. registering for listeners when its view is attached and unregistering on view detached
34  * 2. pausing UDFPS when FingerprintManager may still be running but we temporarily want to hide
35  * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
36  * 3. sending events to its view including:
37  * - enabling and disabling of the UDFPS display mode
38  * - sensor position changes
39  * - doze time event
40  */
41 abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
42     view: T,
43     protected val statusBarStateController: StatusBarStateController,
44     protected val shadeExpansionStateManager: ShadeExpansionStateManager,
45     protected val dialogManager: SystemUIDialogManager,
46     private val dumpManager: DumpManager
47 ) : ViewController<T>(view), Dumpable {
48 
49     protected abstract val tag: String
50 
51     private val view: T
52         get() = mView!!
53 
54     private var dialogAlphaAnimator: ValueAnimator? = null
55     private val dialogListener = SystemUIDialogManager.Listener { runDialogAlphaAnimator() }
56 
57     private val shadeExpansionListener = ShadeExpansionListener { event ->
58         // Notification shade can be expanded but not visible (fraction: 0.0), for example
59         // when a heads-up notification (HUN) is showing.
60         notificationShadeVisible = event.expanded && event.fraction > 0f
61         view.onExpansionChanged(event.fraction)
62         updatePauseAuth()
63     }
64 
65     /** If the notification shade is visible. */
66     var notificationShadeVisible: Boolean = false
67 
68     /**
69      * The amount of translation needed if the view currently requires the user to touch
70      * somewhere other than the exact center of the sensor. For example, this can happen
71      * during guided enrollment.
72      */
73     open val touchTranslation: PointF = PointF(0f, 0f)
74 
75     /**
76      * X-Padding to add to left and right of the sensor rectangle area to increase the size of our
77      * window to draw within.
78      */
79     open val paddingX: Int = 0
80 
81     /**
82      * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
83      * window to draw within.
84      */
85     open val paddingY: Int = 0
86 
87     open fun updateAlpha() {
88         view.updateAlpha()
89     }
90 
91     fun runDialogAlphaAnimator() {
92         val hideAffordance = dialogManager.shouldHideAffordance()
93         dialogAlphaAnimator?.cancel()
94         dialogAlphaAnimator = ValueAnimator.ofFloat(
95                 view.calculateAlpha() / 255f,
96                 if (hideAffordance) 0f else 1f)
97                 .apply {
98             duration = if (hideAffordance) 83L else 200L
99             interpolator = if (hideAffordance) Interpolators.LINEAR else Interpolators.ALPHA_IN
100 
101             addUpdateListener { animatedValue ->
102                 view.setDialogSuggestedAlpha(animatedValue.animatedValue as Float)
103                 updateAlpha()
104                 updatePauseAuth()
105             }
106             start()
107         }
108     }
109 
110     override fun onViewAttached() {
111         shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
112         dialogManager.registerListener(dialogListener)
113         dumpManager.registerDumpable(dumpTag, this)
114     }
115 
116     override fun onViewDetached() {
117         shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
118         dialogManager.unregisterListener(dialogListener)
119         dumpManager.unregisterDumpable(dumpTag)
120     }
121 
122     /**
123      * in some cases, onViewAttached is called for the newly added view using an instance of
124      * this controller before onViewDetached is called on the previous view, so we must have a
125      * unique [dumpTag] per instance of this class.
126      */
127     private val dumpTag = "$tag ($this)"
128 
129     override fun dump(pw: PrintWriter, args: Array<String>) {
130         pw.println("mNotificationShadeVisible=$notificationShadeVisible")
131         pw.println("shouldPauseAuth()=" + shouldPauseAuth())
132         pw.println("isPauseAuth=" + view.isPauseAuth)
133         pw.println("dialogSuggestedAlpha=" + view.dialogSuggestedAlpha)
134     }
135 
136     /**
137      * Returns true if the fingerprint manager is running, but we want to temporarily pause
138      * authentication.
139      */
140     open fun shouldPauseAuth(): Boolean {
141         return notificationShadeVisible || dialogManager.shouldHideAffordance()
142     }
143 
144     /**
145      * Send pause auth update to our view.
146      */
147     fun updatePauseAuth() {
148         if (view.setPauseAuth(shouldPauseAuth())) {
149             view.postInvalidate()
150         }
151     }
152 
153     /**
154      * Send sensor position change to our view. This rect contains paddingX and paddingY.
155      */
156     fun onSensorRectUpdated(sensorRect: RectF) {
157         view.onSensorRectUpdated(sensorRect)
158     }
159 
160     /**
161      * Send dozeTimeTick to view in case it wants to handle its burn-in offset.
162      */
163     fun dozeTimeTick() {
164         if (view.dozeTimeTick()) {
165             view.postInvalidate()
166         }
167     }
168 
169     /**
170      * The display began transitioning into the UDFPS mode and the fingerprint manager started
171      * authenticating.
172      */
173     fun onDisplayConfiguring() {
174         view.onDisplayConfiguring()
175         view.postInvalidate()
176     }
177 
178     /**
179      * The display transitioned away from the UDFPS mode and the fingerprint manager stopped
180      * authenticating.
181      */
182     fun onDisplayUnconfigured() {
183         view.onDisplayUnconfigured()
184         view.postInvalidate()
185     }
186 
187     /**
188      * Whether to listen for touches outside of the view.
189      */
190     open fun listenForTouchesOutsideView(): Boolean = false
191 
192     /**
193      * Called when a view should announce an accessibility event.
194      */
195     open fun doAnnounceForAccessibility(str: String) {}
196 }
197