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.quickaffordance 19 20 import android.graphics.drawable.Drawable 21 import android.service.quickaccesswallet.GetWalletCardsResponse 22 import android.service.quickaccesswallet.QuickAccessWalletClient 23 import android.service.quickaccesswallet.WalletCard 24 import androidx.test.ext.junit.runners.AndroidJUnit4 25 import androidx.test.filters.SmallTest 26 import com.android.systemui.R 27 import com.android.systemui.RoboPilotTest 28 import com.android.systemui.SysuiTestCase 29 import com.android.systemui.animation.ActivityLaunchAnimator 30 import com.android.systemui.animation.Expandable 31 import com.android.systemui.common.shared.model.ContentDescription 32 import com.android.systemui.common.shared.model.Icon 33 import com.android.systemui.plugins.ActivityStarter 34 import com.android.systemui.util.mockito.any 35 import com.android.systemui.util.mockito.mock 36 import com.android.systemui.util.mockito.whenever 37 import com.android.systemui.wallet.controller.QuickAccessWalletController 38 import com.google.common.truth.Truth.assertThat 39 import kotlinx.coroutines.ExperimentalCoroutinesApi 40 import kotlinx.coroutines.flow.launchIn 41 import kotlinx.coroutines.flow.onEach 42 import kotlinx.coroutines.test.UnconfinedTestDispatcher 43 import kotlinx.coroutines.test.runBlockingTest 44 import kotlinx.coroutines.test.runTest 45 import org.junit.Before 46 import org.junit.Test 47 import org.junit.runner.RunWith 48 import org.mockito.Mock 49 import org.mockito.Mockito.verify 50 import org.mockito.MockitoAnnotations 51 52 @OptIn(ExperimentalCoroutinesApi::class) 53 @SmallTest 54 @RoboPilotTest 55 @RunWith(AndroidJUnit4::class) 56 class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { 57 58 @Mock private lateinit var walletController: QuickAccessWalletController 59 @Mock private lateinit var activityStarter: ActivityStarter 60 61 private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig 62 63 @Before 64 fun setUp() { 65 MockitoAnnotations.initMocks(this) 66 67 underTest = 68 QuickAccessWalletKeyguardQuickAffordanceConfig( 69 context, 70 walletController, 71 activityStarter, 72 ) 73 } 74 75 @Test 76 fun affordance_keyguardShowing_hasWalletCard_visibleModel() = runBlockingTest { 77 setUpState() 78 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 79 80 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 81 82 val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible 83 assertThat(visibleModel.icon) 84 .isEqualTo( 85 Icon.Loaded( 86 drawable = ICON, 87 contentDescription = 88 ContentDescription.Resource( 89 res = R.string.accessibility_wallet_button, 90 ), 91 ) 92 ) 93 job.cancel() 94 } 95 96 @Test 97 fun affordance_keyguardShowing_hasNonPaymentCard_modelIsNone() = 98 runTest(UnconfinedTestDispatcher()) { 99 setUpState(cardType = WalletCard.CARD_TYPE_NON_PAYMENT) 100 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 101 102 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 103 104 assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) 105 job.cancel() 106 } 107 108 @Test 109 fun affordance_keyguardShowing_hasPaymentCard_visibleModel() = 110 runTest(UnconfinedTestDispatcher()) { 111 setUpState(cardType = WalletCard.CARD_TYPE_PAYMENT) 112 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 113 114 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 115 116 val visibleModel = latest as KeyguardQuickAffordanceConfig.LockScreenState.Visible 117 assertThat(visibleModel.icon) 118 .isEqualTo( 119 Icon.Loaded( 120 drawable = ICON, 121 contentDescription = 122 ContentDescription.Resource( 123 res = R.string.accessibility_wallet_button, 124 ), 125 ) 126 ) 127 job.cancel() 128 } 129 130 @Test 131 fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest { 132 setUpState(isWalletFeatureAvailable = false) 133 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 134 135 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 136 137 assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) 138 139 job.cancel() 140 } 141 142 @Test 143 fun affordance_queryNotSuccessful_modelIsNone() = runBlockingTest { 144 setUpState(isWalletQuerySuccessful = false) 145 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 146 147 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 148 149 assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) 150 151 job.cancel() 152 } 153 154 @Test 155 fun affordance_noSelectedCard_modelIsNone() = runBlockingTest { 156 setUpState(hasSelectedCard = false) 157 var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null 158 159 val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) 160 161 assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.LockScreenState.Hidden) 162 163 job.cancel() 164 } 165 166 @Test 167 fun onQuickAffordanceTriggered() { 168 val animationController: ActivityLaunchAnimator.Controller = mock() 169 val expandable: Expandable = mock { 170 whenever(this.activityLaunchController()).thenReturn(animationController) 171 } 172 173 assertThat(underTest.onTriggered(expandable)) 174 .isEqualTo(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled) 175 verify(walletController) 176 .startQuickAccessUiIntent( 177 activityStarter, 178 animationController, 179 /* hasCard= */ true, 180 ) 181 } 182 183 @Test 184 fun getPickerScreenState_default() = runTest { 185 setUpState() 186 187 assertThat(underTest.getPickerScreenState()) 188 .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) 189 } 190 191 @Test 192 fun getPickerScreenState_unavailable() = runTest { 193 setUpState( 194 isWalletServiceAvailable = false, 195 ) 196 197 assertThat(underTest.getPickerScreenState()) 198 .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) 199 } 200 201 @Test 202 fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest { 203 setUpState( 204 isWalletFeatureAvailable = false, 205 ) 206 207 assertThat(underTest.getPickerScreenState()) 208 .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) 209 } 210 211 @Test 212 fun getPickerScreenState_disabledWhenThereIsNoCard() = runTest { 213 setUpState( 214 hasSelectedCard = false, 215 ) 216 217 assertThat(underTest.getPickerScreenState()) 218 .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) 219 } 220 221 private fun setUpState( 222 isWalletFeatureAvailable: Boolean = true, 223 isWalletServiceAvailable: Boolean = true, 224 isWalletQuerySuccessful: Boolean = true, 225 hasSelectedCard: Boolean = true, 226 cardType: Int = WalletCard.CARD_TYPE_UNKNOWN 227 ) { 228 val walletClient: QuickAccessWalletClient = mock() 229 whenever(walletClient.tileIcon).thenReturn(ICON) 230 whenever(walletClient.isWalletServiceAvailable).thenReturn(isWalletServiceAvailable) 231 whenever(walletClient.isWalletFeatureAvailable).thenReturn(isWalletFeatureAvailable) 232 233 whenever(walletController.walletClient).thenReturn(walletClient) 234 235 whenever(walletController.queryWalletCards(any())).thenAnswer { invocation -> 236 with( 237 invocation.arguments[0] as QuickAccessWalletClient.OnWalletCardsRetrievedCallback 238 ) { 239 if (isWalletQuerySuccessful) { 240 onWalletCardsRetrieved( 241 if (hasSelectedCard) { 242 GetWalletCardsResponse( 243 listOf( 244 WalletCard.Builder( 245 /*cardId= */ CARD_ID, 246 /*cardType= */ cardType, 247 /*cardImage= */ mock(), 248 /*contentDescription= */ CARD_DESCRIPTION, 249 /*pendingIntent= */ mock() 250 ) 251 .build() 252 ), 253 0 254 ) 255 } else { 256 GetWalletCardsResponse(emptyList(), 0) 257 } 258 ) 259 } else { 260 onWalletCardRetrievalError(mock()) 261 } 262 } 263 } 264 } 265 266 companion object { 267 private val ICON: Drawable = mock() 268 private const val CARD_ID: String = "Id" 269 private const val CARD_DESCRIPTION: String = "Description" 270 } 271 } 272