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