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

Commit ebd045fc authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Perform clean up on status bar Orchestrator and Privacy dot classes on...

Merge "Perform clean up on status bar Orchestrator and Privacy dot classes on PerDisplayStoreImpl#onDisplayRemovalAction signal" into main
parents cf8510c8 974b6631
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.core

import android.platform.test.annotations.EnableFlags
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
class MultiDisplayStatusBarOrchestratorStoreTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val fakeDisplayRepository = kosmos.displayRepository
    private val underTest by lazy { kosmos.multiDisplayStatusBarOrchestratorStore }

    @Before
    fun start() {
        underTest.start()
    }

    @Before fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY) }

    @Test
    fun displayRemoved_stopsInstance() =
        testScope.runTest {
            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!

            fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)

            verify(instance).stop()
        }
}
+11 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.DarkIconDispatcher
@@ -70,6 +71,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
    private val mockBubbles = kosmos.bubbles
    private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
    private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
    private val dumpManager = kosmos.dumpManager

    private val orchestrator = kosmos.statusBarOrchestrator

@@ -291,6 +293,15 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
                .transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
        }

    @Test
    fun stop_unregisterDumpable() {
        orchestrator.start()

        orchestrator.stop()

        verify(dumpManager).unregisterDumpable("StatusBarOrchestrator")
    }

    private fun putDeviceToSleep() {
        fakePowerRepository.updateWakefulness(
            rawState = WakefulnessState.ASLEEP,
+21 −2
Original line number Diff line number Diff line
@@ -22,24 +22,29 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.testKosmos
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
class PrivacyDotWindowControllerStoreImplTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val underTest by lazy { kosmos.privacyDotWindowControllerStoreImpl }

    @Before
    fun installDisplays() = runBlocking {
        underTest.start()
        kosmos.displayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY)
        kosmos.displayRepository.addDisplay(displayId = Display.DEFAULT_DISPLAY + 1)
        kosmos.displayRepository.addDisplay(displayId = DISPLAY_2)
    }

    @Test(expected = IllegalArgumentException::class)
@@ -49,6 +54,20 @@ class PrivacyDotWindowControllerStoreImplTest : SysuiTestCase() {

    @Test
    fun forDisplay_nonDefaultDisplay_doesNotThrow() {
        underTest.forDisplay(displayId = Display.DEFAULT_DISPLAY + 1)
        underTest.forDisplay(displayId = DISPLAY_2)
    }

    @Test
    fun displayRemoved_stopsInstance() =
        testScope.runTest {
            val instance = underTest.forDisplay(DISPLAY_2)!!

            kosmos.displayRepository.removeDisplay(DISPLAY_2)

            verify(instance).stop()
        }

    private companion object {
        const val DISPLAY_2 = Display.DEFAULT_DISPLAY + 1
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -173,6 +173,17 @@ class PrivacyDotWindowControllerTest : SysuiTestCase() {
        expect.that(gravityForView(viewController.bottomRight!!)).isEqualTo(BOTTOM or LEFT)
    }

    @Test
    fun onStop_removeAllWindows() {
        underTest.start()
        executor.runAllReady()

        underTest.stop()
        executor.runAllReady()

        assertThat(windowManager.addedViews).isEmpty()
    }

    private fun paramsForView(view: View): WindowManager.LayoutParams {
        return windowManager.addedViews.entries
            .first { it.key == view || it.key.findViewById<View>(view.id) != null }
+99 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.core

import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayScopeRepository
import com.android.systemui.display.data.repository.PerDisplayStoreImpl
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.phone.AutoHideControllerStore
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope

/** [PerDisplayStoreImpl] for providing display specific [StatusBarOrchestrator]. */
@SysUISingleton
class MultiDisplayStatusBarOrchestratorStore
@Inject
constructor(
    @Background backgroundApplicationScope: CoroutineScope,
    displayRepository: DisplayRepository,
    private val factory: StatusBarOrchestrator.Factory,
    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
    private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
    private val initializerStore: StatusBarInitializerStore,
    private val autoHideControllerStore: AutoHideControllerStore,
    private val displayScopeRepository: DisplayScopeRepository,
    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
) : PerDisplayStoreImpl<StatusBarOrchestrator>(backgroundApplicationScope, displayRepository) {

    init {
        StatusBarConnectedDisplays.assertInNewMode()
    }

    override fun createInstanceForDisplay(displayId: Int): StatusBarOrchestrator? {
        val statusBarModeRepository =
            statusBarModeRepositoryStore.forDisplay(displayId) ?: return null
        val statusBarInitializer = initializerStore.forDisplay(displayId) ?: return null
        val statusBarWindowController =
            statusBarWindowControllerStore.forDisplay(displayId) ?: return null
        val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return null
        return factory.create(
            displayId,
            // TODO: b/398825844 - Handle nullness to prevent leaking CoroutineScope.
            displayScopeRepository.scopeForDisplay(displayId),
            statusBarWindowStateRepositoryStore.forDisplay(displayId),
            statusBarModeRepository,
            statusBarInitializer,
            statusBarWindowController,
            autoHideController,
        )
    }

    override val instanceClass = StatusBarOrchestrator::class.java

    override suspend fun onDisplayRemovalAction(instance: StatusBarOrchestrator) {
        instance.stop()
    }
}

@Module
interface MultiDisplayStatusBarOrchestratorStoreModule {

    @Provides
    @SysUISingleton
    @IntoMap
    @ClassKey(MultiDisplayStatusBarOrchestratorStore::class)
    fun storeAsCoreStartable(
        multiDisplayLazy: Lazy<MultiDisplayStatusBarOrchestratorStore>
    ): CoreStartable {
        return if (StatusBarConnectedDisplays.isEnabled) {
            multiDisplayLazy.get()
        } else {
            CoreStartable.NOP
        }
    }
}
Loading