Tests for StatusBarContentInsetsProvider
Reworked some functions for the status bar content insets calculations and added tests for the content rectangle. Test: atest StatusBarContentInsetsProviderTest Bug: 187973222 Change-Id: I3eb473929944c19888f8c256b4c45ddd99948b88
This commit is contained in:
@@ -23,6 +23,8 @@ import android.util.Pair
|
||||
import android.view.DisplayCutout
|
||||
import android.view.View.LAYOUT_DIRECTION_RTL
|
||||
import android.view.WindowManager
|
||||
import android.view.WindowMetrics
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
@@ -118,17 +120,8 @@ class StatusBarContentInsetsProvider @Inject constructor(
|
||||
val chipWidth = rotatedResources.getDimensionPixelSize(
|
||||
R.dimen.ongoing_appops_chip_max_width)
|
||||
|
||||
return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) {
|
||||
Rect(insets.left - dotWidth,
|
||||
insets.top,
|
||||
insets.left + chipWidth,
|
||||
insets.bottom)
|
||||
} else {
|
||||
Rect(insets.right - chipWidth,
|
||||
insets.top,
|
||||
insets.right + dotWidth,
|
||||
insets.bottom)
|
||||
}
|
||||
val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
|
||||
return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,8 +132,7 @@ class StatusBarContentInsetsProvider @Inject constructor(
|
||||
var insets = insetsByCorner[rotation]
|
||||
if (insets == null) {
|
||||
val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
|
||||
insets = getCalculatedInsetsForRotation(rotation, rotatedResources)
|
||||
insetsByCorner[rotation] = insets
|
||||
insets = getAndSetInsetsForRotation(rotation, rotatedResources)
|
||||
}
|
||||
|
||||
return insets
|
||||
@@ -157,13 +149,19 @@ class StatusBarContentInsetsProvider @Inject constructor(
|
||||
}
|
||||
|
||||
private fun getCalculatedInsetsForRotation(
|
||||
@Rotation rotation: Int,
|
||||
@Rotation targetRotation: Int,
|
||||
rotatedResources: Resources
|
||||
): Rect {
|
||||
val dc = context.display.cutout
|
||||
val currentRotation = RotationUtils.getExactRotation(context)
|
||||
|
||||
return calculateInsetsForRotationWithRotatedResources(
|
||||
rotation, rotatedResources, dc, windowManager, context)
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowManager.maximumWindowMetrics,
|
||||
rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
|
||||
rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding))
|
||||
}
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
|
||||
@@ -179,8 +177,8 @@ interface StatusBarContentInsetsChangedListener {
|
||||
|
||||
private const val TAG = "StatusBarInsetsProvider"
|
||||
|
||||
private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect {
|
||||
val bounds = wm.maximumWindowMetrics.bounds
|
||||
private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
|
||||
val bounds = wm.bounds
|
||||
|
||||
if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
|
||||
return bounds
|
||||
@@ -190,9 +188,24 @@ private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotat
|
||||
return Rect(0, 0, bounds.bottom, bounds.right)
|
||||
}
|
||||
|
||||
private fun getCurrentDisplayBounds(wm: WindowManager): Rect {
|
||||
val bounds = wm.maximumWindowMetrics.bounds
|
||||
return bounds
|
||||
@VisibleForTesting
|
||||
fun getPrivacyChipBoundingRectForInsets(
|
||||
contentRect: Rect,
|
||||
dotWidth: Int,
|
||||
chipWidth: Int,
|
||||
isRtl: Boolean
|
||||
): Rect {
|
||||
return if (isRtl) {
|
||||
Rect(contentRect.left - dotWidth,
|
||||
contentRect.top,
|
||||
contentRect.left + chipWidth,
|
||||
contentRect.bottom)
|
||||
} else {
|
||||
Rect(contentRect.right - chipWidth,
|
||||
contentRect.top,
|
||||
contentRect.right + dotWidth,
|
||||
contentRect.bottom)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,41 +219,32 @@ private fun getCurrentDisplayBounds(wm: WindowManager): Rect {
|
||||
* @see [RotationUtils#getResourcesForRotation]
|
||||
*/
|
||||
fun calculateInsetsForRotationWithRotatedResources(
|
||||
@Rotation currentRotation: Int,
|
||||
@Rotation targetRotation: Int,
|
||||
rotatedResources: Resources,
|
||||
displayCutout: DisplayCutout?,
|
||||
windowmanager: WindowManager,
|
||||
context: Context
|
||||
windowMetrics: WindowMetrics,
|
||||
statusBarHeight: Int,
|
||||
roundedCornerPadding: Int
|
||||
): Rect {
|
||||
val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
|
||||
|
||||
val exactRotation = RotationUtils.getExactRotation(context)
|
||||
val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height)
|
||||
|
||||
/*
|
||||
TODO: Check if this is ever used for devices with no rounded corners
|
||||
val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start)
|
||||
val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end)
|
||||
val left = if (rtl) paddingEnd else paddingStart
|
||||
val right = if(rtl) paddingStart else paddingEnd
|
||||
val left = if (isRtl) paddingEnd else paddingStart
|
||||
val right = if (isRtl) paddingStart else paddingEnd
|
||||
*/
|
||||
|
||||
val roundedCornerPadding = rotatedResources.getDimensionPixelSize(
|
||||
R.dimen.rounded_corner_content_padding)
|
||||
|
||||
val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation)
|
||||
val currentBounds = getCurrentDisplayBounds(windowmanager)
|
||||
val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
|
||||
val currentBounds = windowMetrics.bounds
|
||||
|
||||
val sbLeftRight = getStatusBarLeftRight(
|
||||
displayCutout,
|
||||
height,
|
||||
statusBarHeight,
|
||||
rotZeroBounds.right,
|
||||
rotZeroBounds.bottom,
|
||||
currentBounds.width(),
|
||||
currentBounds.height(),
|
||||
roundedCornerPadding,
|
||||
targetRotation,
|
||||
exactRotation)
|
||||
currentRotation)
|
||||
|
||||
return sbLeftRight
|
||||
}
|
||||
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.phone
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.test.suitebuilder.annotation.SmallTest
|
||||
import android.view.DisplayCutout
|
||||
import android.view.WindowMetrics
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.leak.RotationUtils
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
|
||||
import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
|
||||
import com.android.systemui.util.leak.RotationUtils.Rotation
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class StatusBarContentInsetsProviderTest : SysuiTestCase() {
|
||||
@Mock private lateinit var dc: DisplayCutout
|
||||
@Mock private lateinit var windowMetrics: WindowMetrics
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetBoundingRectForPrivacyChipForRotation_noCutout() {
|
||||
val screenBounds = Rect(0, 0, 1080, 2160)
|
||||
val roundedCornerPadding = 20
|
||||
val sbHeightPortrait = 100
|
||||
val sbHeightLandscape = 60
|
||||
val currentRotation = ROTATION_NONE
|
||||
val chipWidth = 30
|
||||
val dotWidth = 10
|
||||
|
||||
`when`(windowMetrics.bounds).thenReturn(screenBounds)
|
||||
|
||||
var isRtl = false
|
||||
var targetRotation = ROTATION_NONE
|
||||
var bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
null,
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
|
||||
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
|
||||
/* 1080 - 20 (rounded corner) - 30 (chip),
|
||||
* 0 (sb top)
|
||||
* 1080 - 20 (rounded corner) + 10 ( dot),
|
||||
* 100 (sb height portrait)
|
||||
*/
|
||||
var expected = Rect(1030, 0, 1070, 100)
|
||||
assertRects(expected, chipBounds, currentRotation, targetRotation)
|
||||
isRtl = true
|
||||
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
|
||||
/* 0 + 20 (rounded corner) - 10 (dot),
|
||||
* 0 (sb top)
|
||||
* 0 + 20 (rounded corner) + 30 (chip),
|
||||
* 100 (sb height portrait)
|
||||
*/
|
||||
expected = Rect(10, 0, 50, 100)
|
||||
assertRects(expected, chipBounds, currentRotation, targetRotation)
|
||||
|
||||
isRtl = false
|
||||
targetRotation = ROTATION_LANDSCAPE
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
|
||||
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
|
||||
/* 2160 - 20 (rounded corner) - 30 (chip),
|
||||
* 0 (sb top)
|
||||
* 2160 - 20 (rounded corner) + 10 ( dot),
|
||||
* 60 (sb height landscape)
|
||||
*/
|
||||
expected = Rect(2110, 0, 2150, 60)
|
||||
assertRects(expected, chipBounds, currentRotation, targetRotation)
|
||||
isRtl = true
|
||||
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
|
||||
/* 0 + 20 (rounded corner) - 10 (dot),
|
||||
* 0 (sb top)
|
||||
* 0 + 20 (rounded corner) + 30 (chip),
|
||||
* 60 (sb height landscape)
|
||||
*/
|
||||
expected = Rect(10, 0, 50, 60)
|
||||
assertRects(expected, chipBounds, currentRotation, targetRotation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() {
|
||||
// GIVEN a device in portrait mode with width < height and a display cutout in the top-left
|
||||
val screenBounds = Rect(0, 0, 1080, 2160)
|
||||
val dcBounds = Rect(0, 0, 100, 100)
|
||||
val roundedCornerPadding = 20
|
||||
val sbHeightPortrait = 100
|
||||
val sbHeightLandscape = 60
|
||||
val currentRotation = ROTATION_NONE
|
||||
|
||||
`when`(windowMetrics.bounds).thenReturn(screenBounds)
|
||||
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
|
||||
|
||||
// THEN rotations which share a short side should use the greater value between rounded
|
||||
// corner padding and the display cutout's size
|
||||
var targetRotation = ROTATION_NONE
|
||||
var expectedBounds = Rect(dcBounds.right,
|
||||
0,
|
||||
screenBounds.right - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
var bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_LANDSCAPE
|
||||
expectedBounds = Rect(dcBounds.height(),
|
||||
0,
|
||||
screenBounds.height() - roundedCornerPadding,
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
// THEN the side that does NOT share a short side with the display cutout ignores the
|
||||
// display cutout bounds
|
||||
targetRotation = ROTATION_UPSIDE_DOWN
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.width() - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
// Phone in portrait, seascape (rot_270) bounds
|
||||
targetRotation = ROTATION_SEASCAPE
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.height() - dcBounds.height(),
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() {
|
||||
// GIVEN phone in portrait mode, where width < height and the cutout is not in the corner
|
||||
// the assumption here is that if the cutout does NOT touch the corner then we have room to
|
||||
// layout the status bar in the given space.
|
||||
|
||||
val screenBounds = Rect(0, 0, 1080, 2160)
|
||||
// cutout centered at the top
|
||||
val dcBounds = Rect(490, 0, 590, 100)
|
||||
val roundedCornerPadding = 20
|
||||
val sbHeightPortrait = 100
|
||||
val sbHeightLandscape = 60
|
||||
val currentRotation = ROTATION_NONE
|
||||
|
||||
`when`(windowMetrics.bounds).thenReturn(screenBounds)
|
||||
`when`(dc.boundingRects).thenReturn(listOf(dcBounds))
|
||||
|
||||
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
|
||||
// potential letterboxing
|
||||
var targetRotation = ROTATION_NONE
|
||||
var expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.right - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
var bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_LANDSCAPE
|
||||
expectedBounds = Rect(dcBounds.height(),
|
||||
0,
|
||||
screenBounds.height() - roundedCornerPadding,
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_UPSIDE_DOWN
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.right - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_SEASCAPE
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.height() - dcBounds.height(),
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
dc,
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCalculateInsetsForRotationWithRotatedResources_noCutout() {
|
||||
// GIVEN device in portrait mode, where width < height and no cutout
|
||||
val currentRotation = ROTATION_NONE
|
||||
val screenBounds = Rect(0, 0, 1080, 2160)
|
||||
val roundedCornerPadding = 20
|
||||
val sbHeightPortrait = 100
|
||||
val sbHeightLandscape = 60
|
||||
|
||||
`when`(windowMetrics.bounds).thenReturn(screenBounds)
|
||||
|
||||
// THEN content insets should only use rounded corner padding
|
||||
var targetRotation = ROTATION_NONE
|
||||
var expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.right - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
var bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
null, /* no cutout */
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_LANDSCAPE
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.height() - roundedCornerPadding,
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
null, /* no cutout */
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_UPSIDE_DOWN
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.width() - roundedCornerPadding,
|
||||
sbHeightPortrait)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
null, /* no cutout */
|
||||
windowMetrics,
|
||||
sbHeightPortrait,
|
||||
roundedCornerPadding)
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
|
||||
targetRotation = ROTATION_LANDSCAPE
|
||||
expectedBounds = Rect(roundedCornerPadding,
|
||||
0,
|
||||
screenBounds.height() - roundedCornerPadding,
|
||||
sbHeightLandscape)
|
||||
|
||||
bounds = calculateInsetsForRotationWithRotatedResources(
|
||||
currentRotation,
|
||||
targetRotation,
|
||||
null, /* no cutout */
|
||||
windowMetrics,
|
||||
sbHeightLandscape,
|
||||
roundedCornerPadding)
|
||||
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
|
||||
}
|
||||
|
||||
private fun assertRects(
|
||||
expected: Rect,
|
||||
actual: Rect,
|
||||
@Rotation currentRotation: Int,
|
||||
@Rotation targetRotation: Int
|
||||
) {
|
||||
assertTrue(
|
||||
"Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
|
||||
" targetRotation=${RotationUtils.toString(targetRotation)}" +
|
||||
" expected=$expected actual=$actual",
|
||||
expected.equals(actual))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user