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 kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.channels.awaitClose 34 import kotlinx.coroutines.flow.Flow 35 import kotlinx.coroutines.flow.MutableStateFlow 36 import kotlinx.coroutines.flow.SharingStarted 37 import kotlinx.coroutines.flow.StateFlow 38 import kotlinx.coroutines.flow.asStateFlow 39 import kotlinx.coroutines.flow.map 40 import kotlinx.coroutines.flow.stateIn 41 42 /** Encapsulates business logic for interacting with the UDFPS overlay. */ 43 @SysUISingleton 44 class UdfpsOverlayInteractor 45 @Inject 46 constructor( 47 @Application private val context: Context, 48 private val authController: AuthController, 49 private val selectedUserInteractor: SelectedUserInteractor, 50 private val fingerprintManager: FingerprintManager?, 51 @Application scope: CoroutineScope 52 ) { 53 private fun calculateIconSize(): Int { 54 val pixelPitch = context.resources.getFloat(R.dimen.pixel_pitch) 55 if (pixelPitch <= 0) { 56 Log.e( 57 "UdfpsOverlayInteractor", 58 "invalid pixelPitch: $pixelPitch. Pixel pitch must be updated per device." 59 ) 60 } 61 return (context.resources.getFloat(R.dimen.udfps_icon_size) / pixelPitch).toInt() 62 } 63 64 private var iconSize: Int = calculateIconSize() 65 66 /** Whether a touch is within the under-display fingerprint sensor area */ 67 fun isTouchWithinUdfpsArea(ev: MotionEvent): Boolean { 68 val isUdfpsEnrolled = 69 authController.isUdfpsEnrolled(selectedUserInteractor.getSelectedUserId()) 70 val isWithinOverlayBounds = 71 udfpsOverlayParams.value.overlayBounds.contains(ev.rawX.toInt(), ev.rawY.toInt()) 72 return isUdfpsEnrolled && isWithinOverlayBounds 73 } 74 75 private var _requestId = MutableStateFlow(0L) 76 77 /** RequestId of current AcquisitionClient */ 78 val requestId: StateFlow<Long> = _requestId.asStateFlow() 79 80 fun setRequestId(requestId: Long) { 81 _requestId.value = requestId 82 } 83 84 /** Sets whether Udfps overlay should handle touches */ 85 fun setHandleTouches(shouldHandle: Boolean = true) { 86 if (authController.isUltrasonicUdfpsSupported 87 && shouldHandle != _shouldHandleTouches.value) { 88 fingerprintManager?.setIgnoreDisplayTouches( 89 requestId.value, 90 authController.udfpsProps!!.get(0).sensorId, 91 !shouldHandle 92 ) 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 trySendWithFailureLogging( 111 udfpsOverlayParams, 112 TAG, 113 "update udfpsOverlayParams" 114 ) 115 } 116 } 117 authController.addCallback(callback) 118 awaitClose { authController.removeCallback(callback) } 119 } 120 .stateIn(scope, started = SharingStarted.Eagerly, initialValue = UdfpsOverlayParams()) 121 122 // Padding between the fingerprint icon and its bounding box in pixels. 123 val iconPadding: Flow<Int> = 124 udfpsOverlayParams.map { params -> 125 val sensorWidth = params.nativeSensorBounds.right - params.nativeSensorBounds.left 126 val nativePadding = (sensorWidth - iconSize) / 2 127 (nativePadding * params.scaleFactor).toInt() 128 } 129 130 companion object { 131 private const val TAG = "UdfpsOverlayInteractor" 132 } 133 } 134