1 /* <lambda>null2 * 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.shade.domain.startable 18 19 import android.platform.test.annotations.DisableFlags 20 import android.platform.test.annotations.EnableFlags 21 import android.platform.test.flag.junit.FlagsParameterization 22 import androidx.test.filters.SmallTest 23 import com.android.compose.animation.scene.ObservableTransitionState 24 import com.android.compose.animation.scene.SceneKey 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository 27 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel 28 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository 29 import com.android.systemui.coroutines.collectLastValue 30 import com.android.systemui.display.data.repository.displayStateRepository 31 import com.android.systemui.flags.EnableSceneContainer 32 import com.android.systemui.flags.parameterizeSceneContainerFlag 33 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository 34 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus 35 import com.android.systemui.kosmos.testScope 36 import com.android.systemui.res.R 37 import com.android.systemui.scene.domain.interactor.sceneInteractor 38 import com.android.systemui.scene.shared.model.Scenes 39 import com.android.systemui.scene.shared.model.fakeSceneDataSource 40 import com.android.systemui.shade.ShadeExpansionChangeEvent 41 import com.android.systemui.shade.ShadeExpansionListener 42 import com.android.systemui.shade.domain.interactor.shadeInteractor 43 import com.android.systemui.shade.shared.flag.DualShade 44 import com.android.systemui.shade.shared.model.ShadeMode 45 import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController 46 import com.android.systemui.statusbar.phone.scrimController 47 import com.android.systemui.testKosmos 48 import com.android.systemui.util.mockito.any 49 import com.android.systemui.util.mockito.mock 50 import com.android.systemui.util.mockito.whenever 51 import com.google.common.truth.Truth.assertThat 52 import kotlinx.coroutines.ExperimentalCoroutinesApi 53 import kotlinx.coroutines.flow.MutableStateFlow 54 import kotlinx.coroutines.flow.flowOf 55 import kotlinx.coroutines.test.TestScope 56 import kotlinx.coroutines.test.runCurrent 57 import kotlinx.coroutines.test.runTest 58 import org.junit.Test 59 import org.junit.runner.RunWith 60 import org.mockito.kotlin.verify 61 import platform.test.runner.parameterized.ParameterizedAndroidJunit4 62 import platform.test.runner.parameterized.Parameters 63 64 @OptIn(ExperimentalCoroutinesApi::class) 65 @SmallTest 66 @RunWith(ParameterizedAndroidJunit4::class) 67 class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { 68 private val kosmos = testKosmos() 69 private val testScope = kosmos.testScope 70 private val shadeInteractor by lazy { kosmos.shadeInteractor } 71 private val sceneInteractor by lazy { kosmos.sceneInteractor } 72 private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager } 73 private val fakeConfigurationRepository by lazy { kosmos.fakeConfigurationRepository } 74 private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource } 75 76 private val underTest: ShadeStartable = kosmos.shadeStartable 77 78 companion object { 79 @JvmStatic 80 @Parameters(name = "{0}") 81 fun getParams(): List<FlagsParameterization> { 82 return parameterizeSceneContainerFlag() 83 } 84 } 85 86 init { 87 mSetFlagsRule.setFlagsParameterization(flags) 88 } 89 90 @Test 91 @DisableFlags(DualShade.FLAG_NAME) 92 fun hydrateShadeMode_dualShadeDisabled() = 93 testScope.runTest { 94 overrideResource(R.bool.config_use_split_notification_shade, false) 95 val shadeMode by collectLastValue(shadeInteractor.shadeMode) 96 97 underTest.start() 98 assertThat(shadeMode).isEqualTo(ShadeMode.Single) 99 100 overrideResource(R.bool.config_use_split_notification_shade, true) 101 fakeConfigurationRepository.onAnyConfigurationChange() 102 assertThat(shadeMode).isEqualTo(ShadeMode.Split) 103 104 overrideResource(R.bool.config_use_split_notification_shade, false) 105 fakeConfigurationRepository.onAnyConfigurationChange() 106 assertThat(shadeMode).isEqualTo(ShadeMode.Single) 107 } 108 109 @Test 110 @EnableFlags(DualShade.FLAG_NAME) 111 fun hydrateShadeMode_dualShadeEnabled() = 112 testScope.runTest { 113 overrideResource(R.bool.config_use_split_notification_shade, false) 114 val shadeMode by collectLastValue(shadeInteractor.shadeMode) 115 116 underTest.start() 117 assertThat(shadeMode).isEqualTo(ShadeMode.Dual) 118 119 overrideResource(R.bool.config_use_split_notification_shade, true) 120 fakeConfigurationRepository.onAnyConfigurationChange() 121 assertThat(shadeMode).isEqualTo(ShadeMode.Dual) 122 123 overrideResource(R.bool.config_use_split_notification_shade, false) 124 fakeConfigurationRepository.onAnyConfigurationChange() 125 assertThat(shadeMode).isEqualTo(ShadeMode.Dual) 126 } 127 128 @Test 129 @EnableSceneContainer 130 fun hydrateShadeExpansionStateManager() = 131 testScope.runTest { 132 val expansionListener = mock<ShadeExpansionListener>() 133 var latestChangeEvent: ShadeExpansionChangeEvent? = null 134 whenever(expansionListener.onPanelExpansionChanged(any())).thenAnswer { 135 latestChangeEvent = it.arguments[0] as ShadeExpansionChangeEvent 136 Unit 137 } 138 shadeExpansionStateManager.addExpansionListener(expansionListener) 139 140 underTest.start() 141 142 kosmos.fakeAuthenticationRepository.setAuthenticationMethod( 143 AuthenticationMethodModel.Pin 144 ) 145 runCurrent() 146 kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( 147 SuccessFingerprintAuthenticationStatus(0, true) 148 ) 149 runCurrent() 150 val transitionState = 151 MutableStateFlow<ObservableTransitionState>( 152 ObservableTransitionState.Idle(Scenes.Gone) 153 ) 154 sceneInteractor.setTransitionState(transitionState) 155 156 changeScene(Scenes.Gone, transitionState) 157 val currentScene by collectLastValue(sceneInteractor.currentScene) 158 assertThat(currentScene).isEqualTo(Scenes.Gone) 159 160 assertThat(latestChangeEvent) 161 .isEqualTo( 162 ShadeExpansionChangeEvent( 163 fraction = 0f, 164 expanded = false, 165 tracking = false, 166 ) 167 ) 168 169 changeScene(Scenes.Shade, transitionState) { progress -> 170 assertThat(latestChangeEvent?.fraction).isEqualTo(progress) 171 } 172 } 173 174 @Test 175 @EnableSceneContainer 176 fun hydrateFullWidth() = 177 testScope.runTest { 178 underTest.start() 179 180 kosmos.displayStateRepository.setIsLargeScreen(true) 181 runCurrent() 182 verify(kosmos.notificationStackScrollLayoutController).setIsFullWidth(false) 183 assertThat(kosmos.scrimController.clipQsScrim).isFalse() 184 } 185 186 private fun TestScope.changeScene( 187 toScene: SceneKey, 188 transitionState: MutableStateFlow<ObservableTransitionState>, 189 assertDuringProgress: ((progress: Float) -> Unit) = {}, 190 ) { 191 val currentScene by collectLastValue(sceneInteractor.currentScene) 192 val progressFlow = MutableStateFlow(0f) 193 transitionState.value = 194 ObservableTransitionState.Transition( 195 fromScene = checkNotNull(currentScene), 196 toScene = toScene, 197 currentScene = flowOf(checkNotNull(currentScene)), 198 progress = progressFlow, 199 isInitiatedByUserInput = true, 200 isUserInputOngoing = flowOf(true), 201 ) 202 runCurrent() 203 assertDuringProgress(progressFlow.value) 204 205 progressFlow.value = 0.2f 206 runCurrent() 207 assertDuringProgress(progressFlow.value) 208 209 progressFlow.value = 0.6f 210 runCurrent() 211 assertDuringProgress(progressFlow.value) 212 213 progressFlow.value = 1f 214 runCurrent() 215 assertDuringProgress(progressFlow.value) 216 217 transitionState.value = ObservableTransitionState.Idle(toScene) 218 fakeSceneDataSource.changeScene(toScene) 219 runCurrent() 220 assertDuringProgress(progressFlow.value) 221 222 assertThat(currentScene).isEqualTo(toScene) 223 } 224 } 225