1 /* <lambda>null2 * 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 package com.android.systemui.shade 18 19 import android.testing.AndroidTestingRunner 20 import android.view.ViewGroup 21 import androidx.constraintlayout.widget.ConstraintSet 22 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID 23 import androidx.constraintlayout.widget.ConstraintSet.START 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.res.R 26 import com.android.systemui.SysuiTestCase 27 import com.google.common.truth.Expect 28 import com.google.common.truth.Truth.assertThat 29 import com.google.common.truth.Truth.assertWithMessage 30 import org.junit.Before 31 import org.junit.Rule 32 import org.junit.Test 33 import org.junit.runner.RunWith 34 35 @SmallTest 36 @RunWith(AndroidTestingRunner::class) 37 class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { 38 39 private lateinit var qqsConstraint: ConstraintSet 40 private lateinit var qsConstraint: ConstraintSet 41 private lateinit var largeScreenConstraint: ConstraintSet 42 43 @get:Rule 44 val expect: Expect = Expect.create() 45 46 @Before 47 fun setUp() { 48 qqsConstraint = ConstraintSet().apply { 49 load(context, context.resources.getXml(R.xml.qqs_header)) 50 } 51 qsConstraint = ConstraintSet().apply { 52 load(context, context.resources.getXml(R.xml.qs_header)) 53 } 54 largeScreenConstraint = ConstraintSet().apply { 55 load(context, context.resources.getXml(R.xml.large_screen_shade_header)) 56 } 57 } 58 59 @Test 60 fun testEdgeElementsAlignedWithGuide_qqs() { 61 with(qqsConstraint) { 62 assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(R.id.begin_guide) 63 assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f) 64 65 assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) 66 .isEqualTo(R.id.end_guide) 67 assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) 68 .isEqualTo(1f) 69 70 assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) 71 .isEqualTo(R.id.end_guide) 72 assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias) 73 .isEqualTo(1f) 74 } 75 } 76 77 @Test 78 fun testClockScale() { 79 with(qqsConstraint.getConstraint(R.id.clock)) { 80 assertThat(transform.scaleX).isEqualTo(1f) 81 assertThat(transform.scaleY).isEqualTo(1f) 82 } 83 with(qsConstraint.getConstraint(R.id.clock)) { 84 assertThat(transform.scaleX).isGreaterThan(1f) 85 assertThat(transform.scaleY).isGreaterThan(1f) 86 } 87 } 88 89 @Test 90 fun testEdgeElementsAlignedWithEdgeOrGuide_qs() { 91 with(qsConstraint) { 92 assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID) 93 assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f) 94 95 assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID) 96 assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f) 97 98 assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) 99 .isEqualTo(PARENT_ID) 100 assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) 101 .isEqualTo(0.5f) 102 103 assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) 104 .isEqualTo(R.id.end_guide) 105 assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(1f) 106 } 107 } 108 109 @Test 110 fun testEdgeElementsAlignedWithEdge_largeScreen() { 111 with(largeScreenConstraint) { 112 assertThat(getConstraint(R.id.clock).layout.startToEnd).isEqualTo(R.id.begin_guide) 113 assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f) 114 115 assertThat(getConstraint(R.id.privacy_container).layout.endToStart) 116 .isEqualTo(R.id.end_guide) 117 assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(0.5f) 118 } 119 } 120 121 @Test 122 fun testCarrierAlpha() { 123 assertThat(qqsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(0f) 124 assertThat(qsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(1f) 125 assertThat(largeScreenConstraint.getConstraint(R.id.carrier_group).propertySet.alpha) 126 .isEqualTo(1f) 127 } 128 129 @Test 130 fun testPrivacyChipVisibilityConstraints_notVisible() { 131 val changes = CombinedShadeHeadersConstraintManagerImpl 132 .privacyChipVisibilityConstraints(false) 133 changes() 134 135 with(qqsConstraint) { 136 assertThat(systemIconsAlphaConstraint).isEqualTo(1f) 137 } 138 139 with(qsConstraint) { 140 assertThat(systemIconsAlphaConstraint).isEqualTo(1f) 141 } 142 143 with(largeScreenConstraint) { 144 assertThat(systemIconsAlphaConstraint).isEqualTo(1f) 145 } 146 } 147 148 @Test 149 fun testPrivacyChipVisibilityConstraints_visible() { 150 val changes = CombinedShadeHeadersConstraintManagerImpl 151 .privacyChipVisibilityConstraints(true) 152 changes() 153 154 with(qqsConstraint) { 155 assertThat(systemIconsAlphaConstraint).isEqualTo(0f) 156 } 157 158 with(qsConstraint) { 159 assertThat(systemIconsAlphaConstraint).isEqualTo(1f) 160 } 161 162 with(largeScreenConstraint) { 163 assertThat(systemIconsAlphaConstraint).isEqualTo(1f) 164 } 165 } 166 167 @Test 168 fun testEmptyCutoutConstraints() { 169 val changes = CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints() 170 changes() 171 172 // QS and Large Screen don't change with cutouts. 173 assertThat(changes.qsConstraintsChanges).isNull() 174 assertThat(changes.largeScreenConstraintsChanges).isNull() 175 176 with(qqsConstraint) { 177 // In this case, the date is constrained on the end by a Barrier determined by either 178 // privacy or clickableIcons 179 assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.barrier) 180 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 181 .isEqualTo(R.id.date) 182 assertThat(getConstraint(R.id.privacy_container).layout.startToEnd).isEqualTo(R.id.date) 183 assertThat(getConstraint(R.id.barrier).layout.mReferenceIds).asList().containsExactly( 184 R.id.shade_header_system_icons, 185 R.id.privacy_container 186 ) 187 assertThat(getConstraint(R.id.barrier).layout.mBarrierDirection).isEqualTo(START) 188 } 189 } 190 191 @Test 192 fun testGuidesAreSetInCorrectPosition_largeCutoutSmallerPadding() { 193 val cutoutStart = 100 194 val padding = 10 195 val cutoutEnd = 30 196 val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints( 197 cutoutStart, 198 padding, 199 cutoutEnd, 200 padding 201 ) 202 changes() 203 204 with(qqsConstraint) { 205 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) 206 .isEqualTo(cutoutStart - padding) 207 assertThat(getConstraint(R.id.end_guide).layout.guideEnd) 208 .isEqualTo(cutoutEnd - padding) 209 } 210 211 with(qsConstraint) { 212 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) 213 .isEqualTo(cutoutStart - padding) 214 assertThat(getConstraint(R.id.end_guide).layout.guideEnd) 215 .isEqualTo(cutoutEnd - padding) 216 } 217 218 with(largeScreenConstraint) { 219 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) 220 .isEqualTo(cutoutStart - padding) 221 assertThat(getConstraint(R.id.end_guide).layout.guideEnd) 222 .isEqualTo(cutoutEnd - padding) 223 } 224 } 225 226 @Test 227 fun testGuidesAreSetInCorrectPosition_smallCutoutLargerPadding() { 228 val cutoutStart = 5 229 val padding = 10 230 val cutoutEnd = 10 231 232 val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints( 233 cutoutStart, 234 padding, 235 cutoutEnd, 236 padding 237 ) 238 changes() 239 240 with(qqsConstraint) { 241 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) 242 assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) 243 } 244 245 with(qsConstraint) { 246 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) 247 assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) 248 } 249 250 with(largeScreenConstraint) { 251 assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) 252 assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) 253 } 254 } 255 256 @Test 257 fun testCenterCutoutConstraints_ltr() { 258 val offsetFromEdge = 400 259 val rtl = false 260 261 val changes = CombinedShadeHeadersConstraintManagerImpl 262 .centerCutoutConstraints(rtl, offsetFromEdge) 263 changes() 264 265 // In LTR, center_left is towards the start and center_right is towards the end 266 with(qqsConstraint) { 267 assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) 268 assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) 269 assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_left) 270 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 271 .isEqualTo(R.id.center_right) 272 assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) 273 .isEqualTo(R.id.center_right) 274 } 275 276 with(qsConstraint) { 277 assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) 278 assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) 279 280 assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) 281 assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) 282 283 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 284 .isNotEqualTo(R.id.center_left) 285 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 286 .isNotEqualTo(R.id.center_right) 287 288 assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) 289 .isEqualTo(R.id.center_right) 290 } 291 292 assertThat(changes.largeScreenConstraintsChanges).isNull() 293 } 294 295 @Test 296 fun testCenterCutoutConstraints_rtl() { 297 val offsetFromEdge = 400 298 val rtl = true 299 300 val changes = CombinedShadeHeadersConstraintManagerImpl 301 .centerCutoutConstraints(rtl, offsetFromEdge) 302 changes() 303 304 // In RTL, center_left is towards the end and center_right is towards the start 305 with(qqsConstraint) { 306 assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) 307 assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) 308 assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_right) 309 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 310 .isEqualTo(R.id.center_left) 311 assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) 312 .isEqualTo(R.id.center_left) 313 } 314 315 with(qsConstraint) { 316 assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) 317 assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) 318 319 assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) 320 assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) 321 322 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 323 .isNotEqualTo(R.id.center_left) 324 assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) 325 .isNotEqualTo(R.id.center_right) 326 327 assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) 328 .isEqualTo(R.id.center_left) 329 } 330 331 assertThat(changes.largeScreenConstraintsChanges).isNull() 332 } 333 334 @Test 335 fun testRelevantViewsAreNotMatchConstraints() { 336 val views = mapOf( 337 R.id.clock to "clock", 338 R.id.date to "date", 339 R.id.privacy_container to "privacy", 340 ) 341 views.forEach { (id, name) -> 342 assertWithMessage("$name has 0 height in qqs") 343 .that(qqsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0) 344 assertWithMessage("$name has 0 width in qqs") 345 .that(qqsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0) 346 assertWithMessage("$name has 0 height in qs") 347 .that(qsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0) 348 assertWithMessage("$name has 0 width in qs") 349 .that(qsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0) 350 } 351 } 352 353 @Test 354 fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() { 355 val views = mapOf( 356 R.id.clock to "clock", 357 R.id.privacy_container to "privacy", 358 ) 359 views.forEach { (id, name) -> 360 expect.withMessage("$name changes height") 361 .that(qqsConstraint.getConstraint(id).layout.mHeight.fromConstraint()) 362 .isEqualTo(qsConstraint.getConstraint(id).layout.mHeight.fromConstraint()) 363 expect.withMessage("$name changes width") 364 .that(qqsConstraint.getConstraint(id).layout.mWidth.fromConstraint()) 365 .isEqualTo(qsConstraint.getConstraint(id).layout.mWidth.fromConstraint()) 366 } 367 } 368 369 private fun Int.fromConstraint() = when (this) { 370 ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT" 371 ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT" 372 else -> toString() 373 } 374 375 @Test 376 fun testEmptyCutoutDateIconsAreConstrainedWidth() { 377 CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()() 378 379 assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() 380 val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) 381 assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() 382 } 383 384 @Test 385 fun testCenterCutoutDateIconsAreConstrainedWidth() { 386 CombinedShadeHeadersConstraintManagerImpl.centerCutoutConstraints(false, 10)() 387 388 assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() 389 val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) 390 assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() 391 } 392 393 private val ConstraintSet.systemIconsAlphaConstraint 394 get() = getConstraint(R.id.shade_header_system_icons).propertySet.alpha 395 396 private operator fun ConstraintsChanges.invoke() { 397 qqsConstraintsChanges?.invoke(qqsConstraint) 398 qsConstraintsChanges?.invoke(qsConstraint) 399 largeScreenConstraintsChanges?.invoke(largeScreenConstraint) 400 } 401 } 402