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