1 /*
2  * Copyright (C) 2020 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.keyguard
18 
19 import android.testing.TestableLooper
20 import android.view.View
21 import androidx.constraintlayout.widget.ConstraintLayout
22 import androidx.constraintlayout.widget.ConstraintSet
23 import androidx.test.ext.junit.runners.AndroidJUnit4
24 import androidx.test.filters.SmallTest
25 import com.android.internal.util.LatencyTracker
26 import com.android.internal.widget.LockPatternUtils
27 import com.android.systemui.Flags as AConfigFlags
28 import com.android.systemui.SysuiTestCase
29 import com.android.systemui.classifier.FalsingCollector
30 import com.android.systemui.classifier.FalsingCollectorFake
31 import com.android.systemui.flags.FakeFeatureFlags
32 import com.android.systemui.flags.Flags
33 import com.android.systemui.res.R
34 import com.android.systemui.statusbar.policy.DevicePostureController
35 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
36 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
37 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
38 import com.android.systemui.util.mockito.any
39 import com.android.systemui.util.mockito.whenever
40 import com.google.common.truth.Truth.assertThat
41 import org.junit.Before
42 import org.junit.Test
43 import org.junit.runner.RunWith
44 import org.mockito.ArgumentCaptor
45 import org.mockito.ArgumentMatchers.anyBoolean
46 import org.mockito.ArgumentMatchers.anyInt
47 import org.mockito.ArgumentMatchers.anyString
48 import org.mockito.Captor
49 import org.mockito.Mock
50 import org.mockito.Mockito.never
51 import org.mockito.Mockito.reset
52 import org.mockito.Mockito.verify
53 import org.mockito.Mockito.`when`
54 import org.mockito.MockitoAnnotations
55 
56 @SmallTest
57 @RunWith(AndroidJUnit4::class)
58 @TestableLooper.RunWithLooper
59 class KeyguardPatternViewControllerTest : SysuiTestCase() {
60     private lateinit var mKeyguardPatternView: KeyguardPatternView
61 
62     @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
63 
64     @Mock private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
65 
66     @Mock private lateinit var mLockPatternUtils: LockPatternUtils
67 
68     @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
69 
70     @Mock private lateinit var mLatencyTracker: LatencyTracker
71     private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
72 
73     @Mock private lateinit var mEmergencyButtonController: EmergencyButtonController
74 
75     @Mock
76     private lateinit var mKeyguardMessageAreaControllerFactory:
77         KeyguardMessageAreaController.Factory
78 
79     @Mock private lateinit var mSelectedUserInteractor: SelectedUserInteractor
80 
81     @Mock
82     private lateinit var mKeyguardMessageAreaController:
83         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
84 
85     @Mock private lateinit var mPostureController: DevicePostureController
86 
87     private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
88     private lateinit var fakeFeatureFlags: FakeFeatureFlags
89 
90     @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
91 
92     @Before
setupnull93     fun setup() {
94         MockitoAnnotations.initMocks(this)
95         whenever(mKeyguardMessageAreaControllerFactory.create(any()))
96             .thenReturn(mKeyguardMessageAreaController)
97         fakeFeatureFlags = FakeFeatureFlags()
98         fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
99         mKeyguardPatternView =
100             View.inflate(mContext, R.layout.keyguard_pattern_view, null) as KeyguardPatternView
101         mKeyguardPatternView.setIsLockScreenLandscapeEnabled(false)
102         mKeyguardPatternViewController =
103             KeyguardPatternViewController(
104                 mKeyguardPatternView,
105                 mKeyguardUpdateMonitor,
106                 mSecurityMode,
107                 mLockPatternUtils,
108                 mKeyguardSecurityCallback,
109                 mLatencyTracker,
110                 mFalsingCollector,
111                 mEmergencyButtonController,
112                 mKeyguardMessageAreaControllerFactory,
113                 mPostureController,
114                 fakeFeatureFlags,
115                 mSelectedUserInteractor
116             )
117         mKeyguardPatternView.onAttachedToWindow()
118     }
119 
120     @Test
onViewAttached_deviceHalfFolded_propagatedToPatternViewnull121     fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
122         overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
123         whenever(mPostureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
124 
125         mKeyguardPatternViewController.onViewAttached()
126 
127         assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
128     }
129 
130     @Test
onDevicePostureChanged_deviceOpened_propagatedToPatternViewnull131     fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
132         overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
133         whenever(mPostureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
134 
135         mKeyguardPatternViewController.onViewAttached()
136 
137         // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
138         assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
139 
140         // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
141         verify(mPostureController).addCallback(postureCallbackCaptor.capture())
142         val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
143         postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
144 
145         // Simulate posture change to same state with callback
146         assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
147 
148         postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
149 
150         // Verify view is still in posture state DEVICE_POSTURE_OPENED
151         assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
152     }
153 
getPatternTopGuidelinenull154     private fun getPatternTopGuideline(): Float {
155         val cs = ConstraintSet()
156         val container =
157             mKeyguardPatternView.requireViewById(R.id.pattern_container) as ConstraintLayout
158         cs.clone(container)
159         return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
160     }
161 
getHalfOpenedBouncerHeightRationull162     private fun getHalfOpenedBouncerHeightRatio(): Float {
163         return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
164     }
165 
166     @Test
withFeatureFlagOn_oldMessage_isHiddennull167     fun withFeatureFlagOn_oldMessage_isHidden() {
168         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
169 
170         mKeyguardPatternViewController.onViewAttached()
171 
172         verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable()
173     }
174 
175     @Test
onPause_resetsTextnull176     fun onPause_resetsText() {
177         mKeyguardPatternViewController.init()
178         mKeyguardPatternViewController.onPause()
179         verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern)
180     }
181 
182     @Test
testOnViewAttachednull183     fun testOnViewAttached() {
184         reset(mKeyguardMessageAreaController)
185         reset(mLockPatternUtils)
186         mKeyguardPatternViewController.onViewAttached()
187         verify(mKeyguardMessageAreaController)
188             .setMessage(context.resources.getString(R.string.keyguard_enter_your_pattern), false)
189         verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
190     }
191 
192     @Test
testOnViewAttached_withExistingMessagenull193     fun testOnViewAttached_withExistingMessage() {
194         reset(mKeyguardMessageAreaController)
195         `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.")
196         mKeyguardPatternViewController.onViewAttached()
197         verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean())
198     }
199 }
200