Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d3833727 authored by Evan Laird's avatar Evan Laird Committed by Automerger Merge Worker
Browse files

Merge changes Iaaefbeb2,I3eb47392 into sc-dev am: d73b8383

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14650174

Change-Id: Ie21d734cba438869a17284e14e54d6058e4d9eb0
parents c0870640 d73b8383
Loading
Loading
Loading
Loading
+21 −5
Original line number Diff line number Diff line
@@ -26,14 +26,18 @@ import android.os.Process
import android.provider.DeviceConfig
import android.util.Log
import android.view.View
import com.android.systemui.Dumpable

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.Assert
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
import java.io.FileDescriptor
import java.io.PrintWriter

import javax.inject.Inject

@@ -59,9 +63,10 @@ class SystemStatusAnimationScheduler @Inject constructor(
    private val coordinator: SystemEventCoordinator,
    private val chipAnimationController: SystemEventChipAnimationController,
    private val statusBarWindowController: StatusBarWindowController,
    private val dumpManager: DumpManager,
    private val systemClock: SystemClock,
    @Main private val executor: DelayableExecutor
) : CallbackController<SystemStatusAnimationCallback> {
) : CallbackController<SystemStatusAnimationCallback>, Dumpable {

    companion object {
        private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
@@ -71,10 +76,6 @@ class SystemStatusAnimationScheduler @Inject constructor(
                PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true)
    }

    /** True from the time a scheduled event starts until it's animation finishes */
    var isActive = false
        private set

    @SystemAnimationState var animationState: Int = IDLE
        private set

@@ -88,6 +89,7 @@ class SystemStatusAnimationScheduler @Inject constructor(

    init {
        coordinator.attachScheduler(this)
        dumpManager.registerDumpable(TAG, this)
    }

    fun onStatusEvent(event: StatusEvent) {
@@ -293,6 +295,20 @@ class SystemStatusAnimationScheduler @Inject constructor(
        anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
    }

    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
        pw.println("Scheduled event: $scheduledEvent")
        pw.println("Has persistent privacy dot: $hasPersistentDot")
        pw.println("Animation state: $animationState")
        pw.println("Listeners:")
        if (listeners.isEmpty()) {
            pw.println("(none)")
        } else {
            listeners.forEach {
                pw.println("  $it")
            }
        }
    }

    inner class ChipAnimatorAdapter(
        @SystemAnimationState val endState: Int,
        val viewCreator: (context: Context) -> View
+43 −39
Original line number Diff line number Diff line
@@ -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
}
+368 −0
Original line number Diff line number Diff line
/*
 * 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))
    }
}
 No newline at end of file