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

Commit a20b444e authored by Priyanka Advani (xWF)'s avatar Priyanka Advani (xWF) Committed by Android (Google) Code Review
Browse files

Merge changes from topic "revert-34529017-GCHFXOAUZF" into main

* changes:
  Revert "cleanup: Rename and clarify some status bar icon tint me..."
  Revert "fix: Fix the battery icon using the wrong color in split..."
parents 0399edae d119cca9
Loading
Loading
Loading
Loading
+0 −10
Original line number Diff line number Diff line
@@ -481,16 +481,6 @@ flag {
    bug: "365120736"
}

flag {
    name: "status_bar_dark_icon_interactor_mixed_fix"
    namespace: "systemui"
    description: "Fix DarkIconInteractor#toIsAreaDark result for mixed status bar lightness, for icons that do not overlap with the dark icon area."
    bug: "423390143"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "icon_refresh_2025"
    namespace: "systemui"
+0 −109
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.phone.domain.interactor

import android.graphics.Rect
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
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.phone.SysuiDarkIconDispatcher.DarkChange
import com.android.systemui.statusbar.phone.data.repository.fakeDarkIconRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@android.platform.test.annotations.EnabledOnRavenwood
class DarkIconInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val Kosmos.underTest by Kosmos.Fixture { kosmos.darkIconInteractor }

    @Test
    fun isDarkTheme_noDarkIconAreas_darkIconIntensityLightIcons_isDarkTheme() =
        kosmos.runTest {
            val isAreaDark by collectLastValue(underTest.isAreaDark(DEFAULT_DISPLAY_ID))
            val iconBounds = Rect(10, 10, 20, 20)

            fakeDarkIconRepository.darkState(DEFAULT_DISPLAY_ID).value =
                DarkChange(arrayListOf(), DARK_INTENSITY_LIGHT_ICONS, TINT)

            // The dark icon areas list is empty, which means the entire status bar is one color.
            // The status bar is requesting light icons, so this icon should be light,
            // which means the theme should be dark.
            assertThat(isAreaDark!!.isDarkTheme(iconBounds)).isTrue()
        }

    @Test
    fun isDarkTheme_noDarkIconAreas_darkIconIntensityDarkIcons_isLightTheme() =
        kosmos.runTest {
            val isAreaDark by collectLastValue(underTest.isAreaDark(DEFAULT_DISPLAY_ID))
            val iconBounds = Rect(10, 10, 20, 20)

            fakeDarkIconRepository.darkState(DEFAULT_DISPLAY_ID).value =
                DarkChange(arrayListOf(), DARK_INTENSITY_DARK_ICONS, TINT)

            // The dark icon areas list is empty, which means the entire status bar is one color.
            // The status bar is requesting dark icons, so this icon should be dark,
            // which means the theme should be light.
            assertThat(isAreaDark!!.isDarkTheme(iconBounds)).isFalse()
        }

    @Test
    fun isDarkTheme_nonEmptyDarkIconAreas_iconInDarkIconArea_isLightTheme() =
        kosmos.runTest {
            val isAreaDark by collectLastValue(underTest.isAreaDark(DEFAULT_DISPLAY_ID))
            val darkIconArea = Rect(0, 0, 100, 100)
            val iconBounds = Rect(10, 10, 20, 20)

            fakeDarkIconRepository.darkState(DEFAULT_DISPLAY_ID).value =
                DarkChange(arrayListOf(darkIconArea), DARK_INTENSITY_DARK_ICONS, TINT)

            // The icon is within the dark icon area, so the icon should be dark,
            // which means the theme should be light.
            assertThat(isAreaDark!!.isDarkTheme(iconBounds)).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_DARK_ICON_INTERACTOR_MIXED_FIX)
    fun isDarkTheme_nonEmptyDarkIconAreas_iconOutsideDarkIconArea_isDarkTheme() =
        kosmos.runTest {
            val isAreaDark by collectLastValue(underTest.isAreaDark(DEFAULT_DISPLAY_ID))
            val darkIconArea = Rect(0, 0, 100, 100)
            val iconBounds = Rect(110, 10, 120, 20)

            fakeDarkIconRepository.darkState(DEFAULT_DISPLAY_ID).value =
                DarkChange(arrayListOf(darkIconArea), DARK_INTENSITY_DARK_ICONS, TINT)

            // The icon is not within the dark icon area, so the icon should be light,
            // which means the theme should be dark.
            assertThat(isAreaDark!!.isDarkTheme(iconBounds)).isTrue()
        }

    companion object {
        private const val DEFAULT_DISPLAY_ID = 0
        private const val TINT = 0
        private const val DARK_INTENSITY_DARK_ICONS = 1f
        private const val DARK_INTENSITY_LIGHT_ICONS = 0f
    }
}
+0 −6
Original line number Diff line number Diff line
@@ -55,13 +55,7 @@ public interface SysuiDarkIconDispatcher extends DarkIconDispatcher, Dumpable {
            this.tint = tint;
        }

        /**
         * Regions where icons should be dark. Icons outside of these regions should be light.
         * Note: This is empty if all icons should be the same color, regardless of light or dark.
         * This is only non-empty when some icons should be light and others dark.
         */
        public final Collection<Rect> areas;
        /** 0.0f = white icons, 1.0f = black icons */
        public final float darkIntensity;
        public final int tint;
    }
