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

Commit 3b08060b authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge changes I6183a6c0,I49fc4207 into main

* changes:
  Prepare StatusBarContentInsetsProvider for multi display
  Create generic implementation of PerDisplayStore.
parents 7bdf2dcf 507feae5
Loading
Loading
Loading
Loading
+27 −32
Original line number Diff line number Diff line
@@ -14,23 +14,15 @@
 * limitations under the License.
 */

package com.android.systemui.statusbar.window
package com.android.systemui.display.data.repository

import android.platform.test.annotations.EnableFlags
import android.view.Display
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.app.viewcapture.mockViewCaptureAwareWindowManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.unconfinedTestDispatcher
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
@@ -43,28 +35,13 @@ import org.mockito.kotlin.mock

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

    private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
    private val testScope = kosmos.testScope
    private val fakeDisplayRepository = kosmos.displayRepository

    private val store =
        MultiDisplayStatusBarWindowControllerStore(
            backgroundApplicationScope = kosmos.applicationCoroutineScope,
            controllerFactory = kosmos.fakeStatusBarWindowControllerFactory,
            displayWindowPropertiesRepository = kosmos.fakeDisplayWindowPropertiesRepository,
            viewCaptureAwareWindowManagerFactory =
                object : ViewCaptureAwareWindowManager.Factory {
                    override fun create(
                        windowManager: WindowManager
                    ): ViewCaptureAwareWindowManager {
                        return kosmos.mockViewCaptureAwareWindowManager
                    }
                },
            displayRepository = fakeDisplayRepository,
        )
    private val store = kosmos.fakePerDisplayStore

    @Before
    fun start() {
@@ -80,34 +57,52 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() {
    @Test
    fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() =
        testScope.runTest {
            val controller = store.defaultDisplay
            val instance = store.defaultDisplay

            assertThat(store.defaultDisplay).isSameInstanceAs(controller)
            assertThat(store.defaultDisplay).isSameInstanceAs(instance)
        }

    @Test
    fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() =
        testScope.runTest {
            val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)

            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller)
            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(instance)
        }

    @Test
    fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() =
        testScope.runTest {
            val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)

            fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
            fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))

            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller)
            assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance)
        }

    @Test(expected = IllegalArgumentException::class)
    fun forDisplay_nonExistingDisplayId_throws() =
        testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }

    @Test
    fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() =
        testScope.runTest {
            val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)

            fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)

            assertThat(store.removalActions).containsExactly(instance)
        }

    @Test
    fun forDisplay_withoutDisplayRemoval_onDisplayRemovalActionIsNotInvoked() =
        testScope.runTest {
            store.forDisplay(NON_DEFAULT_DISPLAY_ID)

            assertThat(store.removalActions).isEmpty()
        }

    private fun createDisplay(displayId: Int): Display = mock {
        on { getDisplayId() } doReturn displayId
    }
+56 −57
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -135,7 +135,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -164,8 +164,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        val chipWidth = 30
        val dotWidth = 10
        val isRtl = false
        val contentRect =
            Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
        val contentRect = Rect(/* left= */ 0, /* top= */ 10, /* right= */ 1000, /* bottom= */ 100)

        val chipBounds =
            getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
@@ -207,7 +206,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -228,7 +227,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -251,7 +250,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -263,7 +262,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                minLeftPadding,
                0,
                screenBounds.height() - dcBounds.height() - dotWidth,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -278,7 +277,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -320,7 +319,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -331,7 +330,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                protectionBounds.bottom,
                0,
                screenBounds.height() - minRightPadding,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -346,7 +345,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -369,7 +368,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -381,7 +380,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                minLeftPadding,
                0,
                screenBounds.height() - protectionBounds.bottom - dotWidth,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -396,7 +395,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -415,7 +414,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = screenBounds.right - dcWidth,
                top = 0,
                right = screenBounds.right,
                bottom = dcHeight
                bottom = dcHeight,
            )
        val dcBoundsLandscape = Rect(left = 0, top = 0, right = dcHeight, bottom = dcWidth)
        val dcBoundsSeascape =
