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