+8 −41
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone.domain.interactor

import android.graphics.Rect
import com.android.systemui.Flags.statusBarDarkIconInteractorMixedFix
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
@@ -37,7 +36,7 @@ class DarkIconInteractor @Inject constructor(private val repository: DarkIconRep

    /**
     * Given a display id: returns a flow of [IsAreaDark], a function that can tell you if a given
     * [Rect] should be tinted dark theme or not. This flow ignores [DarkChange.tint] and
     * [Rect] should be tinted dark or not. This flow ignores [DarkChange.tint] and
     * [DarkChange.darkIntensity]
     */
    fun isAreaDark(displayId: Int): Flow<IsAreaDark> {
@@ -52,59 +51,27 @@ class DarkIconInteractor @Inject constructor(private val repository: DarkIconRep
        @JvmStatic
        fun Flow<DarkChange>.toIsAreaDark(): Flow<IsAreaDark> =
            map { darkChange ->
                    // Note: DarkChange.darkIntensity is 0.0f when icons should be white (for dark
                    // theme) and 1.0f when icons should be black (for light theme).
                    DarkStateWithoutIntensity(
                        darkChange.areas,
                        isDarkTheme = darkChange.darkIntensity < 0.5f,
                    )
                    DarkStateWithoutIntensity(darkChange.areas, darkChange.darkIntensity < 0.5f)
                }
                .distinctUntilChanged()
                .map { darkState ->
                    IsAreaDark { viewBounds: Rect ->
                        if (DarkIconDispatcher.isInAreas(darkState.darkIconAreas, viewBounds)) {
                            /*
                            This path happens in the following situations:
                            1. The status bar is all one color: dark theme.
                            2. The status bar is all one color: light theme.
                            3. The status bar is half light and half dark, and the provided
                               viewBounds overlaps the dark icon area meaning that these icons
                               should be dark. In this situation darkState.isDarkTheme is always
                               false.

                            In all these cases the icon theme in this region should match the
                            status bar theme, so return the status bar theme.
                            */
                            darkState.isDarkTheme
                        } else {
                            /*
                            This path happens when the status bar is half light and half dark,
                            and the provided viewBounds do *not* overlap the dark icon area meaning
                            that these icons should be light.

                            In this case the icon theme should always be dark so that callers
                            use light icons.
                            */
                            if (statusBarDarkIconInteractorMixedFix()) {
                                true
                        if (DarkIconDispatcher.isInAreas(darkState.areas, viewBounds)) {
                            darkState.isDark
                        } else {
                            false
                        }
                    }
                }
                }
                .conflate()
                .distinctUntilChanged()
    }
}

/** So we can map between [DarkState] and a single boolean, but based on intensity */
private data class DarkStateWithoutIntensity(
    val darkIconAreas: Collection<Rect>,
    val isDarkTheme: Boolean,
)
private data class DarkStateWithoutIntensity(val areas: Collection<Rect>, val isDark: Boolean)

/** Given a region on screen, determine if content in this region should use dark or light theme */
/** Given a region on screen, determine if the foreground should be dark or light */
fun interface IsAreaDark {
    fun isDarkTheme(viewBounds: Rect): Boolean
    fun isDark(viewBounds: Rect): Boolean
}
+2 −2
Original line number Diff line number Diff line
@@ -83,7 +83,7 @@ fun BatteryWithChargeStatus(
        val path = viewModel.batteryFrame

        val colorProvider = {
            if (isDarkProvider().isDarkTheme(bounds)) {
            if (isDarkProvider().isDark(bounds)) {
                viewModel.colorProfile.dark
            } else {
                viewModel.colorProfile.light
@@ -105,7 +105,7 @@ fun BatteryWithChargeStatus(
        if (shouldShowPercent(showPercentMode, viewModel)) {
            // The text can just use the Default.fill color, since we don't want to colorize it
            val colorProducer = {
                if (isDarkProvider().isDarkTheme(bounds)) {
                if (isDarkProvider().isDark(bounds)) {
                    BatteryColors.DarkTheme.Default.fill
                } else {
                    BatteryColors.LightTheme.Default.fill
Loading