Merge "[Catalyst] Provide AbstractKeyedDataObservable" into main
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index cd03dd7..07b1c9e 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -22,6 +22,7 @@
 import com.google.errorprone.annotations.CanIgnoreReturnValue
 import java.util.WeakHashMap
 import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
 
 /**
  * Callback to be informed of changes in [KeyedObservable] object.
@@ -203,13 +204,71 @@
         }
     }
 
-    fun hasAnyObserver(): Boolean {
+    open fun hasAnyObserver(): Boolean {
         synchronized(observers) { if (observers.isNotEmpty()) return true }
         synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true }
         return false
     }
 }
 
+/** [KeyedDataObservable] that maintains a counter for the observers. */
+abstract class AbstractKeyedDataObservable<K> : KeyedDataObservable<K>() {
+    /**
+     * Counter of observers.
+     *
+     * The value is accurate only when [addObserver] and [removeObserver] are invoked in pairs.
+     */
+    private val counter = AtomicInteger()
+
+    override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) =
+        if (super.addObserver(observer, executor)) {
+            onObserverAdded()
+            true
+        } else {
+            false
+        }
+
+    override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) =
+        if (super.addObserver(key, observer, executor)) {
+            onObserverAdded()
+            true
+        } else {
+            false
+        }
+
+    private fun onObserverAdded() {
+        if (counter.getAndIncrement() == 0) onFirstObserverAdded()
+    }
+
+    /** Callbacks when the first observer is just added. */
+    protected abstract fun onFirstObserverAdded()
+
+    override fun removeObserver(observer: KeyedObserver<K?>) =
+        if (super.removeObserver(observer)) {
+            onObserverRemoved()
+            true
+        } else {
+            false
+        }
+
+    override fun removeObserver(key: K, observer: KeyedObserver<K>) =
+        if (super.removeObserver(key, observer)) {
+            onObserverRemoved()
+            true
+        } else {
+            false
+        }
+
+    private fun onObserverRemoved() {
+        if (counter.decrementAndGet() == 0) onLastObserverRemoved()
+    }
+
+    /** Callbacks when the last observer is just removed. */
+    protected abstract fun onLastObserverRemoved()
+
+    override fun hasAnyObserver() = counter.get() > 0
+}
+
 /** [KeyedObservable] with no-op implementations for all interfaces. */
 open class NoOpKeyedObservable<K> : KeyedObservable<K> {
 
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
index 04d4bfe..d6e7a89 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -20,21 +20,10 @@
 import android.database.ContentObserver
 import android.net.Uri
 import android.util.Log
-import java.util.concurrent.Executor
-import java.util.concurrent.atomic.AtomicInteger
 
 /** Base class of the Settings provider data stores. */
 abstract class SettingsStore(protected val contentResolver: ContentResolver) :
-    KeyedDataObservable<String>(), KeyValueStore {
-
-    /**
-     * Counter of observers.
-     *
-     * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When
-     * an observer is not removed (and its weak reference is garbage collected), the content
-     * observer is not unregistered but this is not a big deal.
-     */
-    private val counter = AtomicInteger()
+    AbstractKeyedDataObservable<String>(), KeyValueStore {
 
     private val contentObserver =
         object : ContentObserver(HandlerExecutor.main) {
@@ -48,49 +37,15 @@
             }
         }
 
-    override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
-        if (super.addObserver(observer, executor)) {
-            onObserverAdded()
-            true
-        } else {
-            false
-        }
+    /** The URI to watch for any key change. */
+    protected abstract val uri: Uri
 
-    override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
-        if (super.addObserver(key, observer, executor)) {
-            onObserverAdded()
-            true
-        } else {
-            false
-        }
-
-    private fun onObserverAdded() {
-        if (counter.getAndIncrement() != 0) return
+    override fun onFirstObserverAdded() {
         Log.i(tag, "registerContentObserver")
         contentResolver.registerContentObserver(uri, true, contentObserver)
     }
 
-    /** The URI to watch for any key change. */
-    protected abstract val uri: Uri
-
-    override fun removeObserver(observer: KeyedObserver<String?>) =
-        if (super.removeObserver(observer)) {
-            onObserverRemoved()
-            true
-        } else {
-            false
-        }
-
-    override fun removeObserver(key: String, observer: KeyedObserver<String>) =
-        if (super.removeObserver(key, observer)) {
-            onObserverRemoved()
-            true
-        } else {
-            false
-        }
-
-    private fun onObserverRemoved() {
-        if (counter.decrementAndGet() != 0) return
+    override fun onLastObserverRemoved() {
         Log.i(tag, "unregisterContentObserver")
         contentResolver.unregisterContentObserver(contentObserver)
     }