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

Commit abed4032 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[SB] Add flag to always use color sampling for status bar.

In Idf6d34840ee2bc9bca1ca0d0c44ad930ed6989cb, we added the ability to
use color sampling for the status bar when the dark theme accessibility
feature is enabled (see b/379760792). But, we've been wanting to do
color sampling in the status bar for all users all the time, so that we
don't have apps incorrectly setting the status bar color and making it
invisible.

This CL adds a flag that will enable it at all times. There's still a
lot of testing with various apps & performance testing to be done, but
adding the flag will let us start that sort of testing.

Bug: 365120736
Flag: com.android.systemui.status_bar_always_use_region_sampling
Test: atest StatusBarRegionSamplingInteractorTest
Test: create demo app that can set area behind status bar to any color
-> verify status bar automatically changes between light & dark icons
based on color (see videos in bug)

Change-Id: Ie4add4e96b500718252dbb7d9baff0b5cb012c9e
parent 7f0e1c36
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -475,6 +475,13 @@ flag {
    }
}

flag {
    name: "status_bar_always_use_region_sampling"
    namespace: "systemui"
    description: "Have the status bar *always* use region sampling to calculate icon contrast"
    bug: "365120736"
}

flag {
    name: "icon_refresh_2025"
    namespace: "systemui"
+38 −3
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.internal.view.AppearanceRegion
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.StatusBarAlwaysUseRegionSampling
import com.android.systemui.statusbar.StatusBarRegionSampling
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.data.model.StatusBarMode
@@ -404,7 +405,8 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun statusBarAppearance_sampledAvailable_usesSampledAppearance() =
    @DisableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun statusBarAppearance_a11ySamplingFlagOn_usesSampledAppearance() =
        testScope.runTest {
            val latest by collectLastValue(underTest.statusBarAppearance)

@@ -419,8 +421,25 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
        }

    @Test
    @EnableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun statusBarAppearance_sampledAvailable_flagDisabled_usesDisplayPolicyProvidedAppearance() =
    fun statusBarAppearance_alwaysSamplingFlagOn_usesSampledAppearance() =
        testScope.runTest {
            val latest by collectLastValue(underTest.statusBarAppearance)

            underTest.setSampledAppearanceRegions(SAMPLED_APPEARANCE_REGIONS)
            onSystemBarAttributesChanged(
                appearance = APPEARANCE,
                appearanceRegions = APPEARANCE_REGIONS.toTypedArray(),
                letterboxDetails = emptyArray(),
            )

            assertThat(latest!!.appearanceRegions).isEqualTo(SAMPLED_APPEARANCE_REGIONS)
        }

    @Test
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME, StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun statusBarAppearance_bothFlagsDisabled_sampledAvailable_usesDisplayPolicyProvidedAppearance() =
        testScope.runTest {
            val latest by collectLastValue(underTest.statusBarAppearance)

@@ -436,7 +455,23 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun statusBarAppearance_sampledUnavailable_usesDisplayPolicyProvidedAppearance() =
    fun statusBarAppearance_a11yFlagEnabled_sampledUnavailable_usesDisplayPolicyProvidedAppearance() =
        testScope.runTest {
            val latest by collectLastValue(underTest.statusBarAppearance)

            underTest.setSampledAppearanceRegions(listOf())
            onSystemBarAttributesChanged(
                appearance = APPEARANCE,
                appearanceRegions = APPEARANCE_REGIONS.toTypedArray(),
                letterboxDetails = emptyArray(),
            )

            assertThat(latest!!.appearanceRegions).isEqualTo(APPEARANCE_REGIONS)
        }

    @Test
    @EnableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun statusBarAppearance_alwaysFlagEnabled_sampledUnavailable_usesDisplayPolicyProvidedAppearance() =
        testScope.runTest {
            val latest by collectLastValue(underTest.statusBarAppearance)

+63 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.statusbar.StatusBarAlwaysUseRegionSampling
import com.android.systemui.statusbar.StatusBarRegionSampling
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.testKosmos
@@ -44,7 +45,8 @@ class StatusBarRegionSamplingInteractorTest : SysuiTestCase() {
    private val Kosmos.underTest by Kosmos.Fixture { kosmos.statusBarRegionSamplingInteractor }

    @Test
    fun isRegionSamplingEnabled_forceInvertOff_returnsFalse() =
    @DisableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_alwaysUseFlagOff_forceInvertOff_returnsFalse() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isRegionSamplingEnabled)

@@ -55,7 +57,8 @@ class StatusBarRegionSamplingInteractorTest : SysuiTestCase() {

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_forceInvertDark_flagEnabled_returnsTrue() =
    @DisableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_forceInvertDark_onlyA11yFlagEnabled_returnsTrue() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isRegionSamplingEnabled)

@@ -65,8 +68,8 @@ class StatusBarRegionSamplingInteractorTest : SysuiTestCase() {
        }

    @Test
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_forceInvertDark_flagDisabled_returnsFalse() =
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME, StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_forceInvertDark_bothFlagsDisabled_returnsFalse() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isRegionSamplingEnabled)

@@ -76,7 +79,29 @@ class StatusBarRegionSamplingInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME)
    @EnableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_alwaysUseFlagEnabled_forceInvertDark_returnsTrue() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isRegionSamplingEnabled)

            fakeForceInvertRepository.setForceInvertDark(true)

            assertThat(latest).isTrue()
        }

    @Test
    @EnableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun isRegionSamplingEnabled_alwaysUseFlagEnabled_forceInvertOff_returnsTrue() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isRegionSamplingEnabled)

            fakeForceInvertRepository.setForceInvertDark(false)

            assertThat(latest).isTrue()
        }

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME, StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun setSampledAppearanceRegions_propagatesNonNullToStatusBarModeRepository() =
        kosmos.runTest {
            val firstRegion = AppearanceRegion(0, Rect())
@@ -91,8 +116,40 @@ class StatusBarRegionSamplingInteractorTest : SysuiTestCase() {
        }

    @Test
    @EnableFlags(StatusBarRegionSampling.FLAG_NAME)
    @DisableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun setSampledAppearanceRegions_onlyA11yFlagEnabled_propagates() =
        kosmos.runTest {
            val firstRegion = AppearanceRegion(0, Rect())
            val secondRegion = null
            val thirdRegion = AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect())
            val appearanceRegions = listOf(firstRegion, secondRegion, thirdRegion)

            underTest.setSampledAppearanceRegions(Display.DEFAULT_DISPLAY, appearanceRegions)

            assertThat(fakeStatusBarModeRepository.defaultDisplay.fakeSampledAppearanceRegions)
                .containsExactly(firstRegion, thirdRegion)
        }

    @Test
    @EnableFlags(StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME)
    fun setSampledAppearanceRegions_flagDisabled_doesNothing() =
    fun setSampledAppearanceRegions_onlyAlwaysFlagEnabled_propagates() =
        kosmos.runTest {
            val firstRegion = AppearanceRegion(0, Rect())
            val secondRegion = null
            val thirdRegion = AppearanceRegion(APPEARANCE_LIGHT_STATUS_BARS, Rect())
            val appearanceRegions = listOf(firstRegion, secondRegion, thirdRegion)

            underTest.setSampledAppearanceRegions(Display.DEFAULT_DISPLAY, appearanceRegions)

            assertThat(fakeStatusBarModeRepository.defaultDisplay.fakeSampledAppearanceRegions)
                .containsExactly(firstRegion, thirdRegion)
        }

    @Test
    @DisableFlags(StatusBarRegionSampling.FLAG_NAME, StatusBarAlwaysUseRegionSampling.FLAG_NAME)
    fun setSampledAppearanceRegions_bothFlagsDisabled_doesNothing() =
        kosmos.runTest {
            val region = AppearanceRegion(0, Rect())

+45 −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

import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken

/**
 * Helper for reading or using the status bar always use region sampling flag state.
 *
 * This flag enables region sampling for *all* users, regardless of accessibility settings. See
 * also: [StatusBarRegionSampling].
 */
@Suppress("NOTHING_TO_INLINE")
object StatusBarAlwaysUseRegionSampling {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_ALWAYS_USE_REGION_SAMPLING

    /** A token used for dependency declaration */
    val token: FlagToken
        get() = FlagToken(FLAG_NAME, isEnabled)

    /** Is the refactor enabled */
    @JvmStatic
    inline val isEnabled
        get() = Flags.statusBarAlwaysUseRegionSampling()

    /** Returns true if any kind of region sampling is enabled. */
    inline val isAnyRegionSamplingEnabled
        get() = isEnabled || StatusBarRegionSampling.isEnabled
}
+6 −1
Original line number Diff line number Diff line
@@ -20,7 +20,12 @@ import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils

/** Helper for reading or using the status bar region sampling flag state. */
/**
 * Helper for reading or using the status bar region sampling flag state.
 *
 * This flag enables region sampling if users have the force-dark-theme accessibility setting on.
 * See also: [StatusBarAlwaysUseRegionSampling].
 */
@Suppress("NOTHING_TO_INLINE")
object StatusBarRegionSampling {
    /** The aconfig flag name */
Loading