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

Commit c9d6d894 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Allow static access to SceneContainerFlag.isEnabled" into main

parents 2f52f7c7 182829bd
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -965,14 +965,6 @@
    <!-- Whether to show bottom sheets edge to edge -->
    <bool name="config_edgeToEdgeBottomSheetDialog">true</bool>

    <!--
    Whether the scene container framework is enabled.

    The scene container framework is a newer (2023) way to organize the various "scenes" between the
    bouncer, lockscreen, shade, and quick settings.
    -->
    <bool name="config_sceneContainerFrameworkEnabled">true</bool>

    <!--
    Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate
    TODO(b/302332976) Get this value from the HAL if they can provide an API for it.
+20 −10
Original line number Diff line number Diff line
@@ -25,7 +25,9 @@ import com.android.server.notification.Flags.vibrateWhileUnlocked
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
@@ -36,20 +38,28 @@ import javax.inject.Inject
class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, handler: Handler) :
    FlagDependenciesBase(featureFlags, handler) {
    override fun defineDependencies() {
        // Internal notification backend dependencies
        crossAppPoliteNotifications dependsOn politeNotifications
        vibrateWhileUnlockedToken dependsOn politeNotifications

        // Internal notification frontend dependencies
        NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
        FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token

        val keyguardBottomAreaRefactor = FlagToken(
                FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
        // Internal keyguard dependencies
        KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor

        val crossAppPoliteNotifToken =
                FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
        val politeNotifToken = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
        crossAppPoliteNotifToken dependsOn politeNotifToken

        val vibrateWhileUnlockedToken =
                FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
        vibrateWhileUnlockedToken dependsOn politeNotifToken
        // SceneContainer dependencies
        SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
        SceneContainerFlag.getMainStaticFlag() dependsOn MIGRATE_KEYGUARD_STATUS_BAR_VIEW
    }

    private inline val politeNotifications
        get() = FlagToken(FLAG_POLITE_NOTIFICATIONS, politeNotifications())
    private inline val crossAppPoliteNotifications
        get() = FlagToken(FLAG_CROSS_APP_POLITE_NOTIFICATIONS, crossAppPoliteNotifications())
    private inline val vibrateWhileUnlockedToken: FlagToken
        get() = FlagToken(FLAG_VIBRATE_WHILE_UNLOCKED, vibrateWhileUnlocked())
    private inline val keyguardBottomAreaRefactor
        get() = FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor())
}
+5 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.util

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

