1 /* 2 * 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.statusbar.phone 18 19 import android.graphics.Color 20 import android.graphics.Rect 21 import android.testing.AndroidTestingRunner 22 import android.view.WindowInsetsController 23 import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS 24 import androidx.test.filters.SmallTest 25 import com.android.internal.statusbar.LetterboxDetails 26 import com.android.internal.view.AppearanceRegion 27 import com.android.systemui.SysuiTestCase 28 import com.android.systemui.dump.DumpManager 29 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent 30 import com.google.common.truth.Expect 31 import com.google.common.truth.Truth.assertThat 32 import org.junit.Before 33 import org.junit.Rule 34 import org.junit.Test 35 import org.junit.runner.RunWith 36 import org.mockito.Mock 37 import org.mockito.Mockito.`when` as whenever 38 import org.mockito.MockitoAnnotations 39 40 @RunWith(AndroidTestingRunner::class) 41 @SmallTest 42 class LetterboxAppearanceCalculatorTest : SysuiTestCase() { 43 44 companion object { 45 private const val DEFAULT_APPEARANCE = 0 46 private const val TEST_APPEARANCE = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS 47 private val TEST_APPEARANCE_REGION_BOUNDS = Rect(0, 0, 20, 100) 48 private val TEST_APPEARANCE_REGION = 49 AppearanceRegion(TEST_APPEARANCE, TEST_APPEARANCE_REGION_BOUNDS) 50 private val TEST_APPEARANCE_REGIONS = arrayOf(TEST_APPEARANCE_REGION) 51 private val TEST_WINDOW_BOUNDS = Rect(0, 0, 500, 500) 52 } 53 54 @get:Rule var expect = Expect.create() 55 56 @Mock private lateinit var lightBarController: LightBarController 57 @Mock private lateinit var statusBarBoundsProvider: StatusBarBoundsProvider 58 @Mock private lateinit var statusBarFragmentComponent: StatusBarFragmentComponent 59 @Mock private lateinit var dumpManager: DumpManager 60 @Mock private lateinit var letterboxBackgroundProvider: LetterboxBackgroundProvider 61 62 private lateinit var calculator: LetterboxAppearanceCalculator 63 64 @Before 65 fun setUp() { 66 MockitoAnnotations.initMocks(this) 67 whenever(statusBarFragmentComponent.boundsProvider).thenReturn(statusBarBoundsProvider) 68 calculator = 69 LetterboxAppearanceCalculator( 70 lightBarController, dumpManager, letterboxBackgroundProvider) 71 calculator.onStatusBarViewInitialized(statusBarFragmentComponent) 72 whenever(letterboxBackgroundProvider.letterboxBackgroundColor).thenReturn(Color.BLACK) 73 whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(false) 74 } 75 76 @Test 77 fun getLetterboxAppearance_overlapStartSide_returnsOriginalWithScrim() { 78 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 79 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 80 val letterbox = letterboxWithInnerBounds(Rect(50, 50, 150, 150)) 81 82 val letterboxAppearance = 83 calculator.getLetterboxAppearance( 84 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 85 86 expect 87 .that(letterboxAppearance.appearance) 88 .isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) 89 expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS) 90 } 91 92 @Test 93 fun getLetterboxAppearance_overlapEndSide_returnsOriginalWithScrim() { 94 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 95 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 96 val letterbox = letterboxWithInnerBounds(Rect(150, 50, 250, 150)) 97 98 val letterboxAppearance = 99 calculator.getLetterboxAppearance( 100 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 101 102 expect 103 .that(letterboxAppearance.appearance) 104 .isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) 105 expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS) 106 } 107 108 /** Regression test for b/287508741 */ 109 @Test 110 fun getLetterboxAppearance_withOverlap_doesNotMutateOriginalBounds() { 111 val statusBarStartSideBounds = Rect(left = 0, top = 0, right = 100, bottom = 100) 112 val statusBarEndSideBounds = Rect(left = 200, top = 0, right = 300, bottom = 100) 113 val letterBoxInnerBounds = Rect(left = 150, top = 50, right = 250, bottom = 150) 114 val statusBarStartSideBoundsCopy = Rect(statusBarStartSideBounds) 115 val statusBarEndSideBoundsCopy = Rect(statusBarEndSideBounds) 116 val letterBoxInnerBoundsCopy = Rect(letterBoxInnerBounds) 117 whenever(statusBarBoundsProvider.visibleStartSideBounds) 118 .thenReturn(statusBarStartSideBounds) 119 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(statusBarEndSideBounds) 120 121 calculator.getLetterboxAppearance( 122 TEST_APPEARANCE, 123 TEST_APPEARANCE_REGIONS, 124 arrayOf(letterboxWithInnerBounds(letterBoxInnerBounds)) 125 ) 126 127 expect.that(statusBarStartSideBounds).isEqualTo(statusBarStartSideBoundsCopy) 128 expect.that(statusBarEndSideBounds).isEqualTo(statusBarEndSideBoundsCopy) 129 expect.that(letterBoxInnerBounds).isEqualTo(letterBoxInnerBoundsCopy) 130 } 131 132 @Test 133 fun getLetterboxAppearance_noOverlap_BackgroundMultiColor_returnsAppearanceWithScrim() { 134 whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(true) 135 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 136 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 137 val letterbox = letterboxWithInnerBounds(Rect(101, 0, 199, 100)) 138 139 val letterboxAppearance = 140 calculator.getLetterboxAppearance( 141 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 142 143 expect 144 .that(letterboxAppearance.appearance) 145 .isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS) 146 expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS) 147 } 148 149 @Test 150 fun getLetterboxAppearance_noOverlap_returnsAppearanceWithoutScrim() { 151 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 152 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 153 val letterbox = letterboxWithInnerBounds(Rect(101, 0, 199, 100)) 154 155 val letterboxAppearance = 156 calculator.getLetterboxAppearance( 157 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 158 159 assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE) 160 } 161 162 @Test 163 fun getLetterboxAppearance_letterboxContainsStartSide_returnsAppearanceWithoutScrim() { 164 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 165 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 166 val letterbox = letterboxWithInnerBounds(Rect(0, 0, 101, 101)) 167 168 val letterboxAppearance = 169 calculator.getLetterboxAppearance( 170 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 171 172 assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE) 173 } 174 175 @Test 176 fun getLetterboxAppearance_letterboxContainsEndSide_returnsAppearanceWithoutScrim() { 177 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 178 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 179 val letterbox = letterboxWithInnerBounds(Rect(199, 0, 301, 101)) 180 181 val letterboxAppearance = 182 calculator.getLetterboxAppearance( 183 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 184 185 assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE) 186 } 187 188 @Test 189 fun getLetterboxAppearance_letterboxContainsEntireStatusBar_returnsAppearanceWithoutScrim() { 190 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 100, 100)) 191 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(200, 0, 300, 100)) 192 val letterbox = letterboxWithInnerBounds(Rect(0, 0, 300, 100)) 193 194 val letterboxAppearance = 195 calculator.getLetterboxAppearance( 196 TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, arrayOf(letterbox)) 197 198 assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE) 199 } 200 201 @Test 202 fun getLetterboxAppearance_returnsAdaptedAppearanceRegions_basedOnLetterboxInnerBounds() { 203 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0)) 204 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0)) 205 val letterbox = letterboxWithInnerBounds(Rect(150, 0, 300, 800)) 206 val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds) 207 208 val letterboxAppearance = 209 calculator.getLetterboxAppearance( 210 TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox)) 211 212 val letterboxAdaptedRegion = letterboxRegion.copy(bounds = letterbox.letterboxInnerBounds) 213 assertThat(letterboxAppearance.appearanceRegions.toList()).contains(letterboxAdaptedRegion) 214 assertThat(letterboxAppearance.appearanceRegions.toList()).doesNotContain(letterboxRegion) 215 } 216 217 @Test 218 fun getLetterboxAppearance_returnsDefaultAppearanceRegions_basedOnLetterboxOuterBounds() { 219 whenever(statusBarBoundsProvider.visibleStartSideBounds).thenReturn(Rect(0, 0, 0, 0)) 220 whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(Rect(0, 0, 0, 0)) 221 val letterbox = 222 letterboxWithBounds( 223 innerBounds = Rect(left = 25, top = 0, right = 75, bottom = 100), 224 fullBounds = Rect(left = 0, top = 0, right = 100, bottom = 100)) 225 val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds) 226 227 val letterboxAppearance = 228 calculator.getLetterboxAppearance( 229 TEST_APPEARANCE, arrayOf(letterboxRegion), arrayOf(letterbox)) 230 231 val outerRegions = 232 listOf( 233 AppearanceRegion( 234 DEFAULT_APPEARANCE, Rect(left = 0, top = 0, right = 25, bottom = 100)), 235 AppearanceRegion( 236 DEFAULT_APPEARANCE, Rect(left = 75, top = 0, right = 100, bottom = 100)), 237 ) 238 assertThat(letterboxAppearance.appearanceRegions.toList()) 239 .containsAtLeastElementsIn(outerRegions) 240 } 241 242 private fun letterboxWithBounds(innerBounds: Rect, fullBounds: Rect) = 243 LetterboxDetails(innerBounds, fullBounds, TEST_APPEARANCE) 244 245 private fun letterboxWithInnerBounds(innerBounds: Rect) = 246 letterboxWithBounds(innerBounds, fullBounds = TEST_WINDOW_BOUNDS) 247 } 248 249 private fun AppearanceRegion.copy(appearance: Int = this.appearance, bounds: Rect = this.bounds) = 250 AppearanceRegion(appearance, bounds) 251 252 private fun Rect(left: Int, top: Int, right: Int, bottom: Int) = Rect(left, top, right, bottom) 253