1 /*
<lambda>null2  * Copyright (C) 2024 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.settings.biometrics.fingerprint2.domain.interactor
18 
19 import android.content.Context
20 import android.graphics.PointF
21 import android.util.TypedValue
22 import kotlinx.coroutines.flow.Flow
23 import kotlinx.coroutines.flow.MutableStateFlow
24 import kotlinx.coroutines.flow.combine
25 import kotlinx.coroutines.flow.update
26 
27 /**
28  * This interactor provides information about the current offset of the sensor for guided enrollment
29  * on UDFPS devices.
30  */
31 interface UdfpsEnrollInteractor {
32   /** Indicates at which step a UDFPS enrollment is in. */
33   fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int)
34 
35   /** Indicates if guided enrollment should be enabled or not. */
36   fun updateGuidedEnrollment(enabled: Boolean)
37 
38   /**
39    * A flow indicating how much the sensor image drawable should be offset for guided enrollment. A
40    * null point indicates that the icon should be in its default position.
41    */
42   val guidedEnrollmentOffset: Flow<PointF>
43 }
44 
45 /** Keeps track of which guided enrollment point we should be using */
46 class UdfpsEnrollInteractorImpl(
47   applicationContext: Context,
48   accessibilityInteractor: AccessibilityInteractor,
49 ) : UdfpsEnrollInteractor {
50 
51   private var isGuidedEnrollment = MutableStateFlow(false)
52   // Number of pixels per mm
53   val px =
54     TypedValue.applyDimension(
55       TypedValue.COMPLEX_UNIT_MM,
56       1f,
57       applicationContext.resources.displayMetrics,
58     )
59   private val guidedEnrollmentPoints: MutableList<PointF> =
60     mutableListOf(
61       PointF(2.00f * px, 0.00f * px),
62       PointF(0.87f * px, -2.70f * px),
63       PointF(-1.80f * px, -1.31f * px),
64       PointF(-1.80f * px, 1.31f * px),
65       PointF(0.88f * px, 2.70f * px),
66       PointF(3.94f * px, -1.06f * px),
67       PointF(2.90f * px, -4.14f * px),
68       PointF(-0.52f * px, -5.95f * px),
69       PointF(-3.33f * px, -3.33f * px),
70       PointF(-3.99f * px, -0.35f * px),
71       PointF(-3.62f * px, 2.54f * px),
72       PointF(-1.49f * px, 5.57f * px),
73       PointF(2.29f * px, 4.92f * px),
74       PointF(3.82f * px, 1.78f * px),
75     )
76 
onEnrollmentStepnull77   override fun onEnrollmentStep(stepsRemaining: Int, totalStep: Int) {
78     val index = (totalStep - stepsRemaining) % guidedEnrollmentPoints.size
79     _guidedEnrollment.update { guidedEnrollmentPoints[index] }
80   }
81 
updateGuidedEnrollmentnull82   override fun updateGuidedEnrollment(enabled: Boolean) {
83     isGuidedEnrollment.update { enabled }
84   }
85 
86   private val _guidedEnrollment = MutableStateFlow(PointF(0f, 0f))
87   override val guidedEnrollmentOffset: Flow<PointF> =
88     combine(
89       _guidedEnrollment,
90       accessibilityInteractor.isAccessibilityEnabled,
91       isGuidedEnrollment,
guidedEnrollmentEnablednull92     ) { point, accessibilityEnabled, guidedEnrollmentEnabled ->
93       if (accessibilityEnabled || !guidedEnrollmentEnabled) {
94         return@combine PointF(0f, 0f)
95       } else {
96         return@combine PointF(point.x * SCALE, point.y * SCALE)
97       }
98     }
99 
100   companion object {
101     private const val SCALE = 0.5f
102   }
103 }
104