• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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