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

Commit 4f5ced01 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[SB] Add StatusBarContentInsetsViewModel.

A Recommended Architecture layer on top of
StatusBarContentInsetsProvider.

Bug: 364653005
Flag: EXEMPT code currently unused
Test: atest StatusBarContentInsetsViewModelTest
Change-Id: I1116517440fa5284d87e5de40e258c8736c629f4
parent d0b8249b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+77 −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.layout.ui.viewmodel

import android.content.res.Configuration
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectValues
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.layout.statusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.fake
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@SmallTest
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
class StatusBarContentInsetsViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val configuration = Configuration()

    private val Kosmos.underTest by Kosmos.Fixture { statusBarContentInsetsViewModel }

    @Test
    fun contentArea_onMaxBoundsChanged_emitsNewValue() =
        kosmos.runTest {
            statusBarContentInsetsProvider.start()

            val values by collectValues(underTest.contentArea)

            // WHEN the content area changes
            configurationController.fake.notifyLayoutDirectionChanged(isRtl = true)
            configurationController.fake.notifyDensityOrFontScaleChanged()

            // THEN the flow emits the new bounds
            assertThat(values[0]).isNotEqualTo(values[1])
        }

    @Test
    fun contentArea_onDensityOrFontScaleChanged_emitsLastBounds() =
        kosmos.runTest {
            configuration.densityDpi = 12
            statusBarContentInsetsProvider.start()

            val values by collectValues(underTest.contentArea)

            // WHEN a change happens but it doesn't affect content area
            configuration.densityDpi = 20
            configurationController.onConfigurationChanged(configuration)
            configurationController.fake.notifyDensityOrFontScaleChanged()

            // THEN it still has the last bounds
            assertThat(values).hasSize(1)
        }
}
+4 −3
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
import com.android.systemui.statusbar.layout.statusBarContentInsetsProvider
import com.android.systemui.statusbar.layout.mockStatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import com.android.systemui.statusbar.policy.BatteryController
@@ -153,7 +153,8 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
        shadeViewStateProvider = TestShadeViewStateProvider()

        Mockito.`when`(
                kosmos.statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
                kosmos.mockStatusBarContentInsetsProvider
                    .getStatusBarContentInsetsForCurrentRotation()
            )
            .thenReturn(Insets.of(0, 0, 0, 0))

@@ -162,7 +163,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
        Mockito.`when`(iconManagerFactory.create(ArgumentMatchers.any(), ArgumentMatchers.any()))
            .thenReturn(iconManager)
        Mockito.`when`(statusBarContentInsetsProviderStore.defaultDisplay)
            .thenReturn(kosmos.statusBarContentInsetsProvider)
            .thenReturn(kosmos.mockStatusBarContentInsetsProvider)
        allowTestableLooperAsMainThread()
        looper.runWithLooper {
            keyguardStatusBarView =
+7 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.policy.statusBarConfigurationController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
@@ -41,13 +42,18 @@ class StatusBarWindowControllerImplTest : SysuiTestCase() {
    private val kosmos =
        testKosmos().also { it.statusBarWindowViewInflater = it.fakeStatusBarWindowViewInflater }

    private val underTest = kosmos.statusBarWindowControllerImpl
    private lateinit var underTest: StatusBarWindowControllerImpl
    private val fakeExecutor = kosmos.fakeExecutor
    private val fakeWindowManager = kosmos.fakeWindowManager
    private val mockFragmentService = kosmos.fragmentService
    private val fakeStatusBarWindowViewInflater = kosmos.fakeStatusBarWindowViewInflater
    private val statusBarConfigurationController = kosmos.statusBarConfigurationController

    @Before
    fun setUp() {
        underTest = kosmos.statusBarWindowControllerImpl
    }

    @Test
    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
    fun attach_connectedDisplaysFlagEnabled_setsConfigControllerOnWindowView() {
+51 −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.layout.ui.viewmodel

import android.graphics.Rect
import com.android.systemui.statusbar.layout.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onStart

/** A recommended architecture version of [StatusBarContentInsetsProvider]. */
class StatusBarContentInsetsViewModel(
    private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider
) {
    /** Emits the status bar content area for the given rotation in absolute bounds. */
    val contentArea: Flow<Rect> =
        conflatedCallbackFlow {
                val listener =
                    object : StatusBarContentInsetsChangedListener {
                        override fun onStatusBarContentInsetsChanged() {
                            trySend(
                                statusBarContentInsetsProvider
                                    .getStatusBarContentAreaForCurrentRotation()
                            )
                        }
                    }
                statusBarContentInsetsProvider.addCallback(listener)
                awaitClose { statusBarContentInsetsProvider.removeCallback(listener) }
            }
            .onStart {
                emit(statusBarContentInsetsProvider.getStatusBarContentAreaForCurrentRotation())
            }
            .distinctUntilChanged()
}
Loading