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