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.deviceentry.ui.viewmodel
18 
19 import android.graphics.Point
20 import android.view.MotionEvent
21 import android.view.View
22 import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
23 import com.android.systemui.biometrics.UdfpsUtils
24 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
25 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
26 import kotlinx.coroutines.ExperimentalCoroutinesApi
27 import kotlinx.coroutines.flow.Flow
28 import kotlinx.coroutines.flow.StateFlow
29 import kotlinx.coroutines.flow.flatMapLatest
30 import kotlinx.coroutines.flow.flowOf
31 
32 /** Models the UI state for the UDFPS accessibility overlay */
33 @ExperimentalCoroutinesApi
34 abstract class UdfpsAccessibilityOverlayViewModel(
35     udfpsOverlayInteractor: UdfpsOverlayInteractor,
36     accessibilityInteractor: AccessibilityInteractor,
37 ) {
38     private val udfpsUtils = UdfpsUtils()
39     private val udfpsOverlayParams: StateFlow<UdfpsOverlayParams> =
40         udfpsOverlayInteractor.udfpsOverlayParams
41 
42     val visible: Flow<Boolean> =
43         accessibilityInteractor.isTouchExplorationEnabled.flatMapLatest { touchExplorationEnabled ->
44             if (touchExplorationEnabled) {
45                 isVisibleWhenTouchExplorationEnabled()
46             } else {
47                 flowOf(false)
48             }
49         }
50 
51     abstract fun isVisibleWhenTouchExplorationEnabled(): Flow<Boolean>
52 
53     /** Give directional feedback to help the user authenticate with UDFPS. */
54     fun onHoverEvent(v: View, event: MotionEvent): Boolean {
55         val overlayParams = udfpsOverlayParams.value
56         val scaledTouch: Point =
57             udfpsUtils.getTouchInNativeCoordinates(
58                 event.getPointerId(0),
59                 event,
60                 overlayParams, /* rotateToPortrait */
61                 false
62             )
63 
64         if (
65             !udfpsUtils.isWithinSensorArea(
66                 event.getPointerId(0),
67                 event,
68                 overlayParams,
69                 /* rotateTouchToPortrait */ false
70             )
71         ) {
72             // view only receives motionEvents when [visible] which requires touchExplorationEnabled
73             val announceStr =
74                 udfpsUtils.onTouchOutsideOfSensorArea(
75                     /* touchExplorationEnabled */ true,
76                     v.context,
77                     scaledTouch.x,
78                     scaledTouch.y,
79                     overlayParams,
80                     /* touchRotatedToPortrait */ false
81                 )
82             if (announceStr != null) {
83                 v.announceForAccessibility(announceStr)
84             }
85         }
86         // always let the motion events go through to underlying views
87         return false
88     }
89 }
90