1 /* 2 * Copyright (C) 2024 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.systemui.keyguard.ui.viewmodel 18 19 import androidx.test.ext.junit.runners.AndroidJUnit4 20 import androidx.test.filters.SmallTest 21 import com.android.systemui.Flags as AConfigFlags 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository 24 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor 25 import com.android.systemui.coroutines.collectLastValue 26 import com.android.systemui.doze.util.BurnInHelperWrapper 27 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 28 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor 29 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor 30 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory 31 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor 32 import com.android.systemui.keyguard.shared.model.BurnInModel 33 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition 34 import com.android.systemui.kosmos.testDispatcher 35 import com.android.systemui.kosmos.testScope 36 import com.android.systemui.testKosmos 37 import com.android.systemui.util.mockito.any 38 import com.android.systemui.util.mockito.mock 39 import com.android.systemui.util.mockito.whenever 40 import com.google.common.truth.Truth.assertThat 41 import kotlinx.coroutines.flow.MutableStateFlow 42 import kotlinx.coroutines.test.runTest 43 import org.junit.Before 44 import org.junit.Test 45 import org.junit.runner.RunWith 46 import org.mockito.ArgumentMatchers.anyInt 47 import org.mockito.Mock 48 import org.mockito.MockitoAnnotations 49 50 @SmallTest 51 @RunWith(AndroidJUnit4::class) 52 class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { 53 private val kosmos = testKosmos() 54 private val testScope = kosmos.testScope 55 56 @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper 57 @Mock private lateinit var shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel 58 59 @Mock private lateinit var burnInInteractor: BurnInInteractor 60 private val burnInFlow = MutableStateFlow(BurnInModel()) 61 62 private lateinit var bottomAreaInteractor: KeyguardBottomAreaInteractor 63 private lateinit var underTest: KeyguardIndicationAreaViewModel 64 private lateinit var repository: FakeKeyguardRepository 65 66 private val startButtonFlow = 67 MutableStateFlow<KeyguardQuickAffordanceViewModel>( 68 KeyguardQuickAffordanceViewModel( 69 slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId() 70 ) 71 ) 72 private val endButtonFlow = 73 MutableStateFlow<KeyguardQuickAffordanceViewModel>( 74 KeyguardQuickAffordanceViewModel( 75 slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId() 76 ) 77 ) 78 private val alphaFlow = MutableStateFlow<Float>(1f) 79 80 @Before setUpnull81 fun setUp() { 82 MockitoAnnotations.initMocks(this) 83 84 mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) 85 mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) 86 87 whenever(burnInHelperWrapper.burnInOffset(anyInt(), any())) 88 .thenReturn(RETURNED_BURN_IN_OFFSET) 89 whenever(burnInInteractor.burnIn(anyInt(), anyInt())).thenReturn(burnInFlow) 90 91 val withDeps = KeyguardInteractorFactory.create() 92 val keyguardInteractor = withDeps.keyguardInteractor 93 repository = withDeps.repository 94 95 val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock() 96 whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow) 97 whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow) 98 whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow) 99 bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository) 100 underTest = 101 KeyguardIndicationAreaViewModel( 102 keyguardInteractor = keyguardInteractor, 103 bottomAreaInteractor = bottomAreaInteractor, 104 keyguardBottomAreaViewModel = bottomAreaViewModel, 105 burnInHelperWrapper = burnInHelperWrapper, 106 burnInInteractor = burnInInteractor, 107 shortcutsCombinedViewModel = shortcutsCombinedViewModel, 108 configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()), 109 keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, 110 backgroundCoroutineContext = kosmos.testDispatcher, 111 mainDispatcher = kosmos.testDispatcher 112 ) 113 } 114 115 @Test alphanull116 fun alpha() = 117 testScope.runTest { 118 val value = collectLastValue(underTest.alpha) 119 120 assertThat(value()).isEqualTo(1f) 121 alphaFlow.value = 0.1f 122 assertThat(value()).isEqualTo(0.1f) 123 alphaFlow.value = 0.5f 124 assertThat(value()).isEqualTo(0.5f) 125 alphaFlow.value = 0.2f 126 assertThat(value()).isEqualTo(0.2f) 127 alphaFlow.value = 0f 128 assertThat(value()).isEqualTo(0f) 129 } 130 131 @Test isIndicationAreaPaddednull132 fun isIndicationAreaPadded() = 133 testScope.runTest { 134 repository.setKeyguardShowing(true) 135 val value = collectLastValue(underTest.isIndicationAreaPadded) 136 137 assertThat(value()).isFalse() 138 startButtonFlow.value = startButtonFlow.value.copy(isVisible = true) 139 assertThat(value()).isTrue() 140 endButtonFlow.value = endButtonFlow.value.copy(isVisible = true) 141 assertThat(value()).isTrue() 142 startButtonFlow.value = startButtonFlow.value.copy(isVisible = false) 143 assertThat(value()).isTrue() 144 endButtonFlow.value = endButtonFlow.value.copy(isVisible = false) 145 assertThat(value()).isFalse() 146 } 147 148 @Test indicationAreaTranslationXnull149 fun indicationAreaTranslationX() = 150 testScope.runTest { 151 val value = collectLastValue(underTest.indicationAreaTranslationX) 152 153 assertThat(value()).isEqualTo(0f) 154 bottomAreaInteractor.setClockPosition(100, 100) 155 assertThat(value()).isEqualTo(100f) 156 bottomAreaInteractor.setClockPosition(200, 100) 157 assertThat(value()).isEqualTo(200f) 158 bottomAreaInteractor.setClockPosition(200, 200) 159 assertThat(value()).isEqualTo(200f) 160 bottomAreaInteractor.setClockPosition(300, 100) 161 assertThat(value()).isEqualTo(300f) 162 } 163 164 @Test indicationAreaTranslationYnull165 fun indicationAreaTranslationY() = 166 testScope.runTest { 167 val value = 168 collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET)) 169 170 // Negative 0 - apparently there's a difference in floating point arithmetic - FML 171 assertThat(value()).isEqualTo(-0f) 172 val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f) 173 assertThat(value()).isEqualTo(expected1) 174 val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f) 175 assertThat(value()).isEqualTo(expected2) 176 val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f) 177 assertThat(value()).isEqualTo(expected3) 178 val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f) 179 assertThat(value()).isEqualTo(expected4) 180 } 181 setDozeAmountAndCalculateExpectedTranslationYnull182 private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float { 183 repository.setDozeAmount(dozeAmount) 184 return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET) 185 } 186 187 companion object { 188 private const val DEFAULT_BURN_IN_OFFSET = 5 189 private const val RETURNED_BURN_IN_OFFSET = 3 190 } 191 } 192