1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.flags 18 19 import com.android.systemui.Dependency 20 21 /** 22 * This class promotes best practices for flag guarding System UI view refactors. 23 * * [isEnabled] allows changing an implementation. 24 * * [assertInLegacyMode] allows authors to flag code as being "dead" when the flag gets enabled and 25 * ensure that it is not being invoked accidentally in the post-flag refactor. 26 * * [isUnexpectedlyInLegacyMode] allows authors to guard new code with a "safe" alternative when 27 * invoked on flag-disabled builds, but with a check that should crash eng builds or tests when 28 * the expectation is violated. 29 * 30 * The constructors require that you provide a [FeatureFlags] instance. If you're using this in a 31 * View class, it's acceptable to ue the [forView] constructor methods, which do not require one, 32 * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes 33 * inside Views where injecting flag values after initialization can be error-prone. 34 */ 35 class RefactorFlag 36 private constructor( 37 private val injectedFlags: FeatureFlags?, 38 private val flagName: Any, 39 private val readFlagValue: (FeatureFlags) -> Boolean 40 ) { 41 constructor( 42 flags: FeatureFlags, 43 flag: UnreleasedFlag <lambda>null44 ) : this(flags, flag, { it.isEnabled(flag) }) 45 <lambda>null46 constructor(flags: FeatureFlags, flag: ReleasedFlag) : this(flags, flag, { it.isEnabled(flag) }) 47 48 /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */ <lambda>null49 val isEnabled by lazy { 50 @Suppress("DEPRECATION") 51 val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java) 52 readFlagValue(featureFlags) 53 } 54 55 /** 56 * Called to ensure code is only run when the flag is disabled. This will throw an exception if 57 * the flag is enabled to ensure that the refactor author catches issues in testing. 58 * 59 * Example usage: 60 * ``` 61 * public void setController(NotificationShelfController notificationShelfController) { 62 * mShelfRefactor.assertInLegacyMode(); 63 * mController = notificationShelfController; 64 * } 65 * ```` 66 */ assertInLegacyModenull67 fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, flagName) 68 69 /** 70 * Called to ensure code is only run when the flag is enabled. This protects users from the 71 * unintended behaviors caused by accidentally running new logic, while also crashing on an eng 72 * build to ensure that the refactor author catches issues in testing. 73 * 74 * Example usage: 75 * ``` 76 * public void setShelfIcons(NotificationIconContainer icons) { 77 * if (mShelfRefactor.isUnexpectedlyInLegacyMode()) return; 78 * mShelfIcons = icons; 79 * } 80 * ``` 81 */ 82 fun isUnexpectedlyInLegacyMode(): Boolean = 83 RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, flagName) 84 85 companion object { 86 private const val TAG = "RefactorFlag" 87 88 /** Construct a [RefactorFlag] within View construction where injection is impossible. */ 89 @JvmStatic 90 @JvmOverloads 91 fun forView(flag: UnreleasedFlag, flags: FeatureFlags? = null) = 92 RefactorFlag(flags, flag) { it.isEnabled(flag) } 93 94 /** Construct a [RefactorFlag] within View construction where injection is impossible. */ 95 @JvmStatic 96 @JvmOverloads 97 fun forView(flag: ReleasedFlag, flags: FeatureFlags? = null) = 98 RefactorFlag(flags, flag) { it.isEnabled(flag) } 99 } 100 } 101