summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt6
-rw-r--r--core/java/android/app/SharedPreferencesImpl.java66
-rw-r--r--core/java/android/content/SharedPreferences.java58
3 files changed, 125 insertions, 5 deletions
diff --git a/api/current.txt b/api/current.txt
index 0a8db4611043..a7b38a5d5b10 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10834,7 +10834,9 @@ package android.content {
method @Nullable public String getString(String, @Nullable String);
method @Nullable public java.util.Set<java.lang.String> getStringSet(String, @Nullable java.util.Set<java.lang.String>);
method public void registerOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener);
+ method public default void registerOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener);
method public void unregisterOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener);
+ method public default void unregisterOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener);
}
public static interface SharedPreferences.Editor {
@@ -10854,6 +10856,10 @@ package android.content {
method public void onSharedPreferenceChanged(android.content.SharedPreferences, String);
}
+ public static interface SharedPreferences.OnSharedPreferencesClearListener {
+ method public void onSharedPreferencesClear(@NonNull android.content.SharedPreferences, @NonNull java.util.Set<java.lang.String>);
+ }
+
public class SyncAdapterType implements android.os.Parcelable {
ctor public SyncAdapterType(String, String, boolean, boolean);
ctor public SyncAdapterType(android.os.Parcel);
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 0f8976fe924a..9162626e1b37 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.SharedPreferences;
@@ -25,6 +26,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.system.StructTimespec;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -92,6 +94,10 @@ final class SharedPreferencesImpl implements SharedPreferences {
private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
+ @GuardedBy("mLock")
+ private final WeakHashMap<OnSharedPreferencesClearListener, Object> mClearListeners =
+ new WeakHashMap<>();
+
/** Current memory state (always increasing) */
@GuardedBy("this")
private long mCurrentMemoryStateGeneration;
@@ -252,6 +258,28 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
}
+ @Override
+ public void registerOnSharedPreferencesClearListener(
+ @NonNull OnSharedPreferencesClearListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null.");
+ }
+ synchronized (mLock) {
+ mClearListeners.put(listener, CONTENT);
+ }
+ }
+
+ @Override
+ public void unregisterOnSharedPreferencesClearListener(
+ @NonNull OnSharedPreferencesClearListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null.");
+ }
+ synchronized (mLock) {
+ mClearListeners.remove(listener);
+ }
+ }
+
@GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
@@ -361,7 +389,9 @@ final class SharedPreferencesImpl implements SharedPreferences {
private static class MemoryCommitResult {
final long memoryStateGeneration;
@Nullable final List<String> keysModified;
+ @Nullable final Set<String> keysCleared;
@Nullable final Set<OnSharedPreferenceChangeListener> listeners;
+ @Nullable final Set<OnSharedPreferencesClearListener> clearListeners;
final Map<String, Object> mapToWriteToDisk;
final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
@@ -371,10 +401,14 @@ final class SharedPreferencesImpl implements SharedPreferences {
private MemoryCommitResult(long memoryStateGeneration, @Nullable List<String> keysModified,
@Nullable Set<OnSharedPreferenceChangeListener> listeners,
+ @Nullable Set<String> keysCleared,
+ @Nullable Set<OnSharedPreferencesClearListener> clearListeners,
Map<String, Object> mapToWriteToDisk) {
this.memoryStateGeneration = memoryStateGeneration;
this.keysModified = keysModified;
this.listeners = listeners;
+ this.keysCleared = keysCleared;
+ this.clearListeners = clearListeners;
this.mapToWriteToDisk = mapToWriteToDisk;
}
@@ -492,13 +526,16 @@ final class SharedPreferencesImpl implements SharedPreferences {
// SharedPreferences instance back, which has the
// changes reflected in memory.
notifyListeners(mcr);
+ notifyClearListeners(mcr);
}
// Returns true if any changes were made
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
List<String> keysModified = null;
+ Set<String> keysCleared = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
+ Set<OnSharedPreferencesClearListener> clearListeners = null;
Map<String, Object> mapToWriteToDisk;
synchronized (SharedPreferencesImpl.this.mLock) {
@@ -520,12 +557,20 @@ final class SharedPreferencesImpl implements SharedPreferences {
keysModified = new ArrayList<String>();
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
+ boolean hasClearListeners = !mClearListeners.isEmpty();
+ if (hasClearListeners) {
+ keysCleared = new ArraySet<>();
+ clearListeners = new HashSet<>(mClearListeners.keySet());
+ }
synchronized (mEditorLock) {
boolean changesMade = false;
if (mClear) {
if (!mapToWriteToDisk.isEmpty()) {
+ if (hasClearListeners) {
+ keysCleared.addAll(mapToWriteToDisk.keySet());
+ }
changesMade = true;
mapToWriteToDisk.clear();
}
@@ -569,7 +614,7 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
}
return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
- mapToWriteToDisk);
+ keysCleared, clearListeners, mapToWriteToDisk);
}
@Override
@@ -596,6 +641,7 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
}
notifyListeners(mcr);
+ notifyClearListeners(mcr);
return mcr.writeToDiskResult;
}
@@ -618,6 +664,24 @@ final class SharedPreferencesImpl implements SharedPreferences {
ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr));
}
}
+
+ private void notifyClearListeners(final MemoryCommitResult mcr) {
+ if (mcr.clearListeners == null || mcr.keysCleared == null
+ || mcr.keysCleared.isEmpty()) {
+ return;
+ }
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ for (OnSharedPreferencesClearListener listener : mcr.clearListeners) {
+ if (listener != null) {
+ listener.onSharedPreferencesClear(SharedPreferencesImpl.this,
+ mcr.keysCleared);
+ }
+ }
+ } else {
+ // Run this function on the main thread.
+ ActivityThread.sMainThreadHandler.post(() -> notifyClearListeners(mcr));
+ }
+ }
}
/**
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 877dfee138da..9d87e2550c95 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import java.util.Map;
@@ -58,7 +59,9 @@ public interface SharedPreferences {
* <p>This callback will be run on your main thread.
*
* <p><em>Note: This callback will not be triggered when preferences are cleared via
- * {@link Editor#clear()}.</em>
+ * {@link Editor#clear()}. However, from {@link android.os.Build.VERSION_CODES#R Android R}
+ * onwards, you can use {@link OnSharedPreferencesClearListener} to register for
+ * {@link Editor#clear()} callbacks.</em>
*
* @param sharedPreferences The {@link SharedPreferences} that received
* the change.
@@ -67,7 +70,23 @@ public interface SharedPreferences {
*/
void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
}
-
+
+ /**
+ * Interface definition for a callback to be invoked when shared preferences are cleared.
+ */
+ public interface OnSharedPreferencesClearListener {
+ /**
+ * Called when shared preferences are cleared via {@link Editor#clear()}.
+ *
+ * <p>This callback will be run on your main thread.
+ *
+ * @param sharedPreferences The {@link SharedPreferences} that received the change.
+ * @param keys The set of keys that were cleared.
+ */
+ void onSharedPreferencesClear(@NonNull SharedPreferences sharedPreferences,
+ @NonNull Set<String> keys);
+ }
+
/**
* Interface used for modifying values in a {@link SharedPreferences}
* object. All changes you make in an editor are batched, and not copied
@@ -378,12 +397,43 @@ public interface SharedPreferences {
* @see #unregisterOnSharedPreferenceChangeListener
*/
void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
-
+
/**
* Unregisters a previous callback.
- *
+ *
* @param listener The callback that should be unregistered.
* @see #registerOnSharedPreferenceChangeListener
*/
void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
+
+ /**
+ * Registers a callback to be invoked when preferences are cleared via {@link Editor#clear()}.
+ *
+ * <p class="caution"><strong>Caution:</strong> The preference manager does
+ * not currently store a strong reference to the listener. You must store a
+ * strong reference to the listener, or it will be susceptible to garbage
+ * collection. We recommend you keep a reference to the listener in the
+ * instance data of an object that will exist as long as you need the
+ * listener.</p>
+ *
+ * @param listener The callback that will run.
+ * @see #unregisterOnSharedPreferencesClearListener
+ */
+ default void registerOnSharedPreferencesClearListener(
+ @NonNull OnSharedPreferencesClearListener listener) {
+ throw new UnsupportedOperationException(
+ "registerOnSharedPreferencesClearListener not implemented");
+ }
+
+ /**
+ * Unregisters a previous callback for {@link Editor#clear()}.
+ *
+ * @param listener The callback that should be unregistered.
+ * @see #registerOnSharedPreferencesClearListener
+ */
+ default void unregisterOnSharedPreferencesClearListener(
+ @NonNull OnSharedPreferencesClearListener listener) {
+ throw new UnsupportedOperationException(
+ "unregisterOnSharedPreferencesClearListener not implemented");
+ }
}