Split ChangeReason and decouple it from Observer

Observer is flexible to be used for other scenarios.

Bug: 325144964
Test: atest SettingsLibDataStoreTest
Change-Id: I8a8ce690edee37bdb1ba20ce06a508fbdddb2c15
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
index 8242347..b4a9172 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -138,7 +138,7 @@
         private fun notifyBackupManager(key: Any?, reason: Int) {
             val name = storage.name
             // prefer not triggering backup immediately after restore
-            if (reason == ChangeReason.RESTORE) {
+            if (reason == DataChangeReason.RESTORE) {
                 Log.d(
                     LOG_TAG,
                     "Notify BackupManager dataChanged ignored for restore: storage=$name key=$key"
@@ -161,8 +161,8 @@
 
         fun notifyRestoreFinished() {
             when (storage) {
-                is KeyedObservable<*> -> storage.notifyChange(ChangeReason.RESTORE)
-                is Observable -> storage.notifyChange(ChangeReason.RESTORE)
+                is KeyedObservable<*> -> storage.notifyChange(DataChangeReason.RESTORE)
+                is Observable -> storage.notifyChange(DataChangeReason.RESTORE)
             }
         }
     }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
new file mode 100644
index 0000000..145fabe
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.annotation.IntDef
+
+/** The reason of data change. */
+@IntDef(
+    DataChangeReason.UNKNOWN,
+    DataChangeReason.UPDATE,
+    DataChangeReason.DELETE,
+    DataChangeReason.RESTORE,
+    DataChangeReason.SYNC_ACROSS_PROFILES,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class DataChangeReason {
+    companion object {
+        /** Unknown reason of the change. */
+        const val UNKNOWN = 0
+        /** Data is updated. */
+        const val UPDATE = 1
+        /** Data is deleted. */
+        const val DELETE = 2
+        /** Data is restored from backup/restore framework. */
+        const val RESTORE = 3
+        /** Data is synced from another profile (e.g. personal profile to work profile). */
+        const val SYNC_ACROSS_PROFILES = 4
+    }
+}
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 3ed4d46..ede7c63d 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -37,7 +37,7 @@
      * @param reason the reason of change
      * @see KeyedObservable.addObserver
      */
-    fun onKeyChanged(key: K, @ChangeReason reason: Int)
+    fun onKeyChanged(key: K, reason: Int)
 }
 
 /**
@@ -89,7 +89,7 @@
      *
      * @param reason reason of the change
      */
-    fun notifyChange(@ChangeReason reason: Int)
+    fun notifyChange(reason: Int)
 
     /**
      * Notifies observers that a change occurs on given key.
@@ -99,7 +99,7 @@
      * @param key key of the change
      * @param reason reason of the change
      */
-    fun notifyChange(key: K, @ChangeReason reason: Int)
+    fun notifyChange(key: K, reason: Int)
 }
 
 /** A thread safe implementation of [KeyedObservable]. */
@@ -141,7 +141,7 @@
         }
     }
 
-    override fun notifyChange(@ChangeReason reason: Int) {
+    override fun notifyChange(reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val observers = synchronized(observers) { observers.entries.toTypedArray() }
         val keyedObservers = synchronized(keyedObservers) { keyedObservers.copy() }
@@ -165,7 +165,7 @@
         return result
     }
 
-    override fun notifyChange(key: K, @ChangeReason reason: Int) {
+    override fun notifyChange(key: K, reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val observers = synchronized(observers) { observers.entries.toTypedArray() }
         val keyedObservers =
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
index 6d0ca669..98d0f6e 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
@@ -18,34 +18,9 @@
 
 import androidx.annotation.AnyThread
 import androidx.annotation.GuardedBy
-import androidx.annotation.IntDef
 import java.util.WeakHashMap
 import java.util.concurrent.Executor
 
-/** The reason of a change. */
-@IntDef(
-    ChangeReason.UNKNOWN,
-    ChangeReason.UPDATE,
-    ChangeReason.DELETE,
-    ChangeReason.RESTORE,
-    ChangeReason.SYNC_ACROSS_PROFILES,
-)
-@Retention(AnnotationRetention.SOURCE)
-annotation class ChangeReason {
-    companion object {
-        /** Unknown reason of the change. */
-        const val UNKNOWN = 0
-        /** Data is updated. */
-        const val UPDATE = 1
-        /** Data is deleted. */
-        const val DELETE = 2
-        /** Data is restored from backup/restore framework. */
-        const val RESTORE = 3
-        /** Data is synced from another profile (e.g. personal profile to work profile). */
-        const val SYNC_ACROSS_PROFILES = 4
-    }
-}
-
 /**
  * Callback to be informed of changes in [Observable] object.
  *
@@ -60,7 +35,7 @@
      * @param reason the reason of change
      * @see [Observable.addObserver] for the notices.
      */
-    fun onChanged(@ChangeReason reason: Int)
+    fun onChanged(reason: Int)
 }
 
 /** An observable object allows to observe change with [Observer]. */
@@ -90,7 +65,7 @@
      *
      * @param reason reason of the change
      */
-    fun notifyChange(@ChangeReason reason: Int)
+    fun notifyChange(reason: Int)
 }
 
 /** A thread safe implementation of [Observable]. */
@@ -110,7 +85,7 @@
         synchronized(observers) { observers.remove(observer) }
     }
 
