• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.biometrics.domain.interactor
18 
19 import android.content.Context
20 import android.hardware.fingerprint.FingerprintManager
21 import android.util.Log
22 import android.view.MotionEvent
23 import com.android.systemui.biometrics.AuthController
24 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
25 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
26 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.res.R
30 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
31 import javax.inject.Inject
32 import kotlin.math.max
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.channels.awaitClose
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.MutableStateFlow
37 import kotlinx.coroutines.flow.SharingStarted
38 import kotlinx.coroutines.flow.StateFlow
39 import kotlinx.coroutines.flow.asStateFlow
40 import kotlinx.coroutines.flow.distinctUntilChanged
41 import kotlinx.coroutines.flow.map
42 import kotlinx.coroutines.flow.stateIn
43 
44 /** Encapsulates business logic for interacting with the UDFPS overlay. */
45 @SysUISingleton
46 class UdfpsOverlayInteractor
47 @Inject
48 constructor(
49     @Application private val context: Context,
50     private val authController: AuthController,
51     private val selectedUserInteractor: SelectedUserInteractor,
52     private val fingerprintManager: FingerprintManager?,
53     @Application scope: CoroutineScope,
54 ) {
55     private fun calculateIconSize(): Int {
56         val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch)
57         if (pixelPitch <= 0) {
58             Log.e(TAG, "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device.")
59         }
60         return (context.resources.getFloat(R.dimen.udfps_icon_size) / pixelPitch).toInt()
61     }
62 
63     private var iconSize: Int = calculateIconSize()
64 
65     /** Whether a touch is within the under-display fingerprint sensor area */
66     fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean {
67         val isUdfpsEnrolled =
68             authController.isUdfpsEnrolled(selectedUserInteractor.getSelectedUserId())
69         val isWithinOverlayBounds =
70             udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt())
71         return isUdfpsEnrolled && isWithinOverlayBounds
72     }
73 
74     private var _requestId = MutableStateFlow(0L)
75 
76     /** RequestId of current AcquisitionClient */
77     val requestId: StateFlow<Long> = _requestId.asStateFlow()
78 
79     fun setRequestId(requestId: Long) {
80         _requestId.value = requestId
81     }
82 
83     /** Sets whether Udfps overlay should handle touches */
84     fun setHandleTouches(shouldHandle: Boolean) {
85         if (authController.isUdfpsSupported) {
86             fingerprintManager?.setIgnoreDisplayTouches(
87                 requestId.value,
88                 authController.udfpsProps!!.get(0).sensorId,
89                 !shouldHandle,
90             )
91         } else {
92             Log.d(TAG, "setIgnoreDisplayTouches not set, UDFPS not supported")
93         }
94         _shouldHandleTouches.value = shouldHandle
95     }
96 
97     private var _shouldHandleTouches = MutableStateFlow(true)
98 
99     /** Whether Udfps overlay should handle touches */
100     val shouldHandleTouches: StateFlow<Boolean> = _shouldHandleTouches.asStateFlow()
101 
102     /** Returns the current udfpsOverlayParams */
103     val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
104         ConflatedCallbackFlow.conflatedCallbackFlow {
105                 val callback =
106                     object : AuthController.Callback {
107                         override fun onUdfpsLocationChanged(
108                             udfpsOverlayParams: UdfpsOverlayParams
109                         ) {
110                             Log.d(TAG, "udfpsOverlayParams updated $udfpsOverlayParams")
111                             trySendWithFailureLogging(
112                                 udfpsOverlayParams,
113                                 TAG,
114                                 "update udfpsOverlayParams",
115                             )
116                         }
117                     }
118                 authController.addCallback(callback)
119                 awaitClose { authController.removeCallback(callback) }
120             }
121             .stateIn(scope, started = SharingStarted.Eagerly, initialValue = UdfpsOverlayParams())
122 
123     // Padding between the fingerprint icon and its bounding box in pixels.
124     val iconPadding: Flow<Int> =
125         udfpsOverlayParams
126             .map { params ->
127                 val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left
128                 val nativePadding = (sensorWidth - iconSize) / 2
129                 // padding can be negative when udfpsOverlayParams has not been initialized yet.
130                 max(0, (nativePadding * params.scaleFactor).toInt())
131             }
132             .distinctUntilChanged()
133 
134     companion object {
135         private const val TAG = "UdfpsOverlayInteractor"
136     }
137 }
138