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

Commit 34e80be4 authored by Ale Nijamkin's avatar Ale Nijamkin
Browse files

[flexiglass] LocalStatusIconContext

Caches instances of StatusIconContainer and TintedIconManager for each scene or overlay in order to avoid the expensive work of instantiating them on each scene transition.

Bug: 410757909
Test: Manually verified that the cached items are being reused when
moving back and forth into and out of single shade scenes or dual shade
overlays
Flag: com.android.systemui.scene_container

Change-Id: I8268d734afdb08da02ee1cf956ca64d8097322dd
parent a358d721
Loading
Loading
Loading
Loading
+91 −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.shade.ui.composable

import android.view.ContextThemeWrapper
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import com.android.compose.animation.scene.ContentKey
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.ui.TintedIconManager

/**
 * Defines interface for classes that can provide a context for UI that renders the status bar
 * icons.
 */
interface StatusIconContext {

    /**
     * Returns a [StatusIconContainer] for the given [contentKey]. Note that this container will be
     * cached in the [StatusIconContext].
     */
    fun iconContainer(contentKey: ContentKey): StatusIconContainer

    /**
     * Returns a [TintedIconManager] for the given [contentKey]. Note that this manager will be
     * cached in the [StatusIconContext].
     */
    fun iconManager(contentKey: ContentKey): TintedIconManager
}

val LocalStatusIconContext =
    compositionLocalOf<StatusIconContext> { error("LocalStatusIconContext not set!") }

