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