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