@Composable
fun WithStatusIconContext(
    tintedIconManagerFactory: TintedIconManager.Factory,
    block: @Composable () -> Unit,
) {
    val localContext = LocalContext.current

    val statusIconContext =
        remember(localContext, tintedIconManagerFactory) {
            object : StatusIconContext {
                private val iconContainerByContentKey =
                    mutableMapOf<ContentKey, StatusIconContainer>()
                private val iconManagerByContentKey = mutableMapOf<ContentKey, TintedIconManager>()

                override fun iconContainer(contentKey: ContentKey): StatusIconContainer {
                    return iconContainerByContentKey.getOrPut(contentKey) {
                        StatusIconContainer(
                            ContextThemeWrapper(
                                localContext,
                                R.style.Theme_SystemUI_QuickSettings_Header,
                            ),
                            null,
                        )
                    }
                }

                override fun iconManager(contentKey: ContentKey): TintedIconManager {
                    return iconManagerByContentKey.getOrPut(contentKey) {
                        tintedIconManagerFactory.create(
                            iconContainer(contentKey),
                            StatusBarLocation.QS,
                        )
                    }
                }
            }
        }

    CompositionLocalProvider(LocalStatusIconContext provides statusIconContext, content = block)
}
+4 −5
Original line number Diff line number Diff line
@@ -100,7 +100,6 @@ import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.pipeline.battery.ui.composable.BatteryWithEstimate
import com.android.systemui.statusbar.pipeline.mobile.StatusBarMobileIconKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
@@ -724,10 +723,9 @@ private fun ContentScope.StatusIcons(
    val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
    val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)

    val iconContainer = remember { StatusIconContainer(themedContext, null) }
    val iconManager = remember {
        viewModel.createTintedIconManager(iconContainer, StatusBarLocation.QS)
    }
    val statusIconContext = LocalStatusIconContext.current
    val iconContainer = statusIconContext.iconContainer(contentKey)
    val iconManager = statusIconContext.iconManager(contentKey)

    // TODO(408001821): Use composable system status icons here instead.
    AndroidView(
@@ -737,6 +735,7 @@ private fun ContentScope.StatusIcons(

            iconContainer
        },
        onRelease = { viewModel.statusBarIconController.removeIconGroup(iconManager) },
        update = { iconContainer ->
            iconContainer.setQsExpansionTransitioning(
                layoutState.isTransitioningBetween(Scenes.Shade, Scenes.QuickSettings)
+3 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.TouchLogger
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import javax.inject.Provider
import kotlinx.coroutines.flow.MutableStateFlow

@@ -34,6 +35,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
        qsSceneAdapter: Provider<QSSceneAdapter>,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        windowRootViewKeyEventHandler: WindowRootViewKeyEventHandler,
        tintedIconManagerFactory: TintedIconManager.Factory,
    ) {
        setLayoutInsetsController(layoutInsetController)
        SceneWindowRootViewBinder.bind(
@@ -53,6 +55,7 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
            dataSourceDelegator = sceneDataSourceDelegator,
            qsSceneAdapter = qsSceneAdapter,
            sceneJankMonitorFactory = sceneJankMonitorFactory,
            tintedIconManagerFactory = tintedIconManagerFactory,
        )
        setWindowRootViewKeyEventHandler(windowRootViewKeyEventHandler)
    }
+18 −11
Original line number Diff line number Diff line
@@ -50,7 +50,9 @@ import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.composable.SceneContainer
import com.android.systemui.scene.ui.viewmodel.DualShadeEducationalTooltipsViewModel
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.shade.ui.composable.WithStatusIconContext
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import javax.inject.Provider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.awaitCancellation
@@ -76,6 +78,7 @@ object SceneWindowRootViewBinder {
        dataSourceDelegator: SceneDataSourceDelegator,
        qsSceneAdapter: Provider<QSSceneAdapter>,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        tintedIconManagerFactory: TintedIconManager.Factory,
    ) {
        val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
        val sortedSceneByKey: Map<SceneKey, Scene> =
@@ -128,6 +131,7 @@ object SceneWindowRootViewBinder {
                                qsSceneAdapter = qsSceneAdapter,
                                containerConfig = containerConfig,
                                sceneJankMonitorFactory = sceneJankMonitorFactory,
                                tintedIconManagerFactory = tintedIconManagerFactory,
                            )
                            .also { it.id = R.id.scene_container_root_composable }
                    )
@@ -175,6 +179,7 @@ object SceneWindowRootViewBinder {
        qsSceneAdapter: Provider<QSSceneAdapter>,
        containerConfig: SceneContainerConfig,
        sceneJankMonitorFactory: SceneJankMonitor.Factory,
        tintedIconManagerFactory: TintedIconManager.Factory,
    ): View {
        return ComposeView(context).apply {
            setContent {
@@ -183,6 +188,7 @@ object SceneWindowRootViewBinder {
                        displayCutout = displayCutoutFromWindowInsets(scope, context, windowInsets),
                        screenCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context),
                    ) {
                        WithStatusIconContext(tintedIconManagerFactory = tintedIconManagerFactory) {
                            SceneContainer(
                                viewModel = viewModel,
                                sceneByKey = sceneByKey,
@@ -199,6 +205,7 @@ object SceneWindowRootViewBinder {
                }
            }
        }
    }

    private fun createDualShadeEducationalTooltipsView(
        scope: CoroutineScope,
+3 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificat
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.tuner.TunerService
@@ -99,6 +100,7 @@ abstract class ShadeViewProviderModule {
            blurUtils: BlurUtils,
            choreographer: Choreographer?,
            @Main mainDispatcher: CoroutineDispatcher,
            tintedIconManagerFactory: TintedIconManager.Factory,
        ): WindowRootView {
            return if (SceneContainerFlag.isEnabled) {
                checkNoSceneDuplicates(scenesProvider.get())
@@ -123,6 +125,7 @@ abstract class ShadeViewProviderModule {
                    qsSceneAdapter = qsSceneAdapter,
                    sceneJankMonitorFactory = sceneJankMonitorFactory,
                    windowRootViewKeyEventHandler = windowRootViewKeyEventHandler,
                    tintedIconManagerFactory = tintedIconManagerFactory,
                )
                sceneWindowRootView
            } else {
Loading