-    override fun notifyChange(@ChangeReason reason: Int) {
+    override fun notifyChange(reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val entries = synchronized(observers) { observers.entries.toTypedArray() }
         for (entry in entries) {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 9f9c0d8..20a95d7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -83,10 +83,10 @@
     private val sharedPreferencesListener =
         SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
             if (key != null) {
-                notifyChange(key, ChangeReason.UPDATE)
+                notifyChange(key, DataChangeReason.UPDATE)
             } else {
                 // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
-                notifyChange(ChangeReason.DELETE)
+                notifyChange(DataChangeReason.DELETE)
             }
         }
 
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
index d8f5028..19c574a 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
@@ -157,9 +157,9 @@
 
         manager.onRestoreFinished()
 
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
-        verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE)
-        verify(observer).onChanged(ChangeReason.RESTORE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged(null, DataChangeReason.RESTORE)
+        verify(observer).onChanged(DataChangeReason.RESTORE)
         if (isRobolectric()) {
             Shadows.shadowOf(BackupManager(application)).apply {
                 assertThat(isDataChanged).isFalse()
@@ -186,8 +186,8 @@
             assertThat(dataChangedCount).isEqualTo(0)
         }
 
-        fileStorage.notifyChange(ChangeReason.UPDATE)
-        verify(observer).onChanged(ChangeReason.UPDATE)
+        fileStorage.notifyChange(DataChangeReason.UPDATE)
+        verify(observer).onChanged(DataChangeReason.UPDATE)
         verify(keyedObserver, never()).onKeyChanged(any(), any())
         verify(anyKeyObserver, never()).onKeyChanged(any(), any())
         reset(observer)
@@ -196,10 +196,10 @@
             assertThat(dataChangedCount).isEqualTo(1)
         }
 
-        keyedStorage.notifyChange("key", ChangeReason.DELETE)
+        keyedStorage.notifyChange("key", DataChangeReason.DELETE)
         verify(observer, never()).onChanged(any())
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
-        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE)
+        verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.DELETE)
         backupManager?.apply {
             assertThat(isDataChanged).isTrue()
             assertThat(dataChangedCount).isEqualTo(2)
@@ -207,11 +207,11 @@
         reset(keyedObserver)
 
         // backup manager is not notified for restore event
-        fileStorage.notifyChange(ChangeReason.RESTORE)
-        keyedStorage.notifyChange("key", ChangeReason.RESTORE)
-        verify(observer).onChanged(ChangeReason.RESTORE)
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
-        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE)
+        fileStorage.notifyChange(DataChangeReason.RESTORE)
+        keyedStorage.notifyChange("key", DataChangeReason.RESTORE)
+        verify(observer).onChanged(DataChangeReason.RESTORE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.RESTORE)
         backupManager?.apply {
             assertThat(isDataChanged).isTrue()
             assertThat(dataChangedCount).isEqualTo(2)
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index 8638b2f..0fdecb0 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -77,7 +77,7 @@
         var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
         keyedObservable.addObserver(observer!!, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -85,7 +85,7 @@
         System.gc()
         System.runFinalization()
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -95,7 +95,7 @@
         var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
         keyedObservable.addObserver(key1, keyObserver!!, executor1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -103,7 +103,7 @@
         System.gc()
         System.runFinalization()
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -112,16 +112,16 @@
         keyedObservable.addObserver(observer1, executor1)
         keyedObservable.addObserver(observer2, executor2)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
-        verify(observer2).onKeyChanged(null, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
+        verify(observer2).onKeyChanged(null, DataChangeReason.UPDATE)
 
         reset(observer1, observer2)
         keyedObservable.removeObserver(observer2)
 
-        keyedObservable.notifyChange(ChangeReason.DELETE)
-        verify(observer1).onKeyChanged(null, ChangeReason.DELETE)
-        verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE)
+        keyedObservable.notifyChange(DataChangeReason.DELETE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.DELETE)
+        verify(observer2, never()).onKeyChanged(null, DataChangeReason.DELETE)
     }
 
     @Test
@@ -129,16 +129,16 @@
         keyedObservable.addObserver(key1, keyedObserver1, executor1)
         keyedObservable.addObserver(key2, keyedObserver2, executor2)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
 
         reset(keyedObserver1, keyedObserver2)
         keyedObservable.removeObserver(key1, keyedObserver1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.DELETE)
-        verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE)
-        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE)
+        keyedObservable.notifyChange(key1, DataChangeReason.DELETE)
+        verify(keyedObserver1, never()).onKeyChanged(key1, DataChangeReason.DELETE)
+        verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.DELETE)
     }
 
     @Test
