1 /*
2  * Copyright (C) 2021 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 android.annotation.BoolRes
20 import android.annotation.IntegerRes
21 import android.annotation.StringRes
22 import android.os.Parcel
23 import android.os.Parcelable
24 
25 /**
26  * Base interface for flags that can change value on a running device.
27  * @property teamfood Set to true to include this flag as part of the teamfood flag. This will
28  *                    be removed soon.
29  * @property name Used for server-side flagging where appropriate. Also used for display. No spaces.
30  * @property namespace The server-side namespace that this flag lives under.
31  */
32 interface Flag<T> {
33     val teamfood: Boolean
34     val name: String
35     val namespace: String
36 }
37 
38 interface ParcelableFlag<T> : Flag<T>, Parcelable {
39     val default: T
40     val overridden: Boolean
describeContentsnull41     override fun describeContents() = 0
42 }
43 
44 interface ResourceFlag<T> : Flag<T> {
45     val resourceId: Int
46 }
47 
48 interface SysPropFlag<T> : Flag<T> {
49     val default: T
50 }
51 
52 /**
53  * Base class for most common boolean flags.
54  *
55  * See [UnreleasedFlag] and [ReleasedFlag] for useful implementations.
56  */
57 // Consider using the "parcelize" kotlin library.
58 abstract class BooleanFlag constructor(
59     override val name: String,
60     override val namespace: String,
61     override val default: Boolean = false,
62     override val teamfood: Boolean = false,
63     override val overridden: Boolean = false
64 ) : ParcelableFlag<Boolean> {
65 
66     companion object {
67         @JvmField
68         val CREATOR = object : Parcelable.Creator<BooleanFlag> {
createFromParcelnull69             override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
newArraynull70             override fun newArray(size: Int) = arrayOfNulls<BooleanFlag>(size)
71         }
72     }
73 
74     private constructor(
75             id: Int,
76             name: String,
77             namespace: String,
78             default: Boolean,
79             teamfood: Boolean,
80             overridden: Boolean,
81             ) : this(name, namespace, default, teamfood, overridden)
82 
83     private constructor(parcel: Parcel) : this(
84         parcel.readInt(),
85         name = parcel.readString() ?: "",
86         namespace = parcel.readString() ?: "",
87         default = parcel.readBoolean(),
88         teamfood = parcel.readBoolean(),
89         overridden = parcel.readBoolean()
90     )
91 
92     override fun writeToParcel(parcel: Parcel, flags: Int) {
93         parcel.writeInt(0)
94         parcel.writeString(name)
95         parcel.writeString(namespace)
96         parcel.writeBoolean(default)
97         parcel.writeBoolean(teamfood)
98         parcel.writeBoolean(overridden)
99     }
100 }
101 
102 /**
103  * A Flag that is is false by default.
104  *
105  * It can be changed or overridden in debug builds but not in release builds.
106  */
107 data class UnreleasedFlag constructor(
108     override val name: String,
109     override val namespace: String,
110     override val teamfood: Boolean = false,
111     override val overridden: Boolean = false
112 ) : BooleanFlag(name, namespace, false, teamfood, overridden)
113 
114 /**
115  * A Flag that is true by default.
116  *
117  * It can be changed or overridden in any build, meaning it can be turned off if needed.
118  */
119 data class ReleasedFlag constructor(
120     override val name: String,
121     override val namespace: String,
122     override val overridden: Boolean = false
123 ) : BooleanFlag(name, namespace, true, teamfood = false, overridden)
124 
125 /**
126  * A Flag that reads its default values from a resource overlay instead of code.
127  *
128  * Prefer [UnreleasedFlag] and [ReleasedFlag].
129  */
130 data class ResourceBooleanFlag constructor(
131     override val name: String,
132     override val namespace: String,
133     @BoolRes override val resourceId: Int,
134 ) : ResourceFlag<Boolean> {
135     override val teamfood: Boolean = false
136 }
137 
138 /**
139  * A Flag that can reads its overrides from System Properties.
140  *
141  * This is generally useful for flags that come from or are used _outside_ of SystemUI.
142  *
143  * Prefer [UnreleasedFlag] and [ReleasedFlag].
144  */
145 data class SysPropBooleanFlag constructor(
146     override val name: String,
147     override val namespace: String,
148     override val default: Boolean = false,
149 ) : SysPropFlag<Boolean> {
150     override val teamfood: Boolean = false
151 }
152 
153 data class StringFlag constructor(
154     override val name: String,
155     override val namespace: String,
156     override val default: String = "",
157     override val teamfood: Boolean = false,
158     override val overridden: Boolean = false
159 ) : ParcelableFlag<String> {
160     companion object {
161         @JvmField
162         val CREATOR = object : Parcelable.Creator<StringFlag> {
createFromParcelnull163             override fun createFromParcel(parcel: Parcel) = StringFlag(parcel)
164             override fun newArray(size: Int) = arrayOfNulls<StringFlag>(size)
165         }
166     }
167 
168     private constructor(id: Int, name: String, namespace: String, default: String) : this(
169             name,
170             namespace,
171             default
172     )
173 
174     private constructor(parcel: Parcel) : this(
175         parcel.readInt(),
176         name = parcel.readString() ?: "",
177         namespace = parcel.readString() ?: "",
178         default = parcel.readString() ?: ""
179     )
180 
181     override fun writeToParcel(parcel: Parcel, flags: Int) {
182         parcel.writeInt(0)
183         parcel.writeString(name)
184         parcel.writeString(namespace)
185         parcel.writeString(default)
186     }
187 }
188 
189 data class ResourceStringFlag constructor(
190     override val name: String,
191     override val namespace: String,
192     @StringRes override val resourceId: Int,
193     override val teamfood: Boolean = false
194 ) : ResourceFlag<String>
195 
196 data class IntFlag constructor(
197     override val name: String,
198     override val namespace: String,
199     override val default: Int = 0,
200     override val teamfood: Boolean = false,
201     override val overridden: Boolean = false
202 ) : ParcelableFlag<Int> {
203 
204     companion object {
205         @JvmField
206         val CREATOR = object : Parcelable.Creator<IntFlag> {
createFromParcelnull207             override fun createFromParcel(parcel: Parcel) = IntFlag(parcel)
208             override fun newArray(size: Int) = arrayOfNulls<IntFlag>(size)
209         }
210     }
211 
212     private constructor(id: Int, name: String, namespace: String, default: Int) : this(
213             name,
214             namespace,
215             default
216     )
217 
218     private constructor(parcel: Parcel) : this(
219         parcel.readInt(),
220         name = parcel.readString() ?: "",
221         namespace = parcel.readString() ?: "",
222         default = parcel.readInt()
223     )
224 
225     override fun writeToParcel(parcel: Parcel, flags: Int) {
226         parcel.writeInt(0)
227         parcel.writeString(name)
228         parcel.writeString(namespace)
229         parcel.writeInt(default)
230     }
231 }
232 
233 data class ResourceIntFlag constructor(
234     override val name: String,
235     override val namespace: String,
236     @IntegerRes override val resourceId: Int,
237     override val teamfood: Boolean = false
238 ) : ResourceFlag<Int>
239 
240 data class LongFlag constructor(
241     override val name: String,
242     override val namespace: String,
243     override val default: Long = 0,
244     override val teamfood: Boolean = false,
245     override val overridden: Boolean = false
246 ) : ParcelableFlag<Long> {
247 
248     companion object {
249         @JvmField
250         val CREATOR = object : Parcelable.Creator<LongFlag> {
createFromParcelnull251             override fun createFromParcel(parcel: Parcel) = LongFlag(parcel)
252             override fun newArray(size: Int) = arrayOfNulls<LongFlag>(size)
253         }
254     }
255 
256     private constructor(id: Int, name: String, namespace: String, default: Long) : this(
257             name,
258             namespace,
259             default
260     )
261 
262     private constructor(parcel: Parcel) : this(
263         parcel.readInt(),
264         name = parcel.readString() ?: "",
265         namespace = parcel.readString() ?: "",
266         default = parcel.readLong()
267     )
268 
269     override fun writeToParcel(parcel: Parcel, flags: Int) {
270         parcel.writeInt(0)
271         parcel.writeString(name)
272         parcel.writeString(namespace)
273         parcel.writeLong(default)
274     }
275 }
276 
277 data class FloatFlag constructor(
278     override val name: String,
279     override val namespace: String,
280     override val default: Float = 0f,
281     override val teamfood: Boolean = false,
282     override val overridden: Boolean = false
283 ) : ParcelableFlag<Float> {
284 
285     companion object {
286         @JvmField
287         val CREATOR = object : Parcelable.Creator<FloatFlag> {
createFromParcelnull288             override fun createFromParcel(parcel: Parcel) = FloatFlag(parcel)
289             override fun newArray(size: Int) = arrayOfNulls<FloatFlag>(size)
290         }
291     }
292 
293     private constructor(id: Int, name: String, namespace: String, default: Float) : this(
294             name,
295             namespace,
296             default
297     )
298 
299     private constructor(parcel: Parcel) : this(
300         parcel.readInt(),
301         name = parcel.readString() ?: "",
302         namespace = parcel.readString() ?: "",
303         default = parcel.readFloat()
304     )
305 
306     override fun writeToParcel(parcel: Parcel, flags: Int) {
307         parcel.writeInt(0)
308         parcel.writeString(name)
309         parcel.writeString(namespace)
310         parcel.writeFloat(default)
311     }
312 }
313 
314 data class ResourceFloatFlag constructor(
315     override val name: String,
316     override val namespace: String,
317     override val resourceId: Int,
318     override val teamfood: Boolean = false,
319 ) : ResourceFlag<Int>
320 
321 data class DoubleFlag constructor(
322     override val name: String,
323     override val namespace: String,
324     override val default: Double = 0.0,
325     override val teamfood: Boolean = false,
326     override val overridden: Boolean = false
327 ) : ParcelableFlag<Double> {
328 
329     companion object {
330         @JvmField
331         val CREATOR = object : Parcelable.Creator<DoubleFlag> {
createFromParcelnull332             override fun createFromParcel(parcel: Parcel) = DoubleFlag(parcel)
333             override fun newArray(size: Int) = arrayOfNulls<DoubleFlag>(size)
334         }
335     }
336 
337     private constructor(id: Int, name: String, namespace: String, default: Double) : this(
338             name,
339             namespace,
340             default
341     )
342 
343     private constructor(parcel: Parcel) : this(
344         parcel.readInt(),
345         name = parcel.readString() ?: "",
346         namespace = parcel.readString() ?: "",
347         default = parcel.readDouble()
348     )
349 
350     override fun writeToParcel(parcel: Parcel, flags: Int) {
351         parcel.writeInt(0)
352         parcel.writeString(name)
353         parcel.writeString(namespace)
354         parcel.writeDouble(default)
355     }
356 }
357