/** Helper for reading or using the media_in_scene_container flag state. */
@@ -25,6 +26,10 @@ object MediaInSceneContainerFlag {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_MEDIA_IN_SCENE_CONTAINER

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

    /** Is the flag enabled? */
    @JvmStatic
    inline val isEnabled
+83 −127
Original line number Diff line number Diff line
@@ -14,30 +14,94 @@
 * limitations under the License.
 */

@file:Suppress("NOTHING_TO_INLINE")

package com.android.systemui.scene.shared.flag

import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flag
import com.android.systemui.flags.Flags
import com.android.systemui.flags.ReleasedFlag
import com.android.systemui.flags.ResourceBooleanFlag
import com.android.systemui.flags.UnreleasedFlag
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

/** Helper for reading or using the scene container flag state. */
object SceneContainerFlag {
    /** The flag description -- not an aconfig flag name */
    const val DESCRIPTION = "SceneContainerFlag"

    inline val isEnabled
        get() =
            SCENE_CONTAINER_ENABLED && // mainStaticFlag
            sceneContainer() && // mainAconfigFlag
                keyguardBottomAreaRefactor() &&
                KeyguardShadeMigrationNssl.isEnabled &&
                MediaInSceneContainerFlag.isEnabled &&
                ComposeFacade.isComposeAvailable()

    /**
     * The main static flag, SCENE_CONTAINER_ENABLED. This is an explicit static flag check that
     * helps with downstream optimizations (like unused code stripping) in builds where aconfig
     * flags are still writable. Do not remove!
     */
    inline fun getMainStaticFlag() =
        FlagToken("Flags.SCENE_CONTAINER_ENABLED", SCENE_CONTAINER_ENABLED)

    /** The main aconfig flag. */
    inline fun getMainAconfigFlag() = FlagToken(FLAG_SCENE_CONTAINER, sceneContainer())

    /** The set of secondary flags which must be enabled for scene container to work properly */
    inline fun getSecondaryFlags(): Sequence<FlagToken> =
        sequenceOf(
            FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
            KeyguardShadeMigrationNssl.token,
            MediaInSceneContainerFlag.token,
        )

    /** The full set of requirements for SceneContainer */
    inline fun getAllRequirements(): Sequence<FlagToken> {
        val composeRequirement =
            FlagToken("ComposeFacade.isComposeAvailable()", ComposeFacade.isComposeAvailable())
        return sequenceOf(getMainStaticFlag(), getMainAconfigFlag()) +
            getSecondaryFlags() +
            composeRequirement
    }

    /** Return all dependencies of this flag in pairs where [Pair.first] depends on [Pair.second] */
    inline fun getFlagDependencies(): Sequence<Pair<FlagToken, FlagToken>> {
        val mainStaticFlag = getMainStaticFlag()
        val mainAconfigFlag = getMainAconfigFlag()
        return sequence {
            // The static and aconfig flags should be equal; make them co-dependent
            yield(mainAconfigFlag to mainStaticFlag)
            yield(mainStaticFlag to mainAconfigFlag)
            // all other flags depend on the static flag for brevity
        } + getSecondaryFlags().map { mainStaticFlag to it }
    }

    /**
     * Called to ensure code is only run when the flag is enabled. This protects users from the
     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
     * build to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun isUnexpectedlyInLegacyMode() =
        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, DESCRIPTION)

    /**
     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
     * the flag is enabled to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, DESCRIPTION)
}

/**
 * Defines interface for classes that can check whether the scene container framework feature is
@@ -52,133 +116,25 @@ interface SceneContainerFlags {
    fun requirementDescription(): String
}

class SceneContainerFlagsImpl
@AssistedInject
constructor(
    @Application private val context: Context,
    private val featureFlagsClassic: FeatureFlagsClassic,
    @Assisted private val isComposeAvailable: Boolean,
) : SceneContainerFlags {

    companion object {
        @VisibleForTesting
        val classicFlagTokens: List<Flag<Boolean>> =
            listOf(
                Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW,
            )
    }

    /** The list of requirements, all must be met for the feature to be enabled. */
    private val requirements =
        listOf(
            AconfigFlagMustBeEnabled(
                flagName = AConfigFlags.FLAG_SCENE_CONTAINER,
                flagValue = sceneContainer(),
            ),
            AconfigFlagMustBeEnabled(
                flagName = AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
                flagValue = keyguardBottomAreaRefactor(),
            ),
            AconfigFlagMustBeEnabled(
                flagName = KeyguardShadeMigrationNssl.FLAG_NAME,
                flagValue = KeyguardShadeMigrationNssl.isEnabled,
            ),
            AconfigFlagMustBeEnabled(
                flagName = MediaInSceneContainerFlag.FLAG_NAME,
                flagValue = MediaInSceneContainerFlag.isEnabled,
            ),
        ) +
            classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
            listOf(
                ComposeMustBeAvailable(),
                CompileTimeFlagMustBeEnabled(),
                ResourceConfigMustBeEnabled()
            )
class SceneContainerFlagsImpl : SceneContainerFlags {

    override fun isEnabled(): Boolean {
        // SCENE_CONTAINER_ENABLED is an explicit static flag check that helps with downstream
        // optimizations, e.g., unused code stripping. Do not remove!
        return Flags.SCENE_CONTAINER_ENABLED && requirements.all { it.isMet() }
        return SceneContainerFlag.isEnabled
    }

    override fun requirementDescription(): String {
        return buildString {
            requirements.forEach { requirement ->
            SceneContainerFlag.getAllRequirements().forEach { requirement ->
                append('\n')
                append(if (requirement.isMet()) "    [MET]" else "[NOT MET]")
                append(if (requirement.isEnabled) "    [MET]" else "[NOT MET]")
                append(" ${requirement.name}")
            }
        }
    }

    private interface Requirement {
        val name: String

        fun isMet(): Boolean
    }

    private inner class ComposeMustBeAvailable : Requirement {
        override val name = "Jetpack Compose must be available"

        override fun isMet(): Boolean {
            return isComposeAvailable
        }
    }

    private inner class CompileTimeFlagMustBeEnabled : Requirement {
        override val name = "Flags.SCENE_CONTAINER_ENABLED must be enabled in code"

        override fun isMet(): Boolean {
            return Flags.SCENE_CONTAINER_ENABLED
        }
    }

    private inner class FlagMustBeEnabled<FlagType : Flag<*>>(
        private val flag: FlagType,
    ) : Requirement {
        override val name = "Flag ${flag.name} must be enabled"

        override fun isMet(): Boolean {
            return when (flag) {
                is ResourceBooleanFlag -> featureFlagsClassic.isEnabled(flag)
                is ReleasedFlag -> featureFlagsClassic.isEnabled(flag)
                is UnreleasedFlag -> featureFlagsClassic.isEnabled(flag)
                else -> error("Unsupported flag type ${flag.javaClass}")
            }
        }
    }

    private inner class AconfigFlagMustBeEnabled(
        flagName: String,
        private val flagValue: Boolean,
    ) : Requirement {
        override val name: String = "Aconfig flag $flagName must be enabled"

        override fun isMet(): Boolean {
            return flagValue
        }
    }

    private inner class ResourceConfigMustBeEnabled : Requirement {
        override val name: String = "R.bool.config_sceneContainerFrameworkEnabled must be true"

        override fun isMet(): Boolean {
            return context.resources.getBoolean(R.bool.config_sceneContainerFrameworkEnabled)
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(isComposeAvailable: Boolean): SceneContainerFlagsImpl
    }
}

@Module
object SceneContainerFlagsModule {

    @Provides
    @SysUISingleton
    fun impl(factory: SceneContainerFlagsImpl.Factory): SceneContainerFlags {
        return factory.create(ComposeFacade.isComposeAvailable())
    }
    @Provides @SysUISingleton fun impl(): SceneContainerFlags = SceneContainerFlagsImpl()
}
+5 −0
Original line number Diff line number Diff line
@@ -17,12 +17,17 @@
package com.android.systemui.statusbar.notification.interruption

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

/** Helper for reading or using the visual interruptions refactor flag state. */
object VisualInterruptionRefactor {
    const val FLAG_NAME = Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR

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

    /** Whether the refactor is enabled */
    @JvmStatic
    inline val isEnabled
Loading