Add support for flag change listeners library.
Bug: 203548827
Test: manual
Change-Id: I6f989e89a98c7c6643af762eb0263f81ce584dfa
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 16a6a46..eae6f27 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -83,8 +83,7 @@
"src/**/*.kt",
"src/**/*.java",
"src/**/I*.aidl",
- "src-release/**/*.kt",
- "src-release/**/*.java",
+ ":ReleaseJavaFiles",
],
product_variables: {
debuggable: {
diff --git a/packages/SystemUI/docs/plugins.md b/packages/SystemUI/docs/plugins.md
index 6892005..378cba5 100644
--- a/packages/SystemUI/docs/plugins.md
+++ b/packages/SystemUI/docs/plugins.md
@@ -1,3 +1,4 @@
+
# SystemUI Plugins
Plugins provide an easy way to rapidly prototype SystemUI features. Plugins are APKs that will be installable only on Build.IS_DEBUGGABLE (dogfood) builds, that can change the behavior of SystemUI at runtime. This is done by creating a basic set of interfaces that the plugins can expect to be in SysUI, then the portion of code controlled by the interface can be iterated on faster than currently.
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 7cf22a3..25db478 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -75,10 +75,10 @@
static_kotlin_stdlib: false,
libs: [
"androidx.concurrent_concurrent-futures",
- "SystemUI-flags",
],
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/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index 3125faf..cbb942b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -18,13 +18,19 @@
import android.content.Context
import android.content.Intent
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
import android.provider.Settings
import androidx.concurrent.futures.CallbackToFutureAdapter
import com.google.common.util.concurrent.ListenableFuture
import org.json.JSONException
import org.json.JSONObject
-class FlagManager constructor(val context: Context) : FlagReader {
+class FlagManager constructor(
+ private val context: Context,
+ private val handler: Handler
+) : FlagReader {
companion object {
const val RECEIVING_PACKAGE = "com.android.systemui"
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
@@ -36,6 +42,9 @@
private const val SETTINGS_PREFIX = "systemui/flags"
}
+ private val listeners: MutableSet<FlagReader.Listener> = mutableSetOf()
+ private val settingsObserver: ContentObserver = SettingsObserver()
+
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
val knownFlagMap = Flags.collectFlags()
// Possible todo in the future: query systemui async to actually get the known flag ids.
@@ -82,6 +91,27 @@
}
}
+ override fun addListener(listener: FlagReader.Listener) {
+ synchronized(listeners) {
+ val registerNeeded = listeners.isEmpty()
+ listeners.add(listener)
+ if (registerNeeded) {
+ context.contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(SETTINGS_PREFIX), true, settingsObserver)
+ }
+ }
+ }
+
+ override fun removeListener(listener: FlagReader.Listener) {
+ synchronized(listeners) {
+ val isRegistered = !listeners.isEmpty()
+ listeners.remove(listener)
+ if (isRegistered && listeners.isEmpty()) {
+ context.contentResolver.unregisterContentObserver(settingsObserver)
+ }
+ }
+ }
+
private fun createIntent(id: Int): Intent {
val intent = Intent(ACTION_SET_FLAG)
intent.setPackage(RECEIVING_PACKAGE)
@@ -90,7 +120,7 @@
return intent
}
- fun keyToSettingsPrefix(key: Int): String? {
+ fun keyToSettingsPrefix(key: Int): String {
return SETTINGS_PREFIX + "/" + key
}
@@ -101,6 +131,22 @@
false
}
}
+
+ inner class SettingsObserver : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == null) {
+ return
+ }
+ val parts = uri.pathSegments
+ val idStr = parts[parts.size - 1]
+ try {
+ val id = idStr.toInt()
+ listeners.forEach { l -> l.onFlagChanged(id) }
+ } catch (e: NumberFormatException) {
+ // no-op
+ }
+ }
+ }
}
class InvalidFlagStorageException : Exception("Data found but is invalid")
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
index ef8ec37..ee6dea5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagReader.kt
@@ -31,7 +31,7 @@
fun removeListener(listener: Listener) {}
/** A simple listener to be alerted when a flag changes. */
- interface Listener {
+ fun interface Listener {
/** */
fun onFlagChanged(id: Int)
}
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 3768123..9311966 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -129,10 +129,14 @@
}
@Override
- public void addListener(Listener run) {}
+ public void addListener(Listener run) {
+ mFlagManager.addListener(run);
+ }
@Override
- public void removeListener(Listener run) {}
+ public void removeListener(Listener run) {
+ mFlagManager.removeListener(run);
+ }
private void restartSystemUI() {
Log.i(TAG, "Restarting SystemUI");
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
index 0fe0fee..bee4d7d 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
@@ -17,14 +17,19 @@
package com.android.systemui.flags
import android.content.Context
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.settings.SettingsUtilModule
import dagger.Module
import dagger.Provides
-@Module
+@Module(includes = [
+ SettingsUtilModule::class
+])
object FlagsModule {
@JvmStatic
@Provides
- fun provideFlagManager(context: Context): FlagManager {
- return FlagManager(context)
+ fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
+ return FlagManager(context, handler)
}
}
\ No newline at end of file