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

Commit 8df8359b authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato Committed by alinazaidi
Browse files

Prevent privacy dot from hiding if shade is expanded in another display

Each display has a privacy dot (when needed). before this change, a shade expansion was hiding them on all displays.

After this change, the expansion hides them only in the display the shade is at.

Bug: 362719719
Bug: 403608290
Test: PrivacyDotViewControllerTest
Flag: com.android.systemui.shared.status_bar_connected_displays
Flag: com.android.systemui.shade_window_goes_around
Change-Id: I0e517673a02aa5a006ac1409f5d4b347501c63e8
parent 1502f3be
Loading
Loading
Loading
Loading
+49 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.events

import android.graphics.Point
import android.graphics.Rect
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.DisplayAdjustments
@@ -26,15 +27,24 @@ import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_SHADE_WINDOW_GOES_AROUND
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.display
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shade.domain.interactor.shadeDisplaysInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS
import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
@@ -46,6 +56,7 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@@ -54,6 +65,7 @@ import org.junit.runner.RunWith
@RunWithLooper
class PrivacyDotViewControllerTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val mockDisplay = createMockDisplay()
    private val context = getContext().createDisplayContext(mockDisplay)

@@ -62,6 +74,9 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
    private val statusBarStateController = FakeStatusBarStateController()
    private val configurationController = FakeConfigurationController()
    private val contentInsetsProvider = createMockContentInsetsProvider()
    private val shadeDisplaysInteractor = kosmos.shadeDisplaysInteractor
    private val shadeDisplaysRepository = kosmos.fakeShadeDisplaysRepository
    private val shadeInteractor = kosmos.shadeInteractor

    private val topLeftView = initDotView()
    private val topRightView = initDotView()
@@ -81,8 +96,10 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
            configurationController,
            contentInsetsProvider,
            animationScheduler = mock<SystemStatusAnimationScheduler>(),
            shadeInteractor = null,
            shadeInteractor = shadeInteractor,
            uiExecutor = executor,
            displayId = DISPLAY_ID,
            shadeDisplaysInteractor = { shadeDisplaysInteractor },
        )

    @Test
