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