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
41     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> {
69             override fun createFromParcel(parcel: Parcel) = object : BooleanFlag(parcel) {}
70             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 teamfood: Boolean = false,
123     override val overridden: Boolean = false
124 ) : BooleanFlag(name, namespace, true, teamfood, overridden)
125 
126 /**
127  * A Flag that reads its default values from a resource overlay instead of code.
128  *
129  * Prefer [UnreleasedFlag] and [ReleasedFlag].
130  */
131 data class ResourceBooleanFlag constructor(
132     override val name: String,
133     override val namespace: String,
134     @BoolRes override val resourceId: Int,
135     override val teamfood: Boolean = false
136 ) : ResourceFlag<Boolean>
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     // TODO(b/268520433): Teamfood not supported for sysprop flags yet.
151     override val teamfood: Boolean = false
152 }
153 
154 data class StringFlag constructor(
155     override val name: String,
156     override val namespace: String,
157     override val default: String = "",
158     override val teamfood: Boolean = false,
159     override val overridden: Boolean = false
160 ) : ParcelableFlag<String> {
161     companion object {
162         @JvmField
163         val CREATOR = object : Parcelable.Creator<StringFlag> {
164             override fun createFromParcel(parcel: Parcel) = StringFlag(parcel)
165             override fun newArray(size: Int) = arrayOfNulls<StringFlag>(size)
166         }
167     }
168 
169     private constructor(id: Int, name: String, namespace: String, default: String) : this(
170             name,
171             namespace,
172             default
173     )
174 
175     private constructor(parcel: Parcel) : this(
176         parcel.readInt(),
177         name = parcel.readString() ?: "",
178         namespace = parcel.readString() ?: "",
179         default = parcel.readString() ?: ""
180     )
181 
182     override fun writeToParcel(parcel: Parcel, flags: Int) {
183         parcel.writeInt(0)
184         parcel.writeString(name)
185         parcel.writeString(namespace)
186         parcel.writeString(default)
187     }
188 }
189 
190 data class ResourceStringFlag constructor(
191     override val name: String,
192     override val namespace: String,
193     @StringRes override val resourceId: Int,
194     override val teamfood: Boolean = false
195 ) : ResourceFlag<String>
196 
197 data class IntFlag constructor(
198     override val name: String,
199     override val namespace: String,
200     override val default: Int = 0,
201     override val teamfood: Boolean = false,
202     override val overridden: Boolean = false
203 ) : ParcelableFlag<Int> {
204 
205     companion object {
206         @JvmField
207         val CREATOR = object : Parcelable.Creator<IntFlag> {
208             override fun createFromParcel(parcel: Parcel) = IntFlag(parcel)
209             override fun newArray(size: Int) = arrayOfNulls<IntFlag>(size)
210         }
211     }
212 
213     private constructor(id: Int, name: String, namespace: String, default: Int) : this(
214             name,
215             namespace,
216             default
217     )
218 
219     private constructor(parcel: Parcel) : this(
220         parcel.readInt(),
221         name = parcel.readString() ?: "",
222         namespace = parcel.readString() ?: "",
223         default = parcel.readInt()
224     )
225 
226     override fun writeToParcel(parcel: Parcel, flags: Int) {
227         parcel.writeInt(0)
228         parcel.writeString(name)
229         parcel.writeString(namespace)
230         parcel.writeInt(default)
231     }
232 }
233 
234 data class ResourceIntFlag constructor(
235     override val name: String,
236     override val namespace: String,
237     @IntegerRes override val resourceId: Int,
238     override val teamfood: Boolean = false
239 ) : ResourceFlag<Int>
240 
241 data class LongFlag constructor(
242     override val name: String,
243     override val namespace: String,
244     override val default: Long = 0,
245     override val teamfood: Boolean = false,
246     override val overridden: Boolean = false
247 ) : ParcelableFlag<Long> {
248 
249     companion object {
250         @JvmField
251         val CREATOR = object : Parcelable.Creator<LongFlag> {
252             override fun createFromParcel(parcel: Parcel) = LongFlag(parcel)
253             override fun newArray(size: Int) = arrayOfNulls<LongFlag>(size)
254         }
255     }
256 
257     private constructor(id: Int, name: String, namespace: String, default: Long) : this(
258             name,
259             namespace,
260             default
261     )
262 
263     private constructor(parcel: Parcel) : this(
264         parcel.readInt(),
265         name = parcel.readString() ?: "",
266         namespace = parcel.readString() ?: "",
267         default = parcel.readLong()
268     )
269 
270     override fun writeToParcel(parcel: Parcel, flags: Int) {
271         parcel.writeInt(0)
272         parcel.writeString(name)
273         parcel.writeString(namespace)
274         parcel.writeLong(default)
275     }
276 }
277 
278 data class FloatFlag constructor(
279     override val name: String,
280     override val namespace: String,
281     override val default: Float = 0f,
282     override val teamfood: Boolean = false,
283     override val overridden: Boolean = false
284 ) : ParcelableFlag<Float> {
285 
286     companion object {
287         @JvmField
288         val CREATOR = object : Parcelable.Creator<FloatFlag> {
289             override fun createFromParcel(parcel: Parcel) = FloatFlag(parcel)
290             override fun newArray(size: Int) = arrayOfNulls<FloatFlag>(size)
291         }
292     }
293 
294     private constructor(id: Int, name: String, namespace: String, default: Float) : this(
295             name,
296             namespace,
297             default
298     )
299 
300     private constructor(parcel: Parcel) : this(
301         parcel.readInt(),
302         name = parcel.readString() ?: "",
303         namespace = parcel.readString() ?: "",
304         default = parcel.readFloat()
305     )
306 
307     override fun writeToParcel(parcel: Parcel, flags: Int) {
308         parcel.writeInt(0)
309         parcel.writeString(name)
310         parcel.writeString(namespace)
311         parcel.writeFloat(default)
312     }
313 }
314 
315 data class ResourceFloatFlag constructor(
316     override val name: String,
317     override val namespace: String,
318     override val resourceId: Int,
319     override val teamfood: Boolean = false,
320 ) : ResourceFlag<Int>
321 
322 data class DoubleFlag constructor(
323     override val name: String,
324     override val namespace: String,
325     override val default: Double = 0.0,
326     override val teamfood: Boolean = false,
327     override val overridden: Boolean = false
328 ) : ParcelableFlag<Double> {
329 
330     companion object {
331         @JvmField
332         val CREATOR = object : Parcelable.Creator<DoubleFlag> {
333             override fun createFromParcel(parcel: Parcel) = DoubleFlag(parcel)
334             override fun newArray(size: Int) = arrayOfNulls<DoubleFlag>(size)
335         }
336     }
337 
338     private constructor(id: Int, name: String, namespace: String, default: Double) : this(
339             name,
340             namespace,
341             default
342     )
343 
344     private constructor(parcel: Parcel) : this(
345         parcel.readInt(),
346         name = parcel.readString() ?: "",
347         namespace = parcel.readString() ?: "",
348         default = parcel.readDouble()
349     )
350 
351     override fun writeToParcel(parcel: Parcel, flags: Int) {
352         parcel.writeInt(0)
353         parcel.writeString(name)
354         parcel.writeString(namespace)
355         parcel.writeDouble(default)
356     }
357 }
358