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 18 19 import android.content.Context 20 import android.graphics.Canvas 21 import android.graphics.Insets 22 import android.graphics.Path 23 import android.graphics.Rect 24 import android.graphics.RectF 25 import android.graphics.Region 26 import android.testing.AndroidTestingRunner 27 import android.view.Display 28 import android.view.DisplayCutout 29 import android.view.DisplayInfo 30 import android.view.View 31 import androidx.test.filters.SmallTest 32 import com.android.dx.mockito.inline.extended.ExtendedMockito.never 33 import com.android.internal.R 34 import com.android.systemui.util.mockito.eq 35 import com.google.common.truth.Truth.assertThat 36 import org.junit.Before 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 import org.mockito.Mock 40 import org.mockito.Mockito.spy 41 import org.mockito.Mockito.verify 42 import org.mockito.MockitoAnnotations 43 import org.mockito.Mockito.`when` as whenever 44 45 @RunWith(AndroidTestingRunner::class) 46 @SmallTest 47 class DisplayCutoutBaseViewTest : SysuiTestCase() { 48 49 @Mock private lateinit var mockCanvas: Canvas 50 @Mock private lateinit var mockRootView: View 51 @Mock private lateinit var mockDisplay: Display 52 @Mock private lateinit var mockContext: Context 53 54 private lateinit var cutoutBaseView: DisplayCutoutBaseView 55 private val cutout: DisplayCutout = DisplayCutout.Builder() 56 .setSafeInsets(Insets.of(0, 2, 0, 0)) 57 .setBoundingRectTop(Rect(1, 0, 2, 2)) 58 .build() 59 60 @Before setUpnull61 fun setUp() { 62 MockitoAnnotations.initMocks(this) 63 } 64 65 @Test testBoundingRectsToRegionnull66 fun testBoundingRectsToRegion() { 67 setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */) 68 val rect = Rect(1, 2, 3, 4) 69 assertThat(cutoutBaseView.rectsToRegion(listOf(rect)).bounds).isEqualTo(rect) 70 } 71 72 @Test testDrawCutout_fillCutoutnull73 fun testDrawCutout_fillCutout() { 74 setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */) 75 cutoutBaseView.onDraw(mockCanvas) 76 77 verify(cutoutBaseView).drawCutouts(mockCanvas) 78 } 79 80 @Test testDrawCutout_notFillCutoutnull81 fun testDrawCutout_notFillCutout() { 82 setupDisplayCutoutBaseView(false /* fillCutout */, true /* hasCutout */) 83 cutoutBaseView.onDraw(mockCanvas) 84 85 verify(cutoutBaseView, never()).drawCutouts(mockCanvas) 86 } 87 88 @Test testShouldInterceptTouch_hasCutoutnull89 fun testShouldInterceptTouch_hasCutout() { 90 setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */) 91 cutoutBaseView.updateCutout() 92 93 assertThat(cutoutBaseView.shouldInterceptTouch()).isTrue() 94 } 95 96 @Test testShouldInterceptTouch_noCutoutnull97 fun testShouldInterceptTouch_noCutout() { 98 setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */) 99 cutoutBaseView.updateCutout() 100 101 assertThat(cutoutBaseView.shouldInterceptTouch()).isFalse() 102 } 103 104 @Test testGetInterceptRegion_hasCutoutnull105 fun testGetInterceptRegion_hasCutout() { 106 setupDisplayCutoutBaseView(true /* fillCutout */, true /* hasCutout */) 107 whenever(mockRootView.left).thenReturn(0) 108 whenever(mockRootView.top).thenReturn(0) 109 whenever(mockRootView.right).thenReturn(100) 110 whenever(mockRootView.bottom).thenReturn(200) 111 112 val expect = Region() 113 expect.op(cutout.boundingRectTop, Region.Op.UNION) 114 expect.op(0, 0, 100, 200, Region.Op.INTERSECT) 115 116 cutoutBaseView.updateCutout() 117 118 assertThat(cutoutBaseView.interceptRegion).isEqualTo(expect) 119 } 120 121 @Test testGetInterceptRegion_noCutoutnull122 fun testGetInterceptRegion_noCutout() { 123 setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */) 124 cutoutBaseView.updateCutout() 125 126 assertThat(cutoutBaseView.interceptRegion).isNull() 127 } 128 129 @Test testCutoutProtectionnull130 fun testCutoutProtection() { 131 setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */) 132 val bounds = Rect(0, 0, 10, 10) 133 val path = Path() 134 val pathBounds = RectF(bounds) 135 path.addRect(pathBounds, Path.Direction.CCW) 136 137 context.mainExecutor.execute { 138 cutoutBaseView.setProtection(path, bounds) 139 cutoutBaseView.enableShowProtection(true) 140 } 141 waitForIdleSync() 142 143 assertThat(cutoutBaseView.protectionPath.isRect(pathBounds)).isTrue() 144 assertThat(cutoutBaseView.protectionRect).isEqualTo(pathBounds) 145 } 146 147 @Test testCutoutProtection_withDisplayRationull148 fun testCutoutProtection_withDisplayRatio() { 149 setupDisplayCutoutBaseView(true /* fillCutout */, false /* hasCutout */) 150 whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(0.5f) 151 val bounds = Rect(0, 0, 10, 10) 152 val path = Path() 153 val pathBounds = RectF(bounds) 154 path.addRect(pathBounds, Path.Direction.CCW) 155 156 context.mainExecutor.execute { 157 cutoutBaseView.setProtection(path, bounds) 158 cutoutBaseView.enableShowProtection(true) 159 } 160 waitForIdleSync() 161 162 assertThat(cutoutBaseView.protectionPath.isRect(pathBounds)).isTrue() 163 assertThat(cutoutBaseView.protectionRect).isEqualTo(RectF(0f, 0f, 5f, 5f)) 164 } 165 setupDisplayCutoutBaseViewnull166 private fun setupDisplayCutoutBaseView(fillCutout: Boolean, hasCutout: Boolean) { 167 mContext.orCreateTestableResources.addOverride( 168 R.array.config_displayUniqueIdArray, arrayOf<String>()) 169 mContext.orCreateTestableResources.addOverride( 170 R.bool.config_fillMainBuiltInDisplayCutout, fillCutout) 171 172 cutoutBaseView = spy(DisplayCutoutBaseView(mContext)) 173 174 whenever(cutoutBaseView.context).thenReturn(mockContext) 175 whenever(mockContext.display).thenReturn(mockDisplay) 176 whenever(mockDisplay.uniqueId).thenReturn("mockDisplayUniqueId") 177 whenever(cutoutBaseView.rootView).thenReturn(mockRootView) 178 whenever(cutoutBaseView.getPhysicalPixelDisplaySizeRatio()).thenReturn(1f) 179 whenever(mockDisplay.getDisplayInfo(eq(cutoutBaseView.displayInfo)) 180 ).then { 181 val info = it.getArgument<DisplayInfo>(0) 182 info.displayCutout = if (hasCutout) cutout else null 183 return@then true 184 } 185 } 186 }