1 /*
2  * 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 
18 package com.android.systemui.keyguard.domain.interactor
19 
20 import android.app.StatusBarManager
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.RoboPilotTest
24 import com.android.systemui.SysuiTestCase
25 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
26 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
27 import com.android.systemui.coroutines.collectLastValue
28 import com.android.systemui.flags.FakeFeatureFlags
29 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
30 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
31 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
32 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
33 import com.google.common.truth.Truth.assertThat
34 import kotlinx.coroutines.flow.onCompletion
35 import kotlinx.coroutines.test.TestScope
36 import kotlinx.coroutines.test.runCurrent
37 import kotlinx.coroutines.test.runTest
38 import org.junit.Before
39 import org.junit.Test
40 import org.junit.runner.RunWith
41 import org.mockito.MockitoAnnotations
42 
43 @SmallTest
44 @RoboPilotTest
45 @RunWith(AndroidJUnit4::class)
46 class KeyguardInteractorTest : SysuiTestCase() {
47     private lateinit var commandQueue: FakeCommandQueue
48     private lateinit var featureFlags: FakeFeatureFlags
49     private lateinit var testScope: TestScope
50 
51     private lateinit var underTest: KeyguardInteractor
52     private lateinit var repository: FakeKeyguardRepository
53     private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
54     private lateinit var configurationRepository: FakeConfigurationRepository
55 
56     @Before
57     fun setUp() {
58         MockitoAnnotations.initMocks(this)
59         featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
60         commandQueue = FakeCommandQueue()
61         testScope = TestScope()
62         repository = FakeKeyguardRepository()
63         bouncerRepository = FakeKeyguardBouncerRepository()
64         configurationRepository = FakeConfigurationRepository()
65         underTest =
66             KeyguardInteractor(
67                 repository,
68                 commandQueue,
69                 featureFlags,
70                 bouncerRepository,
71                 configurationRepository,
72             )
73     }
74 
75     @Test
76     fun onCameraLaunchDetected() =
77         testScope.runTest {
78             val flow = underTest.onCameraLaunchDetected
79             var cameraLaunchSource = collectLastValue(flow)
80             runCurrent()
81 
82             commandQueue.doForEachCallback {
83                 it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
84             }
85             assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
86 
87             commandQueue.doForEachCallback {
88                 it.onCameraLaunchGestureDetected(
89                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
90                 )
91             }
92             assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
93 
94             commandQueue.doForEachCallback {
95                 it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
96             }
97             assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
98 
99             commandQueue.doForEachCallback {
100                 it.onCameraLaunchGestureDetected(
101                     StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
102                 )
103             }
104             assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
105 
106             flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
107         }
108 
109     @Test
110     fun testKeyguardGuardVisibilityStopsSecureCamera() =
111         testScope.runTest {
112             val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
113             runCurrent()
114 
115             commandQueue.doForEachCallback {
116                 it.onCameraLaunchGestureDetected(
117                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
118                 )
119             }
120 
121             assertThat(secureCameraActive()).isTrue()
122 
123             // Keyguard is showing but occluded
124             repository.setKeyguardShowing(true)
125             repository.setKeyguardOccluded(true)
126             assertThat(secureCameraActive()).isTrue()
127 
128             // Keyguard is showing and not occluded
129             repository.setKeyguardOccluded(false)
130             assertThat(secureCameraActive()).isFalse()
131         }
132 
133     @Test
134     fun testBouncerShowingResetsSecureCameraState() =
135         testScope.runTest {
136             val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
137             runCurrent()
138 
139             commandQueue.doForEachCallback {
140                 it.onCameraLaunchGestureDetected(
141                     StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
142                 )
143             }
144             assertThat(secureCameraActive()).isTrue()
145 
146             // Keyguard is showing and not occluded
147             repository.setKeyguardShowing(true)
148             repository.setKeyguardOccluded(true)
149             assertThat(secureCameraActive()).isTrue()
150 
151             bouncerRepository.setPrimaryShow(true)
152             assertThat(secureCameraActive()).isFalse()
153         }
154 
155     @Test
156     fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
157         var isVisible = collectLastValue(underTest.isKeyguardVisible)
158         repository.setKeyguardShowing(true)
159         repository.setKeyguardOccluded(false)
160 
161         assertThat(isVisible()).isTrue()
162 
163         repository.setKeyguardOccluded(true)
164         assertThat(isVisible()).isFalse()
165 
166         repository.setKeyguardShowing(false)
167         repository.setKeyguardOccluded(true)
168         assertThat(isVisible()).isFalse()
169     }
170 
171     @Test
172     fun secureCameraIsNotActiveWhenNoCameraLaunchEventHasBeenFiredYet() =
173         testScope.runTest {
174             val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
175             runCurrent()
176 
177             assertThat(secureCameraActive()).isFalse()
178         }
179 }
180