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

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

Merge "[Dual Shade] Change Dual Shade logic around screen width." into main

parents d4ae8056 b90a66af
Loading
Loading
Loading
Loading
+67 −5
Original line number Diff line number Diff line
@@ -14,8 +14,10 @@
 * limitations under the License.
 */

package com.android.systemui.keyguard.data.repository
package com.android.systemui.biometrics.data.repository

import android.content.res.Configuration
import android.util.DisplayMetrics
import android.util.Size
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
@@ -24,8 +26,6 @@ import android.view.Surface
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.DisplayStateRepository
import com.android.systemui.biometrics.data.repository.DisplayStateRepositoryImpl
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
@@ -35,6 +35,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -44,13 +45,15 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class DisplayStateRepositoryTest : SysuiTestCase() {
class DisplayStateRepositoryImplTest : SysuiTestCase() {
    private val display = mock<Display>()
    private val testScope = TestScope(StandardTestDispatcher())
    private val fakeDeviceStateRepository = FakeDeviceStateRepository()
    private val fakeDisplayRepository = FakeDisplayRepository()
    private val configuration = Configuration()

    private lateinit var underTest: DisplayStateRepository

@@ -58,11 +61,16 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
    fun setUp() {
        mContext.orCreateTestableResources.addOverride(
            com.android.internal.R.bool.config_reverseDefaultRotation,
            false
            false,
        )

        // Set densityDpi such that pixels and DP are the same; Makes it easier to read and write
        // tests.
        configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT

        mContext = spy(mContext)
        whenever(mContext.display).thenReturn(display)
        whenever(mContext.resources.configuration).thenReturn(configuration)

        underTest =
            DisplayStateRepositoryImpl(
@@ -137,4 +145,58 @@ class DisplayStateRepositoryTest : SysuiTestCase() {
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(currentSize).isEqualTo(Size(200, 100))
        }

    @Test
    fun updatesIsLargeScreen_whenDisplayStateChanges() =
        testScope.runTest {
            val isLargeScreen by collectLastValue(underTest.isLargeScreen)
            runCurrent()

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_0
                info.logicalWidth = 100
                info.logicalHeight = 700
                return@then true
            }
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(isLargeScreen).isFalse()

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_0
                info.logicalWidth = 800
                info.logicalHeight = 700
                return@then true
            }
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(isLargeScreen).isTrue()
        }

    @Test
    fun updatesIsWideScreen_whenDisplayStateChanges() =
        testScope.runTest {
            val isWideScreen by collectLastValue(underTest.isWideScreen)
            runCurrent()

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_0
                info.logicalWidth = 200
                info.logicalHeight = 700
                return@then true
            }
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(isWideScreen).isFalse()

            whenever(display.getDisplayInfo(any())).then {
                val info = it.getArgument<DisplayInfo>(0)
                info.rotation = Surface.ROTATION_90
                info.logicalWidth = 700
                info.logicalHeight = 200
                return@then true
            }
            fakeDisplayRepository.emitDisplayChangeEvent(DEFAULT_DISPLAY)
            assertThat(isWideScreen).isTrue()
        }
}
+11 −8
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.displayStateRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
@@ -37,7 +36,9 @@ import com.android.systemui.qs.QSImpl
import com.android.systemui.qs.dagger.QSComponent
import com.android.systemui.qs.dagger.QSSceneComponent
import com.android.systemui.settings.brightness.MirrorController
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
@@ -50,6 +51,7 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -61,6 +63,7 @@ import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.verify

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class QSSceneAdapterImplTest : SysuiTestCase() {
@@ -550,16 +553,17 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
    @Test
    fun dispatchSplitShade() =
        testScope.runTest {
            val shadeRepository = kosmos.fakeShadeRepository
            shadeRepository.setShadeLayoutWide(false)
            val qsImpl by collectLastValue(underTest.qsImpl)

            kosmos.enableSingleShade()
            runCurrent()

            underTest.inflate(context)
            runCurrent()

            verify(qsImpl!!).setInSplitShade(false)

            shadeRepository.setShadeLayoutWide(true)
            kosmos.enableSplitShade()
            runCurrent()
            verify(qsImpl!!).setInSplitShade(true)
        }
@@ -581,11 +585,10 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
        testScope.runTest {
            val qsImpl by collectLastValue(underTest.qsImpl)

            underTest.inflate(context)
            kosmos.enableDualShade(wideLayout = true)
            runCurrent()

            kosmos.displayStateRepository.setIsLargeScreen(true)
            runCurrent()
            underTest.inflate(context)

            verify(qsImpl!!).setIsNotificationPanelFullWidth(false)

+1 −1
Original line number Diff line number Diff line
@@ -214,7 +214,7 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
        testScope.runTest {
            underTest.start()

            kosmos.displayStateRepository.setIsLargeScreen(true)
            kosmos.displayStateRepository.setIsWideScreen(true)
            runCurrent()
            verify(kosmos.notificationStackScrollLayoutController).setIsFullWidth(false)
            assertThat(kosmos.scrimController.clipQsScrim).isFalse()
+40 −34
Original line number Diff line number Diff line
@@ -56,8 +56,19 @@ interface DisplayStateRepository {
    /** Provides the current display size */
    val currentDisplaySize: StateFlow<Size>

    /** Provides whether the current display is large screen */
    /**
     * Provides whether the current display is a large screen (i.e. all edges are >= 600dp). This is
     * agnostic of display rotation.
     */
    val isLargeScreen: StateFlow<Boolean>

    /**
     * Provides whether the display's current horizontal width is large (>= 600dp).
     *
     * Note that unlike [isLargeScreen], which checks whether either one of the screen's width or
     * height is large, this flow's state is sensitive to the current display's orientation.
     */
    val isWideScreen: StateFlow<Boolean>
}

@SysUISingleton
@@ -75,17 +86,7 @@ constructor(
    override val isInRearDisplayMode: StateFlow<Boolean> =
        deviceStateRepository.state
            .map { it == REAR_DISPLAY }
            .stateIn(
                backgroundScope,
                started = SharingStarted.Eagerly,
                initialValue = false,
            )

    private fun getDisplayInfo(): DisplayInfo {
        val cachedDisplayInfo = DisplayInfo()
        context.display?.getDisplayInfo(cachedDisplayInfo)
        return cachedDisplayInfo
    }
            .stateIn(backgroundScope, started = SharingStarted.Eagerly, initialValue = false)

    private val currentDisplayInfo: StateFlow<DisplayInfo> =
        displayRepository.displayChangeEvent
@@ -96,21 +97,13 @@ constructor(
                initialValue = getDisplayInfo(),
            )

    private fun rotationToDisplayRotation(rotation: Int): DisplayRotation {
        var adjustedRotation = rotation
        if (isReverseDefaultRotation) {
            adjustedRotation = (rotation + 1) % 4
        }
        return adjustedRotation.toDisplayRotation()
    }

    override val currentRotation: StateFlow<DisplayRotation> =
        currentDisplayInfo
            .map { rotationToDisplayRotation(it.rotation) }
            .stateIn(
                backgroundScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = rotationToDisplayRotation(currentDisplayInfo.value.rotation)
                initialValue = rotationToDisplayRotation(currentDisplayInfo.value.rotation),
            )

    override val currentDisplaySize: StateFlow<Size> =
@@ -122,7 +115,7 @@ constructor(
                initialValue =
                    Size(
                        currentDisplayInfo.value.naturalWidth,
                        currentDisplayInfo.value.naturalHeight
                        currentDisplayInfo.value.naturalHeight,
                    ),
            )

@@ -130,22 +123,35 @@ constructor(
        currentDisplayInfo
            .map {
                // copied from systemui/shared/...Utilities.java
                val smallestWidth =
                    dpiFromPx(
                        min(it.logicalWidth, it.logicalHeight).toFloat(),
                        context.resources.configuration.densityDpi
                    )
                val smallestWidth = min(it.logicalWidth, it.logicalHeight).toDpi()
                smallestWidth >= LARGE_SCREEN_MIN_DPS
            }
            .stateIn(
                backgroundScope,
                started = SharingStarted.Eagerly,
                initialValue = false,
            )
            .stateIn(backgroundScope, started = SharingStarted.Eagerly, initialValue = false)

    override val isWideScreen: StateFlow<Boolean> =
        currentDisplayInfo
            .map { it.logicalWidth.toDpi() >= LARGE_SCREEN_MIN_DPS }
            .stateIn(backgroundScope, started = SharingStarted.Eagerly, initialValue = false)

    private fun getDisplayInfo(): DisplayInfo {
        val cachedDisplayInfo = DisplayInfo()
        context.display.getDisplayInfo(cachedDisplayInfo)
        return cachedDisplayInfo
    }

    private fun rotationToDisplayRotation(rotation: Int): DisplayRotation {
        return if (isReverseDefaultRotation) {
                (rotation + 1) % 4
            } else {
                rotation
            }
            .toDisplayRotation()
    }

    private fun dpiFromPx(size: Float, densityDpi: Int): Float {
    private fun Int.toDpi(): Float {
        val densityDpi = context.resources.configuration.densityDpi
        val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
        return size / densityRatio
        return this / densityRatio
    }

    companion object {
+26 −16
Original line number Diff line number Diff line
@@ -21,12 +21,12 @@ import android.content.res.Configuration
import com.android.systemui.biometrics.data.repository.DisplayStateRepository
import com.android.systemui.biometrics.shared.model.DisplayRotation
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
import com.android.systemui.unfold.updates.FoldProvider
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn

/** Aggregates display state information. */
// TODO(b/411335091): Move to com.android.systemui.display.domain.interactor.
interface DisplayStateInteractor {
    /** Whether the default display is currently off. */
    val isDefaultDisplayOff: Flow<Boolean>
@@ -65,8 +66,19 @@ interface DisplayStateInteractor {
    /** Called on configuration changes, used to keep the display state in sync */
    fun onConfigurationChanged(newConfig: Configuration)

    /** Provides whether the current display is large screen */
    /**
     * Provides whether the current display is a large screen (i.e. all edges are >= 600dp). This is
     * agnostic of display rotation.
     */
    val isLargeScreen: StateFlow<Boolean>

    /**
     * Provides whether the display's current horizontal width is large (>= 600dp).
     *
     * Note: Unlike [isLargeScreen], which checks whether either one of the screen's width or height
     * is large, this flow's state is sensitive to the current display's rotation.
     */
    val isWideScreen: StateFlow<Boolean>
}

/** Encapsulates logic for interacting with the display state. */
@@ -81,10 +93,6 @@ constructor(
) : DisplayStateInteractor {
    private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)

    fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) {
        screenSizeFoldProvider = foldProvider
    }

    override val displayChanges = displayRepository.displayChangeEvent

    override val isFolded: Flow<Boolean> =
@@ -93,7 +101,7 @@ constructor(
                    trySendWithFailureLogging(
                        state,
                        TAG,
                        "Error sending fold state update to $state"
                        "Error sending fold state update to $state",
                    )
                }

@@ -108,11 +116,7 @@ constructor(
                screenSizeFoldProvider.registerCallback(callback, mainExecutor)
                awaitClose { screenSizeFoldProvider.unregisterCallback(callback) }
            }
            .stateIn(
                applicationScope,
                started = SharingStarted.Eagerly,
                initialValue = false,
            )
            .stateIn(applicationScope, started = SharingStarted.Eagerly, initialValue = false)

    override val isInRearDisplayMode: StateFlow<Boolean> =
        displayStateRepository.isInRearDisplayMode
@@ -122,14 +126,20 @@ constructor(

    override val isReverseDefaultRotation: Boolean = displayStateRepository.isReverseDefaultRotation

    override fun onConfigurationChanged(newConfig: Configuration) {
        screenSizeFoldProvider.onConfigurationChange(newConfig)
    }

    override val isDefaultDisplayOff = displayRepository.defaultDisplayOff

    override val isLargeScreen: StateFlow<Boolean> = displayStateRepository.isLargeScreen

    override val isWideScreen: StateFlow<Boolean> = displayStateRepository.isWideScreen

    fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) {
        screenSizeFoldProvider = foldProvider
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        screenSizeFoldProvider.onConfigurationChange(newConfig)
    }

    companion object {
        private const val TAG = "DisplayStateInteractor"
    }
Loading