1 /* 2 * Copyright (C) 2022 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.data.repository 19 20 import android.content.pm.UserInfo 21 import android.os.UserHandle 22 import androidx.test.ext.junit.runners.AndroidJUnit4 23 import androidx.test.filters.SmallTest 24 import com.android.systemui.R 25 import com.android.systemui.RoboPilotTest 26 import com.android.systemui.SysuiTestCase 27 import com.android.systemui.coroutines.collectLastValue 28 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig 29 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory 30 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig 31 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer 32 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager 33 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager 34 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation 35 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation 36 import com.android.systemui.settings.FakeUserTracker 37 import com.android.systemui.settings.UserFileManager 38 import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient 39 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots 40 import com.android.systemui.util.FakeSharedPreferences 41 import com.android.systemui.util.mockito.mock 42 import com.android.systemui.util.mockito.whenever 43 import com.android.systemui.util.settings.FakeSettings 44 import com.google.common.truth.Truth.assertThat 45 import java.util.Locale 46 import kotlinx.coroutines.ExperimentalCoroutinesApi 47 import kotlinx.coroutines.test.StandardTestDispatcher 48 import kotlinx.coroutines.test.TestScope 49 import kotlinx.coroutines.test.runTest 50 import org.junit.After 51 import org.junit.Before 52 import org.junit.Test 53 import org.junit.runner.RunWith 54 import org.mockito.ArgumentMatchers.anyInt 55 import org.mockito.ArgumentMatchers.anyString 56 57 @OptIn(ExperimentalCoroutinesApi::class) 58 @SmallTest 59 @RoboPilotTest 60 @RunWith(AndroidJUnit4::class) 61 class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { 62 63 private lateinit var underTest: KeyguardQuickAffordanceRepository 64 65 private lateinit var config1: FakeKeyguardQuickAffordanceConfig 66 private lateinit var config2: FakeKeyguardQuickAffordanceConfig 67 private lateinit var userTracker: FakeUserTracker 68 private lateinit var client1: FakeCustomizationProviderClient 69 private lateinit var client2: FakeCustomizationProviderClient 70 private lateinit var testScope: TestScope 71 72 @Before 73 fun setUp() { 74 overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true) 75 context.resources.configuration.setLayoutDirection(Locale.US) 76 config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1) 77 config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2) 78 val testDispatcher = StandardTestDispatcher() 79 testScope = TestScope(testDispatcher) 80 userTracker = FakeUserTracker() 81 val localUserSelectionManager = 82 KeyguardQuickAffordanceLocalUserSelectionManager( 83 context = context, 84 userFileManager = 85 mock<UserFileManager>().apply { 86 whenever( 87 getSharedPreferences( 88 anyString(), 89 anyInt(), 90 anyInt(), 91 ) 92 ) 93 .thenReturn(FakeSharedPreferences()) 94 }, 95 userTracker = userTracker, 96 broadcastDispatcher = fakeBroadcastDispatcher, 97 ) 98 client1 = FakeCustomizationProviderClient() 99 client2 = FakeCustomizationProviderClient() 100 val remoteUserSelectionManager = 101 KeyguardQuickAffordanceRemoteUserSelectionManager( 102 scope = testScope.backgroundScope, 103 userTracker = userTracker, 104 clientFactory = 105 FakeKeyguardQuickAffordanceProviderClientFactory( 106 userTracker, 107 ) { selectedUserId -> 108 when (selectedUserId) { 109 SECONDARY_USER_1 -> client1 110 SECONDARY_USER_2 -> client2 111 else -> error("No set-up client for user $selectedUserId!") 112 } 113 }, 114 userHandle = UserHandle.SYSTEM, 115 ) 116 117 overrideResource( 118 R.array.config_keyguardQuickAffordanceDefaults, 119 arrayOf<String>(), 120 ) 121 122 underTest = 123 KeyguardQuickAffordanceRepository( 124 appContext = context, 125 scope = testScope.backgroundScope, 126 localUserSelectionManager = localUserSelectionManager, 127 remoteUserSelectionManager = remoteUserSelectionManager, 128 userTracker = userTracker, 129 legacySettingSyncer = 130 KeyguardQuickAffordanceLegacySettingSyncer( 131 scope = testScope.backgroundScope, 132 backgroundDispatcher = testDispatcher, 133 secureSettings = FakeSettings(), 134 selectionsManager = localUserSelectionManager, 135 ), 136 configs = setOf(config1, config2), 137 dumpManager = mock(), 138 userHandle = UserHandle.SYSTEM, 139 ) 140 } 141 142 @After 143 fun tearDown() { 144 mContext 145 .getOrCreateTestableResources() 146 .removeOverride(R.bool.custom_lockscreen_shortcuts_enabled) 147 } 148 149 @Test 150 fun setSelections() = 151 testScope.runTest { 152 val configsBySlotId = collectLastValue(underTest.selections) 153 val slotId1 = "slot1" 154 val slotId2 = "slot2" 155 156 underTest.setSelections(slotId1, listOf(config1.key)) 157 assertSelections( 158 configsBySlotId(), 159 mapOf( 160 slotId1 to listOf(config1), 161 ), 162 ) 163 164 underTest.setSelections(slotId2, listOf(config2.key)) 165 assertSelections( 166 configsBySlotId(), 167 mapOf( 168 slotId1 to listOf(config1), 169 slotId2 to listOf(config2), 170 ), 171 ) 172 173 underTest.setSelections(slotId1, emptyList()) 174 underTest.setSelections(slotId2, listOf(config1.key)) 175 assertSelections( 176 configsBySlotId(), 177 mapOf( 178 slotId1 to emptyList(), 179 slotId2 to listOf(config1), 180 ), 181 ) 182 } 183 184 @Test 185 fun getAffordancePickerRepresentations() = 186 testScope.runTest { 187 assertThat(underTest.getAffordancePickerRepresentations()) 188 .isEqualTo( 189 listOf( 190 KeyguardQuickAffordancePickerRepresentation( 191 id = config1.key, 192 name = config1.pickerName(), 193 iconResourceId = config1.pickerIconResourceId, 194 ), 195 KeyguardQuickAffordancePickerRepresentation( 196 id = config2.key, 197 name = config2.pickerName(), 198 iconResourceId = config2.pickerIconResourceId, 199 ), 200 ) 201 ) 202 } 203 204 @Test 205 fun getSlotPickerRepresentations() { 206 val slot1 = "slot1" 207 val slot2 = "slot2" 208 val slot3 = "slot3" 209 context.orCreateTestableResources.addOverride( 210 R.array.config_keyguardQuickAffordanceSlots, 211 arrayOf( 212 "$slot1:2", 213 "$slot2:4", 214 "$slot3:5", 215 ), 216 ) 217 218 assertThat(underTest.getSlotPickerRepresentations()) 219 .isEqualTo( 220 listOf( 221 KeyguardSlotPickerRepresentation( 222 id = slot1, 223 maxSelectedAffordances = 2, 224 ), 225 KeyguardSlotPickerRepresentation( 226 id = slot2, 227 maxSelectedAffordances = 4, 228 ), 229 KeyguardSlotPickerRepresentation( 230 id = slot3, 231 maxSelectedAffordances = 5, 232 ), 233 ) 234 ) 235 } 236 237 @Test 238 fun getSlotPickerRepresentations_rightToLeft_slotsReversed() { 239 context.resources.configuration.setLayoutDirection(Locale("he", "IL")) 240 val slot1 = "slot1" 241 val slot2 = "slot2" 242 val slot3 = "slot3" 243 context.orCreateTestableResources.addOverride( 244 R.array.config_keyguardQuickAffordanceSlots, 245 arrayOf( 246 "$slot1:2", 247 "$slot2:4", 248 "$slot3:5", 249 ), 250 ) 251 252 assertThat(underTest.getSlotPickerRepresentations()) 253 .isEqualTo( 254 listOf( 255 KeyguardSlotPickerRepresentation( 256 id = slot3, 257 maxSelectedAffordances = 5, 258 ), 259 KeyguardSlotPickerRepresentation( 260 id = slot2, 261 maxSelectedAffordances = 4, 262 ), 263 KeyguardSlotPickerRepresentation( 264 id = slot1, 265 maxSelectedAffordances = 2, 266 ), 267 ) 268 ) 269 } 270 271 @Test 272 fun selectionsForSecondaryUser() = 273 testScope.runTest { 274 userTracker.set( 275 userInfos = 276 listOf( 277 UserInfo( 278 UserHandle.USER_SYSTEM, 279 "Primary", 280 /* flags= */ 0, 281 ), 282 UserInfo( 283 SECONDARY_USER_1, 284 "Secondary 1", 285 /* flags= */ 0, 286 ), 287 UserInfo( 288 SECONDARY_USER_2, 289 "Secondary 2", 290 /* flags= */ 0, 291 ), 292 ), 293 selectedUserIndex = 2, 294 ) 295 client2.insertSelection( 296 slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, 297 affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2, 298 ) 299 val observed = collectLastValue(underTest.selections) 300 301 assertSelections( 302 observed = observed(), 303 expected = 304 mapOf( 305 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to 306 listOf( 307 config2, 308 ), 309 ) 310 ) 311 } 312 313 private fun assertSelections( 314 observed: Map<String, List<KeyguardQuickAffordanceConfig>>?, 315 expected: Map<String, List<KeyguardQuickAffordanceConfig>>, 316 ) { 317 assertThat(observed).isEqualTo(expected) 318 assertThat(underTest.getCurrentSelections()) 319 .isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } }) 320 expected.forEach { (slotId, configs) -> 321 assertThat(underTest.getCurrentSelections(slotId)).isEqualTo(configs) 322 } 323 } 324 325 companion object { 326 private const val SECONDARY_USER_1 = UserHandle.MIN_SECONDARY_USER_ID + 1 327 private const val SECONDARY_USER_2 = UserHandle.MIN_SECONDARY_USER_ID + 2 328 } 329 } 330