@@ -423,14 +422,14 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = screenBounds.right - dcHeight,
                top = screenBounds.bottom - dcWidth,
                right = screenBounds.right - dcHeight,
                bottom = screenBounds.bottom - dcWidth
                bottom = screenBounds.bottom - dcWidth,
            )
        val dcBoundsUpsideDown =
            Rect(
                left = 0,
                top = screenBounds.bottom - dcHeight,
                right = dcWidth,
                bottom = screenBounds.bottom - dcHeight
                bottom = screenBounds.bottom - dcHeight,
            )
        val minLeftPadding = 20
        val minRightPadding = 20
@@ -448,7 +447,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = minLeftPadding,
                top = 0,
                right = dcBoundsPortrait.left - dotWidth,
                bottom = sbHeightPortrait
                bottom = sbHeightPortrait,
            )

        var bounds =
@@ -463,7 +462,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -475,7 +474,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = dcBoundsLandscape.height(),
                top = 0,
                right = screenBounds.height() - minRightPadding,
                bottom = sbHeightLandscape
                bottom = sbHeightLandscape,
            )

        bounds =
@@ -490,7 +489,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -502,7 +501,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = minLeftPadding,
                top = 0,
                right = screenBounds.width() - minRightPadding,
                bottom = sbHeightPortrait
                bottom = sbHeightPortrait,
            )

        bounds =
@@ -517,7 +516,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -529,7 +528,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                left = minLeftPadding,
                top = 0,
                right = screenBounds.height() - minRightPadding,
                bottom = sbHeightLandscape
                bottom = sbHeightLandscape,
            )

        bounds =
@@ -544,7 +543,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -584,7 +583,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -595,7 +594,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                protectionBounds.bottom,
                0,
                screenBounds.height() - minRightPadding,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -610,7 +609,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -633,7 +632,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -645,7 +644,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                minLeftPadding,
                0,
                screenBounds.height() - protectionBounds.bottom - dotWidth,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -660,7 +659,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -682,7 +681,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl = false,
                dotWidth = 10,
                bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight = 15
                statusBarContentHeight = 15,
            )

        assertThat(bounds.top).isEqualTo(0)
@@ -704,7 +703,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl = false,
                dotWidth = 10,
                bottomAlignedMargin = 5,
                statusBarContentHeight = 15
                statusBarContentHeight = 15,
            )

        // Content in the status bar is centered vertically. To achieve the bottom margin we want,
@@ -756,7 +755,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -777,7 +776,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -798,7 +797,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -809,7 +808,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                minLeftPadding,
                0,
                screenBounds.height() - dcBounds.height() - dotWidth,
                sbHeightLandscape
                sbHeightLandscape,
            )

        bounds =
@@ -824,7 +823,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -860,7 +859,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )
        assertRects(expectedBounds, bounds, currentRotation, targetRotation)

@@ -880,7 +879,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )
        assertRects(expectedBounds, bounds, currentRotation, targetRotation)

@@ -900,7 +899,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )
        assertRects(expectedBounds, bounds, currentRotation, targetRotation)

@@ -920,7 +919,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )
        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
    }
