• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 
17 package com.android.systemui.keyguard
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.animation.ValueAnimator
22 import android.content.res.Resources
23 import android.database.ContentObserver
24 import android.graphics.Bitmap
25 import android.graphics.BitmapFactory
26 import android.graphics.Color
27 import android.graphics.drawable.ColorDrawable
28 import android.hardware.biometrics.BiometricSourceType
29 import android.os.Handler
30 import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
31 import android.util.MathUtils
32 import android.view.View
33 import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
34 import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
35 import com.android.internal.annotations.VisibleForTesting
36 import com.android.keyguard.KeyguardUpdateMonitor
37 import com.android.keyguard.KeyguardUpdateMonitorCallback
38 import com.android.systemui.Dumpable
39 import com.android.systemui.R
40 import com.android.systemui.dagger.SysUISingleton
41 import com.android.systemui.dump.DumpManager
42 import com.android.systemui.statusbar.NotificationShadeWindowController
43 import com.android.systemui.util.settings.GlobalSettings
44 import com.android.systemui.util.settings.SystemSettings
45 import java.io.FileDescriptor
46 import java.io.PrintWriter
47 import java.lang.Float.max
48 import java.util.concurrent.TimeUnit
49 
50 val DEFAULT_ANIMATION_DURATION = TimeUnit.SECONDS.toMillis(4)
51 val MAX_SCREEN_BRIGHTNESS = 100 // 0..100
52 val MAX_SCRIM_OPACTY = 50 // 0..100
53 val DEFAULT_USE_FACE_WALLPAPER = false
54 
55 /**
56  * This class is responsible for ramping up the display brightness (and white overlay) in order
57  * to mitigate low light conditions when running face auth without an IR camera.
58  */
59 @SysUISingleton
60 open class FaceAuthScreenBrightnessController(
61     private val notificationShadeWindowController: NotificationShadeWindowController,
62     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
63     private val resources: Resources,
64     private val globalSettings: GlobalSettings,
65     private val systemSettings: SystemSettings,
66     private val mainHandler: Handler,
67     private val dumpManager: DumpManager,
68     private val enabled: Boolean
69 ) : Dumpable {
70 
71     private var userDefinedBrightness: Float = 1f
72     @VisibleForTesting
73     var useFaceAuthWallpaper = globalSettings
74             .getInt("sysui.use_face_auth_wallpaper", if (DEFAULT_USE_FACE_WALLPAPER) 1 else 0) == 1
75     private val brightnessAnimationDuration = globalSettings
76             .getLong("sysui.face_brightness_anim_duration", DEFAULT_ANIMATION_DURATION)
77     private val maxScreenBrightness = globalSettings
78             .getInt("sysui.face_max_brightness", MAX_SCREEN_BRIGHTNESS) / 100f
79     private val maxScrimOpacity = globalSettings
80             .getInt("sysui.face_max_scrim_opacity", MAX_SCRIM_OPACTY) / 100f
81     private val keyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
onBiometricRunningStateChangednull82         override fun onBiometricRunningStateChanged(
83             running: Boolean,
84             biometricSourceType: BiometricSourceType?
85         ) {
86             if (biometricSourceType != BiometricSourceType.FACE) {
87                 return
88             }
89             // TODO enable only when receiving a low-light error
90             overridingBrightness = if (enabled) running else false
91         }
92     }
93     private lateinit var whiteOverlay: View
94     private var brightnessAnimator: ValueAnimator? = null
95     private var overridingBrightness = false
96     set(value) {
97         if (field == value) {
98             return
99         }
100         field = value
101         brightnessAnimator?.cancel()
102 
103         if (!value) {
104             notificationShadeWindowController.setFaceAuthDisplayBrightness(BRIGHTNESS_OVERRIDE_NONE)
105             if (whiteOverlay.alpha > 0) {
<lambda>null106                 brightnessAnimator = createAnimator(whiteOverlay.alpha, 0f).apply {
107                     duration = 200
108                     addUpdateListener {
109                         whiteOverlay.alpha = it.animatedValue as Float
110                     }
111                     addListener(object : AnimatorListenerAdapter() {
112                         override fun onAnimationEnd(animation: Animator?) {
113                             whiteOverlay.visibility = View.INVISIBLE
114                             brightnessAnimator = null
115                         }
116                     })
117                     start()
118                 }
119             }
120             return
121         }
122 
123         val targetBrightness = max(maxScreenBrightness, userDefinedBrightness)
124         whiteOverlay.visibility = View.VISIBLE
<lambda>null125         brightnessAnimator = createAnimator(0f, 1f).apply {
126             duration = brightnessAnimationDuration
127             addUpdateListener {
128                 val progress = it.animatedValue as Float
129                 val brightnessProgress = MathUtils.constrainedMap(
130                         userDefinedBrightness, targetBrightness, 0f, 0.5f, progress)
131                 val scrimProgress = MathUtils.constrainedMap(
132                         0f, maxScrimOpacity, 0.5f, 1f, progress)
133                 notificationShadeWindowController.setFaceAuthDisplayBrightness(brightnessProgress)
134                 whiteOverlay.alpha = scrimProgress
135             }
136             addListener(object : AnimatorListenerAdapter() {
137                 override fun onAnimationEnd(animation: Animator?) {
138                     brightnessAnimator = null
139                 }
140             })
141             start()
142         }
143     }
144 
145     @VisibleForTesting
createAnimatornull146     open fun createAnimator(start: Float, end: Float) = ValueAnimator.ofFloat(start, end)
147 
148     /**
149      * Returns a bitmap that should be used by the lock screen as a wallpaper, if face auth requires
150      * a secure wallpaper.
151      */
152     var faceAuthWallpaper: Bitmap? = null
153     get() {
154         val user = KeyguardUpdateMonitor.getCurrentUser()
155         if (useFaceAuthWallpaper && keyguardUpdateMonitor.isFaceAuthEnabledForUser(user)) {
156             val options = BitmapFactory.Options().apply {
157                 inScaled = false
158             }
159             return BitmapFactory.decodeResource(resources, R.drawable.face_auth_wallpaper, options)
160         }
161         return null
162     }
163     private set
164 
attachnull165     fun attach(overlayView: View) {
166         whiteOverlay = overlayView
167         whiteOverlay.focusable = FLAG_NOT_FOCUSABLE
168         whiteOverlay.background = ColorDrawable(Color.WHITE)
169         whiteOverlay.isEnabled = false
170         whiteOverlay.alpha = 0f
171         whiteOverlay.visibility = View.INVISIBLE
172 
173         dumpManager.registerDumpable(this.javaClass.name, this)
174         keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback)
175         systemSettings.registerContentObserver(SCREEN_BRIGHTNESS_FLOAT,
176             object : ContentObserver(mainHandler) {
177                 override fun onChange(selfChange: Boolean) {
178                     userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
179                 }
180             })
181         userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT, 1f)
182     }
183 
dumpnull184     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
185         pw.apply {
186             println("overridingBrightness: $overridingBrightness")
187             println("useFaceAuthWallpaper: $useFaceAuthWallpaper")
188             println("brightnessAnimator: $brightnessAnimator")
189             println("brightnessAnimationDuration: $brightnessAnimationDuration")
190             println("maxScreenBrightness: $maxScreenBrightness")
191             println("userDefinedBrightness: $userDefinedBrightness")
192             println("enabled: $enabled")
193         }
194     }
195 }