diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 25db478e57b7e..290904369a4d4 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -62,6 +62,7 @@ java_library { srcs: [ "src/com/android/systemui/flags/Flag.kt", ], + include_srcs: true, static_kotlin_stdlib: false, java_version: "1.8", min_sdk_version: "current", @@ -74,11 +75,11 @@ java_library { ], static_kotlin_stdlib: false, libs: [ + "SystemUI-flags", "androidx.concurrent_concurrent-futures", ], static_libs: [ "SystemUI-flag-types", - "SystemUI-flags", ], java_version: "1.8", min_sdk_version: "current", diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index 68834bc2aa23c..d9b6a342f289d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -16,37 +16,157 @@ package com.android.systemui.flags -interface Flag { +import android.os.Parcel +import android.os.Parcelable + +interface Flag : Parcelable { val id: Int val default: T + + override fun describeContents() = 0 } +// Consider using the "parcelize" kotlin library. + data class BooleanFlag @JvmOverloads constructor( override val id: Int, override val default: Boolean = false -) : Flag +) : Flag { + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = BooleanFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readBoolean() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeBoolean(default) + } +} data class StringFlag @JvmOverloads constructor( override val id: Int, override val default: String = "" -) : Flag +) : Flag { + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = StringFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readString() ?: "" + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeString(default) + } +} data class IntFlag @JvmOverloads constructor( override val id: Int, override val default: Int = 0 -) : Flag +) : Flag { + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = IntFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readInt() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeInt(default) + } +} data class LongFlag @JvmOverloads constructor( override val id: Int, override val default: Long = 0 -) : Flag +) : Flag { + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = LongFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readLong() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeLong(default) + } +} data class FloatFlag @JvmOverloads constructor( override val id: Int, override val default: Float = 0f -) : Flag +) : Flag { + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = FloatFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readFloat() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeFloat(default) + } +} data class DoubleFlag @JvmOverloads constructor( override val id: Int, override val default: Double = 0.0 -) : Flag \ No newline at end of file +) : Flag { + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel) = DoubleFlag(parcel) + override fun newArray(size: Int) = arrayOfNulls(size) + } + } + + private constructor(parcel: Parcel) : this( + id = parcel.readInt(), + default = parcel.readDouble() + ) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(id) + parcel.writeDouble(default) + } +} \ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt index cbb942bc30152..1dc555e300b4c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt @@ -16,10 +16,13 @@ package com.android.systemui.flags +import android.app.Activity +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.database.ContentObserver import android.net.Uri +import android.os.Bundle import android.os.Handler import android.provider.Settings import androidx.concurrent.futures.CallbackToFutureAdapter @@ -34,10 +37,12 @@ class FlagManager constructor( companion object { const val RECEIVING_PACKAGE = "com.android.systemui" const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG" + const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS" const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS" const val FIELD_ID = "id" const val FIELD_VALUE = "value" const val FIELD_TYPE = "type" + const val FIELD_FLAGS = "flags" const val TYPE_BOOLEAN = "boolean" private const val SETTINGS_PREFIX = "systemui/flags" } @@ -46,14 +51,26 @@ class FlagManager constructor( private val settingsObserver: ContentObserver = SettingsObserver() fun getFlagsFuture(): ListenableFuture>> { - val knownFlagMap = Flags.collectFlags() - // Possible todo in the future: query systemui async to actually get the known flag ids. - return CallbackToFutureAdapter.getFuture( - CallbackToFutureAdapter.Resolver { - completer: CallbackToFutureAdapter.Completer>> -> - completer.set(knownFlagMap.values as Collection>) - "Retrieving Flags" - }) + val intent = Intent(ACTION_GET_FLAGS) + intent.setPackage(RECEIVING_PACKAGE) + + return CallbackToFutureAdapter.getFuture { + completer: CallbackToFutureAdapter.Completer -> + context.sendOrderedBroadcast(intent, null, + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val extras: Bundle? = getResultExtras(false) + val listOfFlags: java.util.ArrayList>? = + extras?.getParcelableArrayList(FIELD_FLAGS) + if (listOfFlags != null) { + completer.set(listOfFlags) + } else { + completer.setException(NoFlagResultsException()) + } + } + }, null, Activity.RESULT_OK, "extra data", null) + "QueryingFlags" + } as ListenableFuture>> } fun setFlagValue(id: Int, enabled: Boolean) { @@ -149,4 +166,7 @@ class FlagManager constructor( } } -class InvalidFlagStorageException : Exception("Data found but is invalid") \ No newline at end of file +class InvalidFlagStorageException : Exception("Data found but is invalid") + +class NoFlagResultsException : Exception( + "SystemUI failed to communicate its flags back successfully") \ No newline at end of file diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java index 9311966f82f26..ef046193473d2 100644 --- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java +++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java @@ -16,10 +16,11 @@ package com.android.systemui.flags; +import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS; import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG; +import static com.android.systemui.flags.FlagManager.FIELD_FLAGS; import static com.android.systemui.flags.FlagManager.FIELD_ID; import static com.android.systemui.flags.FlagManager.FIELD_VALUE; -import static com.android.systemui.flags.FlagManager.FLAGS_PERMISSION; import android.content.BroadcastReceiver; import android.content.Context; @@ -69,8 +70,10 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { DumpManager dumpManager) { mFlagManager = flagManager; mSecureSettings = secureSettings; - IntentFilter filter = new IntentFilter(ACTION_SET_FLAG); - context.registerReceiver(mReceiver, filter, FLAGS_PERMISSION, null); + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_SET_FLAG); + filter.addAction(ACTION_GET_FLAGS); + context.registerReceiver(mReceiver, filter, null, null); dumpManager.registerDumpable(TAG, this); } @@ -151,9 +154,15 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable { if (action == null) { return; } - if (ACTION_SET_FLAG.equals(action)) { handleSetFlag(intent.getExtras()); + } else if (ACTION_GET_FLAGS.equals(action)) { + Map> knownFlagMap = Flags.collectFlags(); + ArrayList> flags = new ArrayList<>(knownFlagMap.values()); + Bundle extras = getResultExtras(true); + if (extras != null) { + extras.putParcelableArrayList(FIELD_FLAGS, flags); + } } }