/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.shade import android.testing.AndroidTestingRunner import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.google.common.truth.Expect import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { private lateinit var qqsConstraint: ConstraintSet private lateinit var qsConstraint: ConstraintSet private lateinit var largeScreenConstraint: ConstraintSet @get:Rule val expect: Expect = Expect.create() @Before fun setUp() { qqsConstraint = ConstraintSet().apply { load(context, context.resources.getXml(R.xml.qqs_header)) } qsConstraint = ConstraintSet().apply { load(context, context.resources.getXml(R.xml.qs_header)) } largeScreenConstraint = ConstraintSet().apply { load(context, context.resources.getXml(R.xml.large_screen_shade_header)) } } @Test fun testEdgeElementsAlignedWithGuide_qqs() { with(qqsConstraint) { assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(R.id.begin_guide) assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f) assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(R.id.end_guide) assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(1f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) .isEqualTo(R.id.end_guide) assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias) .isEqualTo(1f) } } @Test fun testClockScale() { with(qqsConstraint.getConstraint(R.id.clock)) { assertThat(transform.scaleX).isEqualTo(1f) assertThat(transform.scaleY).isEqualTo(1f) } with(qsConstraint.getConstraint(R.id.clock)) { assertThat(transform.scaleX).isGreaterThan(1f) assertThat(transform.scaleY).isGreaterThan(1f) } } @Test fun testEdgeElementsAlignedWithEdgeOrGuide_qs() { with(qsConstraint) { assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(PARENT_ID) assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f) assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID) assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f) assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(PARENT_ID) assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(0.5f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) .isEqualTo(R.id.end_guide) assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(1f) } } @Test fun testEdgeElementsAlignedWithEdge_largeScreen() { with(largeScreenConstraint) { assertThat(getConstraint(R.id.clock).layout.startToEnd).isEqualTo(R.id.begin_guide) assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0.5f) assertThat(getConstraint(R.id.privacy_container).layout.endToStart) .isEqualTo(R.id.end_guide) assertThat(getConstraint(R.id.privacy_container).layout.horizontalBias).isEqualTo(0.5f) } } @Test fun testCarrierAlpha() { assertThat(qqsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(0f) assertThat(qsConstraint.getConstraint(R.id.carrier_group).propertySet.alpha).isEqualTo(1f) assertThat(largeScreenConstraint.getConstraint(R.id.carrier_group).propertySet.alpha) .isEqualTo(1f) } @Test fun testPrivacyChipVisibilityConstraints_notVisible() { val changes = CombinedShadeHeadersConstraintManagerImpl .privacyChipVisibilityConstraints(false) changes() with(qqsConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(qsConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @Test fun testPrivacyChipVisibilityConstraints_visible() { val changes = CombinedShadeHeadersConstraintManagerImpl .privacyChipVisibilityConstraints(true) changes() with(qqsConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(0f) } with(qsConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @Test fun testEmptyCutoutConstraints() { val changes = CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints() changes() // QS and Large Screen don't change with cutouts. assertThat(changes.qsConstraintsChanges).isNull() assertThat(changes.largeScreenConstraintsChanges).isNull() with(qqsConstraint) { // In this case, the date is constrained on the end by a Barrier determined by either // privacy or clickableIcons assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.barrier) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.date) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd).isEqualTo(R.id.date) assertThat(getConstraint(R.id.barrier).layout.mReferenceIds).asList().containsExactly( R.id.shade_header_system_icons, R.id.privacy_container ) assertThat(getConstraint(R.id.barrier).layout.mBarrierDirection).isEqualTo(START) } } @Test fun testGuidesAreSetInCorrectPosition_largeCutoutSmallerPadding() { val cutoutStart = 100 val padding = 10 val cutoutEnd = 30 val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints( cutoutStart, padding, cutoutEnd, padding ) changes() with(qqsConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) .isEqualTo(cutoutStart - padding) assertThat(getConstraint(R.id.end_guide).layout.guideEnd) .isEqualTo(cutoutEnd - padding) } with(qsConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) .isEqualTo(cutoutStart - padding) assertThat(getConstraint(R.id.end_guide).layout.guideEnd) .isEqualTo(cutoutEnd - padding) } with(largeScreenConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin) .isEqualTo(cutoutStart - padding) assertThat(getConstraint(R.id.end_guide).layout.guideEnd) .isEqualTo(cutoutEnd - padding) } } @Test fun testGuidesAreSetInCorrectPosition_smallCutoutLargerPadding() { val cutoutStart = 5 val padding = 10 val cutoutEnd = 10 val changes = CombinedShadeHeadersConstraintManagerImpl.edgesGuidelinesConstraints( cutoutStart, padding, cutoutEnd, padding ) changes() with(qqsConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) } with(qsConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) } with(largeScreenConstraint) { assertThat(getConstraint(R.id.begin_guide).layout.guideBegin).isEqualTo(0) assertThat(getConstraint(R.id.end_guide).layout.guideEnd).isEqualTo(0) } } @Test fun testCenterCutoutConstraints_ltr() { val offsetFromEdge = 400 val rtl = false val changes = CombinedShadeHeadersConstraintManagerImpl .centerCutoutConstraints(rtl, offsetFromEdge) changes() // In LTR, center_left is towards the start and center_right is towards the end with(qqsConstraint) { assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_left) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_right) } with(qsConstraint) { assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_right) } assertThat(changes.largeScreenConstraintsChanges).isNull() } @Test fun testCenterCutoutConstraints_rtl() { val offsetFromEdge = 400 val rtl = true val changes = CombinedShadeHeadersConstraintManagerImpl .centerCutoutConstraints(rtl, offsetFromEdge) changes() // In RTL, center_left is towards the end and center_right is towards the start with(qqsConstraint) { assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_right) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_left) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_left) } with(qsConstraint) { assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_left) } assertThat(changes.largeScreenConstraintsChanges).isNull() } @Test fun testRelevantViewsAreNotMatchConstraints() { val views = mapOf( R.id.clock to "clock", R.id.date to "date", R.id.privacy_container to "privacy", ) views.forEach { (id, name) -> assertWithMessage("$name has 0 height in qqs") .that(qqsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0) assertWithMessage("$name has 0 width in qqs") .that(qqsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0) assertWithMessage("$name has 0 height in qs") .that(qsConstraint.getConstraint(id).layout.mHeight).isNotEqualTo(0) assertWithMessage("$name has 0 width in qs") .that(qsConstraint.getConstraint(id).layout.mWidth).isNotEqualTo(0) } } @Test fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() { val views = mapOf( R.id.clock to "clock", R.id.privacy_container to "privacy", ) views.forEach { (id, name) -> expect.withMessage("$name changes height") .that(qqsConstraint.getConstraint(id).layout.mHeight.fromConstraint()) .isEqualTo(qsConstraint.getConstraint(id).layout.mHeight.fromConstraint()) expect.withMessage("$name changes width") .that(qqsConstraint.getConstraint(id).layout.mWidth.fromConstraint()) .isEqualTo(qsConstraint.getConstraint(id).layout.mWidth.fromConstraint()) } } private fun Int.fromConstraint() = when (this) { ViewGroup.LayoutParams.MATCH_PARENT -> "MATCH_PARENT" ViewGroup.LayoutParams.WRAP_CONTENT -> "WRAP_CONTENT" else -> toString() } @Test fun testEmptyCutoutDateIconsAreConstrainedWidth() { CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } @Test fun testCenterCutoutDateIconsAreConstrainedWidth() { CombinedShadeHeadersConstraintManagerImpl.centerCutoutConstraints(false, 10)() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } private val ConstraintSet.systemIconsAlphaConstraint get() = getConstraint(R.id.shade_header_system_icons).propertySet.alpha private operator fun ConstraintsChanges.invoke() { qqsConstraintsChanges?.invoke(qqsConstraint) qsConstraintsChanges?.invoke(qsConstraint) largeScreenConstraintsChanges?.invoke(largeScreenConstraint) } }