@@ -147,24 +147,24 @@
         keyedObservable.addObserver(key1, keyedObserver1, executor1)
         keyedObservable.addObserver(key2, keyedObserver2, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE)
 
         reset(observer1, keyedObserver1, keyedObserver2)
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
 
-        verify(observer1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2, never()).onKeyChanged(key1, ChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2, never()).onKeyChanged(key1, DataChangeReason.UPDATE)
 
         reset(observer1, keyedObserver1, keyedObserver2)
-        keyedObservable.notifyChange(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key2, DataChangeReason.UPDATE)
 
-        verify(observer1).onKeyChanged(key2, ChangeReason.UPDATE)
-        verify(keyedObserver1, never()).onKeyChanged(key2, ChangeReason.UPDATE)
-        verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(key2, DataChangeReason.UPDATE)
+        verify(keyedObserver1, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
+        verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE)
     }
 
     @Test
@@ -176,7 +176,7 @@
 
         keyedObservable.addObserver(observer, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         keyedObservable.removeObserver(observer)
     }
 
@@ -189,7 +189,7 @@
 
         keyedObservable.addObserver(key1, keyObserver, executor1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         keyedObservable.removeObserver(key1, keyObserver)
     }
 }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
index 173c2b1..5d0303c 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -58,7 +58,7 @@
         var observer: Observer? = Observer { counter.incrementAndGet() }
         observable.addObserver(observer!!, executor1)
 
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -66,7 +66,7 @@
         System.gc()
         System.runFinalization()
 
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -75,17 +75,17 @@
         observable.addObserver(observer1, executor1)
         observable.addObserver(observer2, executor2)
 
-        observable.notifyChange(ChangeReason.DELETE)
+        observable.notifyChange(DataChangeReason.DELETE)
 
-        verify(observer1).onChanged(ChangeReason.DELETE)
-        verify(observer2).onChanged(ChangeReason.DELETE)
+        verify(observer1).onChanged(DataChangeReason.DELETE)
+        verify(observer2).onChanged(DataChangeReason.DELETE)
 
         reset(observer1, observer2)
         observable.removeObserver(observer2)
 
-        observable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onChanged(ChangeReason.UPDATE)
-        verify(observer2, never()).onChanged(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onChanged(DataChangeReason.UPDATE)
+        verify(observer2, never()).onChanged(DataChangeReason.UPDATE)
     }
 
     @Test
@@ -93,7 +93,7 @@
         // ConcurrentModificationException is raised if it is not implemented correctly
         val observer = Observer { observable.addObserver(observer1, executor1) }
         observable.addObserver(observer, executor1)
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         observable.removeObserver(observer)
     }
 }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
index fec7d75..a135d77 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
@@ -80,13 +80,13 @@
         storage.addObserver("key", keyedObserver, executor)
 
         storage.sharedPreferences.edit().putString("key", "string").applySync()
-        verify(observer).onKeyChanged("key", ChangeReason.UPDATE)
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE)
+        verify(observer).onKeyChanged("key", DataChangeReason.UPDATE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.UPDATE)
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
             storage.sharedPreferences.edit().clear().applySync()
-            verify(observer).onKeyChanged(null, ChangeReason.DELETE)
-            verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+            verify(observer).onKeyChanged(null, DataChangeReason.DELETE)
+            verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE)
         }
     }