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

Commit 830dd1ae authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Cache the movable content used for Status icons

Caching the movable content based on the TintedIconManager helps by
making sure there's a single AndroidView (and a single registration) for
that TintedIconManager.

This prevents double registering (for example, when switching from
ExpandedShadeHeader to CollapsedShadeHeader in the same scene).

Test: manual, landscape, unfold, fold
Test: atest ExpandSensitiveNotificationOnLockScreenLandscape doesn't
crash
Fixes: 430728918
Flag: com.android.systemui.scene_container

Change-Id: I38f7d461840e72af846b86de04357946dfa8aa52
parent bcfdc1b0
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ 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
import com.android.systemui.statusbar.systemstatusicons.ui.compose.MovableSystemStatusIconLegacy
import com.android.systemui.statusbar.systemstatusicons.ui.compose.movableSystemStatusIconsLegacyAndroidView

/**
 * Defines interface for classes that can provide a context for UI that renders the status bar
@@ -45,6 +47,12 @@ interface StatusIconContext {
     * cached in the [StatusIconContext].
     */
    fun iconManager(contentKey: ContentKey): TintedIconManager

    /**
     * Returns a [MovableSystemStatusIconLegacy] movable content for the given [TintedIconManager].
     * This movable content will be cached in the [StatusIconContext].
     */
    fun movableContent(tintedIconManager: TintedIconManager): MovableSystemStatusIconLegacy
}

val LocalStatusIconContext =
@@ -63,6 +71,8 @@ fun WithStatusIconContext(
                private val iconContainerByContentKey =
                    mutableMapOf<ContentKey, StatusIconContainer>()
                private val iconManagerByContentKey = mutableMapOf<ContentKey, TintedIconManager>()
                private val movableContentByIconManager =
                    mutableMapOf<TintedIconManager, MovableSystemStatusIconLegacy>()

                override fun iconContainer(contentKey: ContentKey): StatusIconContainer {
                    return iconContainerByContentKey.getOrPut(contentKey) {
@@ -84,6 +94,14 @@ fun WithStatusIconContext(
                        )
                    }
                }

                override fun movableContent(
                    tintedIconManager: TintedIconManager
                ): MovableSystemStatusIconLegacy {
                    return movableContentByIconManager.getOrPut(tintedIconManager) {
                        movableSystemStatusIconsLegacyAndroidView(tintedIconManager)
                    }
                }
            }
        }

+101 −26
Original line number Diff line number Diff line
@@ -17,13 +17,19 @@
package com.android.systemui.statusbar.systemstatusicons.ui.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.movableContentOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
import com.android.systemui.shade.ui.composable.LocalStatusIconContext
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager

@Suppress("NAME_SHADOWING")
@Composable
fun SystemStatusIconsLegacy(
    statusBarIconController: StatusBarIconController,
@@ -48,13 +54,29 @@ fun SystemStatusIconsLegacy(
    val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
    val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)

    AndroidView(
        factory = {
            statusBarIconController.addIconGroup(iconManager)
            iconContainer
        },
        onRelease = { statusBarIconController.removeIconGroup(iconManager) },
        update = { container ->
    /*
     * Use `rememberUpdatedState` to guarantee that a state will be exposed (without recomposition)
     * for all these parameters, so the update block will be called when any of them changes.
     */
    val useExpandedFormat by rememberUpdatedState(useExpandedFormat)
    val isTransitioning by rememberUpdatedState(isTransitioning)
    val foregroundColor by rememberUpdatedState(foregroundColor)
    val backgroundColor by rememberUpdatedState(backgroundColor)
    val isSingleCarrier by rememberUpdatedState(isSingleCarrier)
    val isMicCameraIndicationEnabled by rememberUpdatedState(isMicCameraIndicationEnabled)
    val isPrivacyChipEnabled by rememberUpdatedState(isPrivacyChipEnabled)
    val isLocationIndicationEnabled by rememberUpdatedState(isLocationIndicationEnabled)

    val update =
        remember(
            statusBarIconController,
            iconManager,
            carrierIconSlots,
            cameraSlot,
            micSlot,
            locationSlot,
        ) {
            { container: StatusIconContainer ->
                container.setQsExpansionTransitioning(isTransitioning)

                if (isSingleCarrier || !useExpandedFormat) {
@@ -83,7 +105,60 @@ fun SystemStatusIconsLegacy(
                }

                iconManager.setTint(foregroundColor, backgroundColor)
            }
        }

    val statusIconContext = LocalStatusIconContext.current

    val movableContent =
        remember(statusIconContext, iconManager) { statusIconContext.movableContent(iconManager) }

    movableContent(statusBarIconController, iconContainer, update, modifier)
}

/** Alias for [movableSystemStatusIconsLegacyAndroidView] */
typealias MovableSystemStatusIconLegacy =
    @Composable
    (StatusBarIconController, StatusIconContainer, (StatusIconContainer) -> Unit, Modifier) -> Unit

/**
 * Returns a movable content for the given `TintedIconManager`. This can be used to guarantee that
 * the same one is always used (to prevent double registration with [StatusBarIconController]) when
 * used with a cache.
 */
fun movableSystemStatusIconsLegacyAndroidView(
    iconManager: TintedIconManager
): MovableSystemStatusIconLegacy {
    return movableContentOf {
        statusBarIconController: StatusBarIconController,
        iconContainer: StatusIconContainer,
        update: (StatusIconContainer) -> Unit,
        modifier: Modifier ->
        SystemStatusIconsLegacyAndroidView(
            statusBarIconController,
            iconManager,
            iconContainer,
            update,
            modifier,
        )
    }
}

@Composable
private fun SystemStatusIconsLegacyAndroidView(
    statusBarIconController: StatusBarIconController,
    iconManager: TintedIconManager,
    iconContainer: StatusIconContainer,
    update: (StatusIconContainer) -> Unit,
    modifier: Modifier = Modifier,
) {
    AndroidView(
        factory = {
            statusBarIconController.addIconGroup(iconManager)
            iconContainer
        },
        onRelease = { statusBarIconController.removeIconGroup(iconManager) },
        update = update,
        modifier = modifier,
    )
}