@@ -297,6 +314,36 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
    }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS, FLAG_SHADE_WINDOW_GOES_AROUND)
    fun init_shadeExpandedOnDifferentDisplay_doesNotChangeShadeExpandedState() =
        testScope.runTest {
            shadeDisplaysRepository.setDisplayId(Display.DEFAULT_DISPLAY)
            statusBarStateController.state = SHADE
            statusBarStateController.expanded = true

            val controller = createAndInitializeController()
            shadeDisplaysRepository.setDisplayId(DISPLAY_ID + 1) // other display id
            statusBarStateController.fakeShadeExpansionFullyChanged(true)

            assertThat(controller.currentViewState.shadeExpanded).isEqualTo(false)
        }

    @Test
    @EnableFlags(FLAG_STATUS_BAR_CONNECTED_DISPLAYS, FLAG_SHADE_WINDOW_GOES_AROUND)
    fun init_shadeExpandedOnThisDisplay_doesChangeShadeExpandedState() =
        testScope.runTest {
            shadeDisplaysRepository.setDisplayId(Display.DEFAULT_DISPLAY)
            statusBarStateController.state = SHADE
            statusBarStateController.expanded = false

            val controller = createAndInitializeController()
            shadeDisplaysRepository.setDisplayId(DISPLAY_ID)
            statusBarStateController.fakeShadeExpansionFullyChanged(true)

            assertThat(controller.currentViewState.shadeExpanded).isEqualTo(true)
        }

    @Test
    fun initialize_newViews_gravityIsUpdated() {
        val newTopLeftView = initDotView()
@@ -341,6 +388,7 @@ private val CONTENT_AREA_ROTATION_SEASCAPE = Rect(left = 10, top = 40, right = 9
private val CONTENT_AREA_ROTATION_NONE = Rect(left = 20, top = 30, right = 980, bottom = 100)
private val CONTENT_AREA_ROTATION_LANDSCAPE = Rect(left = 30, top = 20, right = 970, bottom = 100)
private val CONTENT_AREA_ROTATION_UPSIDE_DOWN = Rect(left = 40, top = 10, right = 960, bottom = 100)
private const val DISPLAY_ID = 1

private class InstantExecutor : DelayableExecutor {
    override fun execute(runnable: Runnable) {
+6 −1
Original line number Diff line number Diff line
@@ -59,7 +59,12 @@ constructor(
            statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
        val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null
        val displayScope = displayScopeRepository[displayId] ?: return null
        return factory.create(displayScope, configurationController, contentInsetsProvider)
        return factory.create(
            displayScope,
            configurationController,
            contentInsetsProvider,
            displayId,
        )
    }

    override suspend fun onDisplayRemovalAction(instance: PrivacyDotViewController) {
+40 −5
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.UiThread
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
import android.view.Display
import android.view.View
import android.widget.FrameLayout
import androidx.core.animation.Animator
@@ -32,7 +33,9 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeDisplaysInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -51,6 +54,7 @@ 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 dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
@@ -58,6 +62,8 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf

/**
 * Understands how to keep the persistent privacy dot in the corner of the screen in
@@ -118,6 +124,8 @@ constructor(
    private val animationScheduler: SystemStatusAnimationScheduler,
    shadeInteractor: ShadeInteractor?,
    @ScreenDecorationsThread val uiExecutor: DelayableExecutor,
    @Assisted private val displayId: Int,
    private val shadeDisplaysInteractor: Lazy<ShadeDisplaysInteractor>?,
) : PrivacyDotViewController {
    private lateinit var tl: View
    private lateinit var tr: View
@@ -182,9 +190,24 @@ constructor(
        configurationController.addCallback(configurationListener)
        stateController.addCallback(statusBarStateListener)
        scope.launch {
            if (
                StatusBarConnectedDisplays.isEnabled &&
                    ShadeWindowGoesAround.isEnabled &&
                    shadeDisplaysInteractor != null
            ) {
                combine(
                    shadeInteractor?.isQsExpanded ?: flowOf(false),
                    shadeDisplaysInteractor.get().displayId,
                ) { _, _ ->
                    updateStatusBarState()
                }
            } else {
                shadeInteractor?.isQsExpanded?.collect { isQsExpanded ->
                    dlog("setQsExpanded $isQsExpanded")
                synchronized(lock) { nextViewState = nextViewState.copy(qsExpanded = isQsExpanded) }
                    synchronized(lock) {
                        nextViewState = nextViewState.copy(qsExpanded = isQsExpanded)
                    }
                }
            }
        }
    }
@@ -478,8 +501,18 @@ constructor(
     */
    @GuardedBy("lock")
    private fun isShadeInQs(): Boolean {
        return (stateController.isExpanded && stateController.state == SHADE) ||
            (stateController.state == SHADE_LOCKED)
        val isShadeExpanded = (stateController.isExpanded && stateController.state == SHADE)
        val isShadeExpandedOnThisDisplay =
            if (
                StatusBarConnectedDisplays.isEnabled &&
                    ShadeWindowGoesAround.isEnabled &&
                    shadeDisplaysInteractor != null
            ) {
                isShadeExpanded && shadeDisplaysInteractor.get().displayId.value == displayId
            } else {
                isShadeExpanded
            }
        return isShadeExpandedOnThisDisplay || (stateController.state == SHADE_LOCKED)
    }

    private fun scheduleUpdate() {
@@ -612,6 +645,7 @@ constructor(
            scope: CoroutineScope,
            configurationController: ConfigurationController,
            contentInsetsProvider: StatusBarContentInsetsProvider,
            displayId: Int,
        ): PrivacyDotViewControllerImpl
    }
}
@@ -688,6 +722,7 @@ object PrivacyDotViewControllerModule {
            scope,
            configurationController,
            contentInsetsProviderStore.defaultDisplay,
            Display.DEFAULT_DISPLAY,
        )
    }
}