1 /* 2 * Copyright (C) 2022 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 java.io.PrintWriter 20 21 class FakeFeatureFlags : FeatureFlags { 22 private val booleanFlags = mutableMapOf<String, Boolean>() 23 private val stringFlags = mutableMapOf<String, String>() 24 private val intFlags = mutableMapOf<String, Int>() 25 private val knownFlagNames = mutableMapOf<String, String>() 26 private val flagListeners = mutableMapOf<String, MutableSet<FlagListenable.Listener>>() 27 private val listenerflagNames = mutableMapOf<FlagListenable.Listener, MutableSet<String>>() 28 29 init { 30 FlagsFactory.knownFlags.forEach { entry: Map.Entry<String, Flag<*>> -> 31 knownFlagNames[entry.value.name] = entry.key 32 } 33 } 34 35 fun set(flag: BooleanFlag, value: Boolean) { 36 if (booleanFlags.put(flag.name, value)?.let { value != it } != false) { 37 notifyFlagChanged(flag) 38 } 39 } 40 41 fun set(flag: ResourceBooleanFlag, value: Boolean) { 42 if (booleanFlags.put(flag.name, value)?.let { value != it } != false) { 43 notifyFlagChanged(flag) 44 } 45 } 46 47 fun set(flag: SysPropBooleanFlag, value: Boolean) { 48 if (booleanFlags.put(flag.name, value)?.let { value != it } != false) { 49 notifyFlagChanged(flag) 50 } 51 } 52 53 fun set(flag: StringFlag, value: String) { 54 if (stringFlags.put(flag.name, value)?.let { value != it } == null) { 55 notifyFlagChanged(flag) 56 } 57 } 58 59 fun set(flag: ResourceStringFlag, value: String) { 60 if (stringFlags.put(flag.name, value)?.let { value != it } == null) { 61 notifyFlagChanged(flag) 62 } 63 } 64 65 /** 66 * Set the given flag's default value if no other value has been set. 67 * 68 * REMINDER: You should always test your code with your flag in both configurations, so 69 * generally you should be setting a particular value. This method should be reserved for 70 * situations where the flag needs to be read (e.g. in the class constructor), but its 71 * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use 72 * this method than to hard-code `false` or `true` because then at least if you're wrong, 73 * and the flag value *does* matter, you'll notice when the flag is flipped and tests 74 * start failing. 75 */ 76 fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default) 77 78 /** 79 * Set the given flag's default value if no other value has been set. 80 * 81 * REMINDER: You should always test your code with your flag in both configurations, so 82 * generally you should be setting a particular value. This method should be reserved for 83 * situations where the flag needs to be read (e.g. in the class constructor), but its 84 * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use 85 * this method than to hard-code `false` or `true` because then at least if you're wrong, 86 * and the flag value *does* matter, you'll notice when the flag is flipped and tests 87 * start failing. 88 */ 89 fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.name, flag.default) 90 91 private fun notifyFlagChanged(flag: Flag<*>) { 92 flagListeners[flag.name]?.let { listeners -> 93 listeners.forEach { listener -> 94 listener.onFlagChanged( 95 object : FlagListenable.FlagEvent { 96 override val flagName = flag.name 97 override fun requestNoRestart() {} 98 } 99 ) 100 } 101 } 102 } 103 104 override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.name) 105 106 override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.name) 107 108 override fun isEnabled(flag: ResourceBooleanFlag): Boolean = requireBooleanValue(flag.name) 109 110 override fun isEnabled(flag: SysPropBooleanFlag): Boolean = requireBooleanValue(flag.name) 111 112 override fun getString(flag: StringFlag): String = requireStringValue(flag.name) 113 114 override fun getString(flag: ResourceStringFlag): String = requireStringValue(flag.name) 115 116 override fun getInt(flag: IntFlag): Int = requireIntValue(flag.name) 117 118 override fun getInt(flag: ResourceIntFlag): Int = requireIntValue(flag.name) 119 120 override fun addListener(flag: Flag<*>, listener: FlagListenable.Listener) { 121 flagListeners.getOrPut(flag.name) { mutableSetOf() }.add(listener) 122 listenerflagNames.getOrPut(listener) { mutableSetOf() }.add(flag.name) 123 } 124 125 override fun removeListener(listener: FlagListenable.Listener) { 126 listenerflagNames.remove(listener)?.let { 127 flagNames -> flagNames.forEach { 128 id -> flagListeners[id]?.remove(listener) 129 } 130 } 131 } 132 133 override fun dump(writer: PrintWriter, args: Array<out String>?) { 134 // no-op 135 } 136 137 private fun flagName(flagName: String): String { 138 return knownFlagNames[flagName] ?: "UNKNOWN($flagName)" 139 } 140 141 private fun requireBooleanValue(flagName: String): Boolean { 142 return booleanFlags[flagName] 143 ?: error("Flag ${flagName(flagName)} was accessed as boolean but not specified.") 144 } 145 146 private fun requireStringValue(flagName: String): String { 147 return stringFlags[flagName] 148 ?: error("Flag ${flagName(flagName)} was accessed as string but not specified.") 149 } 150 151 private fun requireIntValue(flagName: String): Int { 152 return intFlags[flagName] 153 ?: error("Flag ${flagName(flagName)} was accessed as int but not specified.") 154 } 155 } 156