1 /* 2 * 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.qs.panels.ui 18 19 import androidx.compose.foundation.layout.Box 20 import androidx.compose.foundation.layout.fillMaxWidth 21 import androidx.compose.foundation.layout.height 22 import androidx.compose.runtime.Composable 23 import androidx.compose.ui.Modifier 24 import androidx.compose.ui.platform.testTag 25 import androidx.compose.ui.test.getBoundsInRoot 26 import androidx.compose.ui.test.junit4.createComposeRule 27 import androidx.compose.ui.test.onNodeWithTag 28 import androidx.compose.ui.unit.Dp 29 import androidx.compose.ui.unit.DpRect 30 import androidx.compose.ui.unit.dp 31 import androidx.compose.ui.unit.width 32 import androidx.test.ext.junit.runners.AndroidJUnit4 33 import androidx.test.filters.SmallTest 34 import com.android.systemui.SysuiTestCase 35 import com.android.systemui.qs.composefragment.QuickQuickSettingsLayout 36 import com.android.systemui.qs.composefragment.QuickSettingsLayout 37 import com.google.common.truth.Truth.assertThat 38 import org.junit.Rule 39 import org.junit.Test 40 import org.junit.runner.RunWith 41 42 @RunWith(AndroidJUnit4::class) 43 @SmallTest 44 class QSFragmentComposeTest : SysuiTestCase() { 45 46 @get:Rule val composeTestRule = createComposeRule() 47 48 @Test portraitLayout_qqsnull49 fun portraitLayout_qqs() { 50 composeTestRule.setContent { 51 QuickQuickSettingsLayout( 52 tiles = { Tiles(TILES_HEIGHT_PORTRAIT) }, 53 media = { Media() }, 54 mediaInRow = false, 55 ) 56 } 57 58 composeTestRule.waitForIdle() 59 60 val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot() 61 val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot() 62 63 // All nodes aligned in a column 64 assertThat(tilesBounds.left).isEqualTo(mediaBounds.left) 65 assertThat(tilesBounds.right).isEqualTo(mediaBounds.right) 66 assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top) 67 } 68 69 @Test landscapeLayout_qqsnull70 fun landscapeLayout_qqs() { 71 composeTestRule.setContent { 72 QuickQuickSettingsLayout( 73 tiles = { Tiles(TILES_HEIGHT_LANDSCAPE) }, 74 media = { Media() }, 75 mediaInRow = true, 76 ) 77 } 78 79 composeTestRule.waitForIdle() 80 81 val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot() 82 val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot() 83 84 // Media to the right of tiles 85 assertThat(tilesBounds.right).isLessThan(mediaBounds.left) 86 // "Same" width 87 assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp) 88 // Vertically centered 89 assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp) 90 } 91 92 @Test portraitLayout_qsnull93 fun portraitLayout_qs() { 94 composeTestRule.setContent { 95 QuickSettingsLayout( 96 brightness = { Brightness() }, 97 tiles = { Tiles(TILES_HEIGHT_PORTRAIT) }, 98 media = { Media() }, 99 mediaInRow = false, 100 ) 101 } 102 103 composeTestRule.waitForIdle() 104 105 val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot() 106 val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot() 107 val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot() 108 109 assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left) 110 assertThat(tilesBounds.left).isEqualTo(mediaBounds.left) 111 112 assertThat(brightnessBounds.right).isEqualTo(tilesBounds.right) 113 assertThat(tilesBounds.right).isEqualTo(mediaBounds.right) 114 115 assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top) 116 assertThat(tilesBounds.bottom).isLessThan(mediaBounds.top) 117 } 118 119 @Test landscapeLayout_qsnull120 fun landscapeLayout_qs() { 121 composeTestRule.setContent { 122 QuickSettingsLayout( 123 brightness = { Brightness() }, 124 tiles = { Tiles(TILES_HEIGHT_PORTRAIT) }, 125 media = { Media() }, 126 mediaInRow = true, 127 ) 128 } 129 130 composeTestRule.waitForIdle() 131 132 val brightnessBounds = composeTestRule.onNodeWithTag(BRIGHTNESS).getBoundsInRoot() 133 val tilesBounds = composeTestRule.onNodeWithTag(TILES).getBoundsInRoot() 134 val mediaBounds = composeTestRule.onNodeWithTag(MEDIA).getBoundsInRoot() 135 136 // Brightness takes full width, with left end aligned with tiles and right end aligned with 137 // media 138 assertThat(brightnessBounds.left).isEqualTo(tilesBounds.left) 139 assertThat(brightnessBounds.right).isEqualTo(mediaBounds.right) 140 141 // Brightness above tiles and media 142 assertThat(brightnessBounds.bottom).isLessThan(tilesBounds.top) 143 assertThat(brightnessBounds.bottom).isLessThan(mediaBounds.top) 144 145 // Media to the right of tiles 146 assertThat(tilesBounds.right).isLessThan(mediaBounds.left) 147 // "Same" width 148 assertThat((tilesBounds.width - mediaBounds.width).abs()).isAtMost(1.dp) 149 // Vertically centered 150 assertThat((tilesBounds.centerY - mediaBounds.centerY).abs()).isAtMost(1.dp) 151 } 152 153 private companion object { 154 const val BRIGHTNESS = "brightness" 155 const val TILES = "tiles" 156 const val MEDIA = "media" 157 val TILES_HEIGHT_PORTRAIT = 300.dp 158 val TILES_HEIGHT_LANDSCAPE = 150.dp 159 val MEDIA_HEIGHT = 100.dp 160 val BRIGHTNESS_HEIGHT = 64.dp 161 162 @Composable Brightnessnull163 fun Brightness() { 164 Box(modifier = Modifier.testTag(BRIGHTNESS).height(BRIGHTNESS_HEIGHT).fillMaxWidth()) 165 } 166 167 @Composable Tilesnull168 fun Tiles(height: Dp) { 169 Box(modifier = Modifier.testTag(TILES).height(height).fillMaxWidth()) 170 } 171 172 @Composable Medianull173 fun Media() { 174 Box(modifier = Modifier.testTag(MEDIA).height(MEDIA_HEIGHT).fillMaxWidth()) 175 } 176 177 val DpRect.centerY: Dp 178 get() = (top + bottom) / 2 179 absnull180 fun Dp.abs() = if (this > 0.dp) this else -this 181 } 182 } 183