1 /* <lambda>null2 * Copyright (C) 2023 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.ui.view.layout.sections 19 20 import android.content.pm.PackageManager 21 import android.content.res.Resources 22 import android.view.View.GONE 23 import android.view.View.VISIBLE 24 import androidx.constraintlayout.widget.ConstraintSet 25 import androidx.test.ext.junit.runners.AndroidJUnit4 26 import androidx.test.filters.SmallTest 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.coroutines.collectLastValue 29 import com.android.systemui.customization.R as customR 30 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository 31 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor 32 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor 33 import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor 34 import com.android.systemui.keyguard.shared.model.ClockSize 35 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel 36 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel 37 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel 38 import com.android.systemui.kosmos.Kosmos 39 import com.android.systemui.kosmos.testScope 40 import com.android.systemui.res.R 41 import com.android.systemui.shade.data.repository.shadeRepository 42 import com.android.systemui.shade.shared.model.ShadeMode 43 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor 44 import com.android.systemui.statusbar.policy.fakeConfigurationController 45 import com.android.systemui.statusbar.ui.fakeSystemBarUtilsProxy 46 import com.android.systemui.testKosmos 47 import com.android.systemui.util.mockito.eq 48 import com.android.systemui.util.mockito.mock 49 import com.android.systemui.util.mockito.whenever 50 import com.google.common.truth.Truth.assertThat 51 import kotlinx.coroutines.test.advanceUntilIdle 52 import kotlinx.coroutines.test.runTest 53 import org.junit.Before 54 import org.junit.Test 55 import org.junit.runner.RunWith 56 import org.mockito.ArgumentMatchers.anyInt 57 import org.mockito.ArgumentMatchers.anyString 58 import org.mockito.MockitoAnnotations 59 60 @RunWith(AndroidJUnit4::class) 61 @SmallTest 62 class ClockSectionTest : SysuiTestCase() { 63 private lateinit var underTest: ClockSection 64 65 private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE: Int 66 get() = 67 kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() + 68 context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + 69 context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) 70 71 private val LARGE_CLOCK_TOP 72 get() = 73 LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE + 74 SMART_SPACE_DATE_WEATHER_HEIGHT + 75 ENHANCED_SMART_SPACE_HEIGHT 76 77 private val CLOCK_FADE_TRANSLATION_Y 78 get() = context.resources.getDimensionPixelSize(customR.dimen.small_clock_height) 79 80 private var DIMENSION_BY_IDENTIFIER: List<Pair<String, Int>> = listOf() 81 private lateinit var kosmos: Kosmos 82 83 @Before 84 fun setup() { 85 DIMENSION_BY_IDENTIFIER = 86 listOf( 87 "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT, 88 "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT, 89 ) 90 91 MockitoAnnotations.initMocks(this) 92 val remoteResources = 93 mock<Resources>().apply { 94 whenever(getIdentifier(anyString(), eq("dimen"), anyString())).then { invocation -> 95 val name = invocation.arguments[0] as String 96 val index = DIMENSION_BY_IDENTIFIER.indexOfFirst { (key, _) -> key == name } 97 // increment index so that the not-found sentinel value lines up w/ what is 98 // returned by getIdentifier when a resource is not found 99 index + 1 100 } 101 whenever(getDimensionPixelSize(anyInt())).then { invocation -> 102 val id = invocation.arguments[0] as Int 103 DIMENSION_BY_IDENTIFIER[id - 1].second 104 } 105 } 106 mContext.setMockPackageManager( 107 mock<PackageManager>().apply { 108 whenever(getResourcesForApplication(anyString())).thenReturn(remoteResources) 109 } 110 ) 111 112 kosmos = testKosmos() 113 with(kosmos) { 114 underTest = 115 ClockSection( 116 keyguardClockInteractor, 117 keyguardClockViewModel, 118 context, 119 keyguardSmartspaceViewModel, 120 { keyguardBlueprintInteractor }, 121 keyguardRootViewModel, 122 ) 123 } 124 } 125 126 @Test 127 fun testApplyDefaultConstraints_LargeClock_SplitShade() = 128 kosmos.testScope.runTest { 129 with(kosmos) { 130 shadeRepository.setShadeMode(ShadeMode.Split) 131 keyguardClockInteractor.setClockSize(ClockSize.LARGE) 132 advanceUntilIdle() 133 } 134 135 val cs = ConstraintSet() 136 underTest.applyDefaultConstraints(cs) 137 138 assertLargeClockTop(cs, LARGE_CLOCK_TOP) 139 assertSmallClockTop(cs) 140 } 141 142 @Test 143 fun testApplyDefaultConstraints_LargeClock_NonSplitShade() = 144 kosmos.testScope.runTest { 145 with(kosmos) { 146 val collectedShadeMode by collectLastValue(shadeRepository.shadeMode) 147 val isLargeClockVisible by 148 collectLastValue(keyguardClockViewModel.isLargeClockVisible) 149 150 shadeRepository.setShadeMode(ShadeMode.Single) 151 keyguardClockInteractor.setClockSize(ClockSize.LARGE) 152 fakeKeyguardRepository.setClockShouldBeCentered(true) 153 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 154 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 155 fakeConfigurationController.notifyConfigurationChanged() 156 advanceUntilIdle() 157 158 val cs = ConstraintSet() 159 underTest.applyDefaultConstraints(cs) 160 161 assertLargeClockTop(cs, LARGE_CLOCK_TOP) 162 assertSmallClockTop(cs) 163 } 164 } 165 166 @Test 167 fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade() = 168 kosmos.testScope.runTest { 169 with(kosmos) { 170 DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock 171 val collectedShadeMode by collectLastValue(shadeRepository.shadeMode) 172 val isLargeClockVisible by 173 collectLastValue(keyguardClockViewModel.isLargeClockVisible) 174 175 shadeRepository.setShadeMode(ShadeMode.Split) 176 keyguardClockInteractor.setClockSize(ClockSize.LARGE) 177 fakeKeyguardRepository.setClockShouldBeCentered(true) 178 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 179 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 180 fakeConfigurationController.notifyConfigurationChanged() 181 advanceUntilIdle() 182 183 val cs = ConstraintSet() 184 underTest.applyDefaultConstraints(cs) 185 186 assertLargeClockTop(cs, LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE) 187 assertSmallClockTop(cs) 188 } 189 } 190 191 @Test 192 fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade() = 193 kosmos.testScope.runTest { 194 with(kosmos) { 195 DIMENSION_BY_IDENTIFIER = listOf() // Remove Smartspace from mock 196 val collectedShadeMode by collectLastValue(shadeRepository.shadeMode) 197 val isLargeClockVisible by 198 collectLastValue(keyguardClockViewModel.isLargeClockVisible) 199 200 shadeRepository.setShadeMode(ShadeMode.Single) 201 keyguardClockInteractor.setClockSize(ClockSize.LARGE) 202 fakeKeyguardRepository.setClockShouldBeCentered(true) 203 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 204 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 205 fakeConfigurationController.notifyConfigurationChanged() 206 advanceUntilIdle() 207 208 val cs = ConstraintSet() 209 underTest.applyDefaultConstraints(cs) 210 211 assertLargeClockTop(cs, LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE) 212 assertSmallClockTop(cs) 213 } 214 } 215 216 @Test 217 fun testApplyDefaultConstraints_SmallClock_SplitShade() = 218 kosmos.testScope.runTest { 219 with(kosmos) { 220 val collectedShadeMode by collectLastValue(shadeRepository.shadeMode) 221 val isLargeClockVisible by 222 collectLastValue(keyguardClockViewModel.isLargeClockVisible) 223 224 shadeRepository.setShadeMode(ShadeMode.Split) 225 keyguardClockInteractor.setClockSize(ClockSize.SMALL) 226 fakeKeyguardRepository.setClockShouldBeCentered(true) 227 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 228 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 229 fakeConfigurationController.notifyConfigurationChanged() 230 advanceUntilIdle() 231 232 val cs = ConstraintSet() 233 underTest.applyDefaultConstraints(cs) 234 235 assertLargeClockTop(cs, LARGE_CLOCK_TOP) 236 assertSmallClockTop(cs) 237 } 238 } 239 240 @Test 241 fun testApplyDefaultConstraints_SmallClock_NonSplitShade() = 242 kosmos.testScope.runTest { 243 with(kosmos) { 244 val collectedShadeMode by collectLastValue(shadeRepository.shadeMode) 245 val isLargeClockVisible by 246 collectLastValue(keyguardClockViewModel.isLargeClockVisible) 247 248 shadeRepository.setShadeMode(ShadeMode.Single) 249 keyguardClockInteractor.setClockSize(ClockSize.SMALL) 250 fakeKeyguardRepository.setClockShouldBeCentered(true) 251 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 252 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 253 fakeConfigurationController.notifyConfigurationChanged() 254 advanceUntilIdle() 255 256 val cs = ConstraintSet() 257 underTest.applyDefaultConstraints(cs) 258 assertLargeClockTop(cs, LARGE_CLOCK_TOP) 259 assertSmallClockTop(cs) 260 } 261 } 262 263 @Test 264 fun testSmartspaceVisible_weatherClockDateAndIconsBarrierBottomBelowBCSmartspace() = 265 kosmos.testScope.runTest { 266 with(kosmos) { 267 notificationsKeyguardInteractor.setNotificationsFullyHidden(false) 268 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE) 269 fakeConfigurationController.notifyConfigurationChanged() 270 advanceUntilIdle() 271 } 272 273 val cs = ConstraintSet() 274 underTest.applyDefaultConstraints(cs) 275 val referencedIds = 276 cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom) 277 referencedIds.contentEquals( 278 intArrayOf(com.android.systemui.shared.R.id.bc_smartspace_view) 279 ) 280 } 281 282 @Test 283 fun testSmartspaceGone_weatherClockDateAndIconsBarrierBottomBelowSmartspaceDateWeather() = 284 kosmos.testScope.runTest { 285 with(kosmos) { 286 notificationsKeyguardInteractor.setNotificationsFullyHidden(false) 287 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(GONE) 288 fakeConfigurationController.notifyConfigurationChanged() 289 advanceUntilIdle() 290 } 291 292 val cs = ConstraintSet() 293 underTest.applyDefaultConstraints(cs) 294 val referencedIds = 295 cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom) 296 referencedIds.contentEquals(intArrayOf(R.id.lockscreen_clock_view)) 297 } 298 299 @Test 300 fun testHasAodIcons_weatherClockDateAndIconsBarrierBottomBelowSmartspaceDateWeather() = 301 kosmos.testScope.runTest { 302 with(kosmos) { 303 notificationsKeyguardInteractor.setNotificationsFullyHidden(true) 304 fakeConfigurationController.notifyConfigurationChanged() 305 advanceUntilIdle() 306 } 307 308 val cs = ConstraintSet() 309 underTest.applyDefaultConstraints(cs) 310 val referencedIds = 311 cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom) 312 referencedIds.contentEquals( 313 intArrayOf( 314 com.android.systemui.shared.R.id.bc_smartspace_view, 315 R.id.aod_notification_icon_container 316 ) 317 ) 318 } 319 320 private fun assertLargeClockTop(cs: ConstraintSet, expectedLargeClockTopMargin: Int) { 321 val largeClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view_large) 322 assertThat(largeClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) 323 assertThat(largeClockConstraint.layout.topMargin).isEqualTo(expectedLargeClockTopMargin) 324 } 325 326 private fun assertSmallClockTop(cs: ConstraintSet) { 327 val smallClockGuidelineConstraint = cs.getConstraint(R.id.small_clock_guideline_top) 328 assertThat(smallClockGuidelineConstraint.layout.topToTop).isEqualTo(-1) 329 330 val smallClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view) 331 assertThat(smallClockConstraint.layout.topToBottom) 332 .isEqualTo(R.id.small_clock_guideline_top) 333 assertThat(smallClockConstraint.layout.topMargin).isEqualTo(0) 334 } 335 336 companion object { 337 private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10 338 private val ENHANCED_SMART_SPACE_HEIGHT = 11 339 } 340 } 341