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.decor 18 19 import android.graphics.Insets 20 import android.graphics.Rect 21 import android.testing.AndroidTestingRunner 22 import android.testing.TestableResources 23 import android.util.RotationUtils 24 import android.util.Size 25 import android.view.Display 26 import android.view.DisplayCutout 27 import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH 28 import android.view.DisplayInfo 29 import android.view.Surface 30 import androidx.test.filters.SmallTest 31 import com.android.systemui.SysuiTestCase 32 import com.android.systemui.util.mockito.any 33 import org.junit.Assert 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.Mock 38 import org.mockito.Mockito.doAnswer 39 import org.mockito.MockitoAnnotations 40 41 @RunWith(AndroidTestingRunner::class) 42 @SmallTest 43 class CutoutDecorProviderFactoryTest : SysuiTestCase() { 44 45 @Mock private lateinit var display: Display 46 private var testableRes: TestableResources? = null 47 private lateinit var factory: CutoutDecorProviderFactory 48 49 @Before 50 fun setUp() { 51 MockitoAnnotations.initMocks(this) 52 testableRes = mContext.orCreateTestableResources 53 factory = CutoutDecorProviderFactory(testableRes!!.resources, display) 54 } 55 56 private fun setupFillCutout(fillCutout: Boolean) { 57 testableRes!!.addOverride( 58 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout 59 ) 60 } 61 62 private fun setupDisplayInfo( 63 displayCutout: DisplayCutout? = null, 64 @Surface.Rotation rotation: Int = Surface.ROTATION_0, 65 displayId: Int = -1 66 ) { 67 doAnswer { 68 it.getArgument<DisplayInfo>(0).let { info -> 69 info.displayCutout = displayCutout 70 info.rotation = rotation 71 info.displayId = displayId 72 } 73 true 74 }.`when`(display).getDisplayInfo(any<DisplayInfo>()) 75 } 76 77 private fun getCutout( 78 safeInsets: Insets, 79 cutoutBounds: Array<Rect?>, 80 @Surface.Rotation rotation: Int = Surface.ROTATION_0, 81 cutoutParentSizeForRotate: Size = Size(100, 200) 82 ): DisplayCutout { 83 val insets = RotationUtils.rotateInsets(safeInsets, rotation) 84 val sorted = arrayOfNulls<Rect>(BOUNDS_POSITION_LENGTH) 85 for (pos in 0 until BOUNDS_POSITION_LENGTH) { 86 val rotatedPos = (pos - rotation + BOUNDS_POSITION_LENGTH) % BOUNDS_POSITION_LENGTH 87 if (cutoutBounds[pos] != null) { 88 RotationUtils.rotateBounds( 89 cutoutBounds[pos], 90 cutoutParentSizeForRotate.width, 91 cutoutParentSizeForRotate.height, 92 rotation 93 ) 94 } 95 sorted[rotatedPos] = cutoutBounds[pos] 96 } 97 return DisplayCutout( 98 insets, 99 sorted[DisplayCutout.BOUNDS_POSITION_LEFT], 100 sorted[DisplayCutout.BOUNDS_POSITION_TOP], 101 sorted[DisplayCutout.BOUNDS_POSITION_RIGHT], 102 sorted[DisplayCutout.BOUNDS_POSITION_BOTTOM] 103 ) 104 } 105 106 @Test 107 fun testGetNothingIfNoCutout() { 108 setupFillCutout(false) 109 110 Assert.assertFalse(factory.hasProviders) 111 Assert.assertEquals(0, factory.providers.size) 112 } 113 114 @Test 115 fun testGetTopCutoutProvider() { 116 setupFillCutout(true) 117 setupDisplayInfo( 118 getCutout( 119 safeInsets = Insets.of(0, 1, 0, 0), 120 cutoutBounds = arrayOf(null, Rect(9, 0, 10, 1), null, null) 121 ) 122 ) 123 124 Assert.assertTrue(factory.hasProviders) 125 126 val providers = factory.providers 127 Assert.assertEquals(1, providers.size) 128 Assert.assertEquals(1, providers[0].numOfAlignedBound) 129 Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, providers[0].alignedBounds[0]) 130 } 131 132 @Test 133 fun testGetBottomCutoutProviderOnLandscape() { 134 setupFillCutout(true) 135 setupDisplayInfo( 136 getCutout( 137 safeInsets = Insets.of(0, 0, 0, 1), 138 cutoutBounds = arrayOf(null, null, null, Rect(45, 199, 55, 200)), 139 rotation = Surface.ROTATION_90 140 ), 141 Surface.ROTATION_90 142 ) 143 144 Assert.assertTrue(factory.hasProviders) 145 146 val providers = factory.providers 147 Assert.assertEquals(1, providers.size) 148 Assert.assertEquals(1, providers[0].numOfAlignedBound) 149 Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_BOTTOM, providers[0].alignedBounds[0]) 150 } 151 152 @Test 153 fun testGetLeftCutoutProviderOnSeascape() { 154 setupFillCutout(true) 155 setupDisplayInfo( 156 getCutout( 157 safeInsets = Insets.of(1, 0, 0, 0), 158 cutoutBounds = arrayOf(Rect(0, 20, 1, 40), null, null, null), 159 rotation = Surface.ROTATION_270 160 ), 161 Surface.ROTATION_270 162 ) 163 164 Assert.assertTrue(factory.hasProviders) 165 166 val providers = factory.providers 167 Assert.assertEquals(1, providers.size) 168 Assert.assertEquals(1, providers[0].numOfAlignedBound) 169 Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_LEFT, providers[0].alignedBounds[0]) 170 } 171 172 @Test 173 fun testGetTopRightCutoutProviderOnReverse() { 174 setupFillCutout(true) 175 setupDisplayInfo( 176 getCutout( 177 safeInsets = Insets.of(0, 1, 1, 0), 178 cutoutBounds = arrayOf( 179 null, 180 Rect(9, 0, 10, 1), 181 Rect(99, 40, 100, 60), 182 null 183 ), 184 rotation = Surface.ROTATION_180 185 ), 186 Surface.ROTATION_180 187 ) 188 189 Assert.assertTrue(factory.hasProviders) 190 191 val providers = factory.providers 192 Assert.assertEquals(2, providers.size) 193 Assert.assertEquals(1, providers[0].numOfAlignedBound) 194 Assert.assertEquals(1, providers[1].numOfAlignedBound) 195 providers.sortedBy { it.alignedBounds[0] }.let { 196 Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_TOP, it[0].alignedBounds[0]) 197 Assert.assertEquals(DisplayCutout.BOUNDS_POSITION_RIGHT, it[1].alignedBounds[0]) 198 } 199 } 200 } 201