@@ -958,7 +957,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
                isRtl,
                dotWidth,
                BOTTOM_ALIGNED_MARGIN_NONE,
                statusBarContentHeight
                statusBarContentHeight,
            )

        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -968,12 +967,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
    fun testDisplayChanged_returnsUpdatedInsets() {
        // GIVEN: get insets on the first display and switch to the second display
        val provider =
            StatusBarContentInsetsProvider(
            StatusBarContentInsetsProviderImpl(
                contextMock,
                configurationController,
                mock<DumpManager>(),
                mock<CommandRegistry>(),
                mock<SysUICutoutProvider>()
                mock<SysUICutoutProvider>(),
            )

        configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -993,12 +992,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        // GIVEN: get insets on the first display, switch to the second display,
        // get insets and switch back
        val provider =
            StatusBarContentInsetsProvider(
            StatusBarContentInsetsProviderImpl(
                contextMock,
                configurationController,
                mock<DumpManager>(),
                mock<CommandRegistry>(),
                mock<SysUICutoutProvider>()
                mock<SysUICutoutProvider>(),
            )

        configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -1024,12 +1023,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
        configurationController.onConfigurationChanged(configuration)
        val provider =
            StatusBarContentInsetsProvider(
            StatusBarContentInsetsProviderImpl(
                contextMock,
                configurationController,
                mock<DumpManager>(),
                mock<CommandRegistry>(),
                mock<SysUICutoutProvider>()
                mock<SysUICutoutProvider>(),
            )
        val listener =
            object : StatusBarContentInsetsChangedListener {
@@ -1053,12 +1052,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
    fun onDensityOrFontScaleChanged_listenerNotified() {
        configuration.densityDpi = 12
        val provider =
            StatusBarContentInsetsProvider(
            StatusBarContentInsetsProviderImpl(
                contextMock,
                configurationController,
                mock<DumpManager>(),
                mock<CommandRegistry>(),
                mock<SysUICutoutProvider>()
                mock<SysUICutoutProvider>(),
            )
        val listener =
            object : StatusBarContentInsetsChangedListener {
@@ -1081,12 +1080,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
    @Test
    fun onThemeChanged_listenerNotified() {
        val provider =
            StatusBarContentInsetsProvider(
            StatusBarContentInsetsProviderImpl(
                contextMock,
                configurationController,
                mock<DumpManager>(),
                mock<CommandRegistry>(),
                mock<SysUICutoutProvider>()
                mock<SysUICutoutProvider>(),
            )
        val listener =
            object : StatusBarContentInsetsChangedListener {
@@ -1108,13 +1107,13 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        expected: Rect,
        actual: Rect,
        @Rotation currentRotation: Int,
        @Rotation targetRotation: Int
        @Rotation targetRotation: Int,
    ) {
        assertTrue(
            "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
                " targetRotation=${RotationUtils.toString(targetRotation)}" +
                " expected=$expected actual=$actual",
            expected.equals(actual)
            expected.equals(actual),
        )
    }

@@ -1126,7 +1125,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
        left: Rect = Rect(),
        top: Rect = Rect(),
        right: Rect = Rect(),
        bottom: Rect = Rect()
        bottom: Rect = Rect(),
    ) {
        whenever(dc.boundingRects)
            .thenReturn(listOf(left, top, right, bottom).filter { !it.isEmpty })
+108 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.display.data.repository

import android.view.Display
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Background
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

/** Provides per display instances of [T]. */
interface PerDisplayStore<T> {

    /**
     * The instance for the default/main display of the device. For example, on a phone or a tablet,
     * the default display is the internal/built-in display of the device.
     *
     * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
     */
    val defaultDisplay: T

    /**
     * Returns an instance for a specific display id.
     *
     * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
     *   displays.
     */
    fun forDisplay(displayId: Int): T
}

abstract class PerDisplayStoreImpl<T>(
    @Background private val backgroundApplicationScope: CoroutineScope,
    private val displayRepository: DisplayRepository,
) : PerDisplayStore<T>, CoreStartable {

    private val perDisplayInstances = ConcurrentHashMap<Int, T>()

    /**
     * The instance for the default/main display of the device. For example, on a phone or a tablet,
     * the default display is the internal/built-in display of the device.
     *
     * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
     */
    override val defaultDisplay: T
        get() = forDisplay(Display.DEFAULT_DISPLAY)

    /**
     * Returns an instance for a specific display id.
     *
     * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
     *   displays.
     */
    override fun forDisplay(displayId: Int): T {
        if (displayRepository.getDisplay(displayId) == null) {
            throw IllegalArgumentException("Display with id $displayId doesn't exist.")
        }
        return perDisplayInstances.computeIfAbsent(displayId) {
            createInstanceForDisplay(displayId)
        }
    }

    abstract fun createInstanceForDisplay(displayId: Int): T

    override fun start() {
        val instanceType = instanceClass.simpleName
        backgroundApplicationScope.launch(CoroutineName("PerDisplayStore#<$instanceType>start")) {
            displayRepository.displayRemovalEvent.collect { removedDisplayId ->
                val removedInstance = perDisplayInstances.remove(removedDisplayId)
                removedInstance?.let { onDisplayRemovalAction(it) }
            }
        }
    }

    abstract val instanceClass: Class<T>

    /**
     * Will be called when the display associated with [instance] was removed. It allows to perform
     * any clean up if needed.
     */
    open suspend fun onDisplayRemovalAction(instance: T) {}

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println(perDisplayInstances)
    }
}

class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> {
    override val defaultDisplay: T = defaultInstance

    override fun forDisplay(displayId: Int): T = defaultDisplay
}
+17 −53

File changed.

Preview size limit exceeded, changes collapsed.

+15 −1

File changed.

Preview size limit exceeded, changes collapsed.

Loading