[Settings] Support pure switch of inline toggle of Settings Injection v2
Bug: 132808482
Test: robotest
Change-Id: Ib24614fb46fe990925edad721e3b7d5d032854fc
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index e459ab8..70536c0 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -583,12 +583,7 @@
// Generally the items that are will be changing from these updates will
// not be in the top list of tiles, so run it in the background and the
// SettingsBaseActivity will pick up on the updates automatically.
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- doUpdateTilesList();
- }
- });
+ AsyncTask.execute(() -> doUpdateTilesList());
}
private void doUpdateTilesList() {
@@ -648,7 +643,6 @@
|| somethingChanged;
if (UserHandle.MU_ENABLED && !isAdmin) {
-
// When on restricted users, disable all extra categories (but only the settings ones).
final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
synchronized (categories) {
diff --git a/src/com/android/settings/dashboard/CategoryManager.java b/src/com/android/settings/dashboard/CategoryManager.java
index 5cc75c8..525b6f8 100644
--- a/src/com/android/settings/dashboard/CategoryManager.java
+++ b/src/com/android/settings/dashboard/CategoryManager.java
@@ -27,6 +27,7 @@
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
@@ -189,21 +190,31 @@
/**
* Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
- * same intent.
+ * same intent for ActivityTile, and also the ones having the same description for ProviderTile.
*/
@VisibleForTesting
synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
final DashboardCategory category = categoryEntry.getValue();
final int count = category.getTilesCount();
+ final Set<String> descriptions = new ArraySet<>();
final Set<ComponentName> components = new ArraySet<>();
for (int i = count - 1; i >= 0; i--) {
final Tile tile = category.getTile(i);
- final ComponentName tileComponent = tile.getIntent().getComponent();
- if (components.contains(tileComponent)) {
- category.removeTile(i);
+ if (tile instanceof ProviderTile) {
+ final String desc = tile.getDescription();
+ if (descriptions.contains(desc)) {
+ category.removeTile(i);
+ } else {
+ descriptions.add(desc);
+ }
} else {
- components.add(tileComponent);
+ final ComponentName tileComponent = tile.getIntent().getComponent();
+ if (components.contains(tileComponent)) {
+ category.removeTile(i);
+ } else {
+ components.add(tileComponent);
+ }
}
}
}
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
index 9f562a0..8c872f0 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
@@ -44,19 +44,22 @@
String getDashboardKeyForTile(Tile tile);
/**
- * Binds preference to data provided by tile.
+ * Binds preference to data provided by tile and gets dynamic data observers.
*
* @param activity If tile contains intent to launch, it will be launched from this activity
- * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to rounded icon.
+ * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to
+ * rounded icon.
* @param sourceMetricsCategory The context (source) from which an action is performed
* @param pref The preference to bind data
* @param tile The binding data
* @param key They key for preference. If null, we will generate one from tile data
* @param baseOrder The order offset value. When binding, pref's order is determined by
* both this value and tile's own priority.
+ * @return The list of dynamic data observers
*/
- void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
- int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder);
+ List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
+ boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
+ String key, int baseOrder);
/**
* Opens a tile to its destination intent.
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 2cfcbe6..4e541cb 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -18,9 +18,18 @@
import static android.content.Intent.EXTRA_USER;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_CHECKED_STATE;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_PROVIDER_ICON;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_IS_CHECKED;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_ON_CHECKED_CHANGED;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
@@ -40,22 +49,26 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.widget.Toast;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AdaptiveIcon;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -106,40 +119,54 @@
}
@Override
- public void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
- int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder) {
+ public List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
+ boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
+ String key, int baseOrder) {
if (pref == null) {
- return;
+ return null;
}
if (!TextUtils.isEmpty(key)) {
pref.setKey(key);
} else {
pref.setKey(getDashboardKeyForTile(tile));
}
- bindTitle(pref, tile);
- bindSummary(pref, tile);
+ final List<DynamicDataObserver> outObservers = new ArrayList<>();
+ DynamicDataObserver observer = bindTitleAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
+ observer = bindSummaryAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
+ observer = bindSwitchAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
bindIcon(pref, tile, forceRoundedIcon);
- final Bundle metadata = tile.getMetaData();
- String clsName = null;
- String action = null;
- if (metadata != null) {
- clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
- action = metadata.getString(META_DATA_KEY_INTENT_ACTION);
- }
- if (!TextUtils.isEmpty(clsName)) {
- pref.setFragment(clsName);
- } else {
- final Intent intent = new Intent(tile.getIntent());
- intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
- sourceMetricsCategory);
- if (action != null) {
- intent.setAction(action);
+ if (tile instanceof ActivityTile) {
+ final Bundle metadata = tile.getMetaData();
+ String clsName = null;
+ String action = null;
+ if (metadata != null) {
+ clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
+ action = metadata.getString(META_DATA_KEY_INTENT_ACTION);
}
- pref.setOnPreferenceClickListener(preference -> {
- launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory);
- return true;
- });
+ if (!TextUtils.isEmpty(clsName)) {
+ pref.setFragment(clsName);
+ } else {
+ final Intent intent = new Intent(tile.getIntent());
+ intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
+ sourceMetricsCategory);
+ if (action != null) {
+ intent.setAction(action);
+ }
+ pref.setOnPreferenceClickListener(preference -> {
+ launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory);
+ return true;
+ });
+ }
}
if (tile.hasOrder()) {
@@ -153,6 +180,7 @@
pref.setOrder(order + baseOrder);
}
}
+ return outObservers.isEmpty() ? null : outObservers;
}
@Override
@@ -170,11 +198,35 @@
launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY);
}
- private void bindTitle(Preference preference, Tile tile) {
+ private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) {
+ return new DynamicDataObserver() {
+ @Override
+ public Uri getUri() {
+ return uri;
+ }
+
+ @Override
+ public void onDataChanged() {
+ switch (method) {
+ case METHOD_GET_DYNAMIC_TITLE:
+ refreshTitle(uri, pref);
+ break;
+ case METHOD_GET_DYNAMIC_SUMMARY:
+ refreshSummary(uri, pref);
+ break;
+ case METHOD_IS_CHECKED:
+ refreshSwitch(uri, pref);
+ break;
+ }
+ }
+ };
+ }
+
+ private DynamicDataObserver bindTitleAndGetObserver(Preference preference, Tile tile) {
final CharSequence title = tile.getTitle(mContext.getApplicationContext());
if (title != null) {
preference.setTitle(title);
- return;
+ return null;
}
if (tile.getMetaData() != null && tile.getMetaData().containsKey(
META_DATA_PREFERENCE_TITLE_URI)) {
@@ -182,9 +234,12 @@
// to avoid preference height change.
preference.setTitle(R.string.summary_placeholder);
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_TITLE_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_TITLE_URI,
+ METHOD_GET_DYNAMIC_TITLE);
refreshTitle(uri, preference);
+ return createDynamicDataObserver(METHOD_GET_DYNAMIC_TITLE, uri, preference);
}
+ return null;
}
private void refreshTitle(Uri uri, Preference preference) {
@@ -196,7 +251,7 @@
});
}
- private void bindSummary(Preference preference, Tile tile) {
+ private DynamicDataObserver bindSummaryAndGetObserver(Preference preference, Tile tile) {
final CharSequence summary = tile.getSummary(mContext);
if (summary != null) {
preference.setSummary(summary);
@@ -206,11 +261,14 @@
// to avoid preference height change.
preference.setSummary(R.string.summary_placeholder);
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SUMMARY_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SUMMARY_URI,
+ METHOD_GET_DYNAMIC_SUMMARY);
refreshSummary(uri, preference);
+ return createDynamicDataObserver(METHOD_GET_DYNAMIC_SUMMARY, uri, preference);
} else {
preference.setSummary(R.string.summary_placeholder);
}
+ return null;
}
private void refreshSummary(Uri uri, Preference preference) {
@@ -222,6 +280,70 @@
});
}
+ private DynamicDataObserver bindSwitchAndGetObserver(Preference preference, Tile tile) {
+ if (!tile.hasSwitch()) {
+ return null;
+ }
+
+ final Uri onCheckedChangedUri = TileUtils.getCompleteUri(tile,
+ META_DATA_PREFERENCE_SWITCH_URI, METHOD_ON_CHECKED_CHANGED);
+ preference.setOnPreferenceChangeListener((pref, newValue) -> {
+ onCheckedChanged(onCheckedChangedUri, pref, (boolean) newValue);
+ return true;
+ });
+
+ final Uri isCheckedUri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SWITCH_URI,
+ METHOD_IS_CHECKED);
+ setSwitchEnabled(preference, false);
+ refreshSwitch(isCheckedUri, preference);
+ return createDynamicDataObserver(METHOD_IS_CHECKED, isCheckedUri, preference);
+ }
+
+ private void onCheckedChanged(Uri uri, Preference pref, boolean checked) {
+ setSwitchEnabled(pref, false);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Map<String, IContentProvider> providerMap = new ArrayMap<>();
+ final Bundle result = TileUtils.putBooleanToUri(mContext, uri, providerMap,
+ EXTRA_SWITCH_CHECKED_STATE, checked);
+
+ ThreadUtils.postOnMainThread(() -> {
+ setSwitchEnabled(pref, true);
+ final boolean error = result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR);
+ if (!error) {
+ return;
+ }
+
+ setSwitchChecked(pref, !checked);
+ final String errorMsg = result.getString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE);
+ if (!TextUtils.isEmpty(errorMsg)) {
+ Toast.makeText(mContext, errorMsg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
+ }
+
+ private void refreshSwitch(Uri uri, Preference preference) {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Map<String, IContentProvider> providerMap = new ArrayMap<>();
+ final boolean checked = TileUtils.getBooleanFromUri(mContext, uri, providerMap,
+ EXTRA_SWITCH_CHECKED_STATE);
+ ThreadUtils.postOnMainThread(() -> {
+ setSwitchChecked(preference, checked);
+ setSwitchEnabled(preference, true);
+ });
+ });
+ }
+
+ private void setSwitchChecked(Preference pref, boolean checked) {
+ if (pref instanceof SwitchPreference) {
+ ((SwitchPreference) pref).setChecked(checked);
+ }
+ }
+
+ private void setSwitchEnabled(Preference pref, boolean enabled) {
+ pref.setEnabled(enabled);
+ }
+
@VisibleForTesting
void bindIcon(Preference preference, Tile tile, boolean forceRoundedIcon) {
// Use preference context instead here when get icon from Tile, as we are using the context
@@ -246,7 +368,8 @@
packageName = intent.getComponent().getPackageName();
}
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_ICON_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_ICON_URI,
+ METHOD_GET_PROVIDER_ICON);
final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri(
mContext, packageName, uri, providerMap);
if (iconInfo == null) {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 3575b72..30d6df3 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -17,11 +17,11 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
+import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.CallSuper;
@@ -30,6 +30,7 @@
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
@@ -41,6 +42,7 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.Indexable;
@@ -49,7 +51,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.Objects;
/**
* Base fragment for dashboard style UI containing a list of static and dynamic setting items.
@@ -60,9 +62,11 @@
BasePreferenceController.UiBlockListener {
private static final String TAG = "DashboardFragment";
+ @VisibleForTesting
+ final ArrayMap<String, List<DynamicDataObserver>> mDashboardTilePrefKeys = new ArrayMap<>();
private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers =
new ArrayMap<>();
- private final Set<String> mDashboardTilePrefKeys = new ArraySet<>();
+ private final List<DynamicDataObserver> mRegisteredObservers = new ArrayList<>();
private final List<AbstractPreferenceController> mControllers = new ArrayList<>();
private DashboardFeatureProvider mDashboardFeatureProvider;
@@ -171,6 +175,15 @@
mListeningToCategoryChange = true;
((SettingsBaseActivity) activity).addCategoryListener(this);
}
+ final ContentResolver resolver = getContentResolver();
+ mDashboardTilePrefKeys.values().stream()
+ .filter(Objects::nonNull)
+ .flatMap(List::stream)
+ .forEach(observer -> {
+ if (!mRegisteredObservers.contains(observer)) {
+ registerDynamicDataObserver(resolver, observer);
+ }
+ });
}
@Override
@@ -200,6 +213,7 @@
@Override
public void onStop() {
super.onStop();
+ unregisterDynamicDataObservers(new ArrayList<>(mRegisteredObservers));
if (mListeningToCategoryChange) {
final Activity activity = getActivity();
if (activity instanceof SettingsBaseActivity) {
@@ -325,7 +339,7 @@
* Refresh all preference items, including both static prefs from xml, and dynamic items from
* DashboardCategory.
*/
- private void refreshAllPreferences(final String TAG) {
+ private void refreshAllPreferences(final String tag) {
final PreferenceScreen screen = getPreferenceScreen();
// First remove old preferences.
if (screen != null) {
@@ -336,11 +350,11 @@
// Add resource based tiles.
displayResourceTiles();
- refreshDashboardTiles(TAG);
+ refreshDashboardTiles(tag);
final Activity activity = getActivity();
if (activity != null) {
- Log.d(TAG, "All preferences added, reporting fully drawn");
+ Log.d(tag, "All preferences added, reporting fully drawn");
activity.reportFullyDrawn();
}
@@ -371,59 +385,62 @@
/**
* Refresh preference items backed by DashboardCategory.
*/
- @VisibleForTesting
- void refreshDashboardTiles(final String TAG) {
+ private void refreshDashboardTiles(final String tag) {
final PreferenceScreen screen = getPreferenceScreen();
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
- Log.d(TAG, "NO dashboard tiles for " + TAG);
+ Log.d(tag, "NO dashboard tiles for " + tag);
return;
}
final List<Tile> tiles = category.getTiles();
if (tiles == null) {
- Log.d(TAG, "tile list is empty, skipping category " + category.key);
+ Log.d(tag, "tile list is empty, skipping category " + category.key);
return;
}
// Create a list to track which tiles are to be removed.
- final List<String> remove = new ArrayList<>(mDashboardTilePrefKeys);
+ final Map<String, List<DynamicDataObserver>> remove = new ArrayMap(mDashboardTilePrefKeys);
// Install dashboard tiles.
final boolean forceRoundedIcons = shouldForceRoundedIcon();
for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) {
- Log.d(TAG, "tile does not contain a key, skipping " + tile);
+ Log.d(tag, "tile does not contain a key, skipping " + tile);
continue;
}
if (!displayTile(tile)) {
continue;
}
- if (mDashboardTilePrefKeys.contains(key)) {
+ if (mDashboardTilePrefKeys.containsKey(key)) {
// Have the key already, will rebind.
final Preference preference = screen.findPreference(key);
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
- getMetricsCategory(), preference, tile, key,
+ mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
+ forceRoundedIcons, getMetricsCategory(), preference, tile, key,
mPlaceholderPreferenceController.getOrder());
} else {
// Don't have this key, add it.
- final Preference pref = new Preference(getPrefContext());
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
- getMetricsCategory(), pref, tile, key,
- mPlaceholderPreferenceController.getOrder());
+ final Preference pref = createPreference(tile);
+ final List<DynamicDataObserver> observers =
+ mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
+ forceRoundedIcons, getMetricsCategory(), pref, tile, key,
+ mPlaceholderPreferenceController.getOrder());
screen.addPreference(pref);
- mDashboardTilePrefKeys.add(key);
+ registerDynamicDataObservers(observers);
+ mDashboardTilePrefKeys.put(key, observers);
}
remove.remove(key);
}
// Finally remove tiles that are gone.
- for (String key : remove) {
+ for (Map.Entry<String, List<DynamicDataObserver>> entry : remove.entrySet()) {
+ final String key = entry.getKey();
mDashboardTilePrefKeys.remove(key);
final Preference preference = screen.findPreference(key);
if (preference != null) {
screen.removePreference(preference);
}
+ unregisterDynamicDataObservers(entry.getValue());
}
}
@@ -431,4 +448,41 @@
public void onBlockerWorkFinished(BasePreferenceController controller) {
mBlockerController.countDown(controller.getPreferenceKey());
}
+
+ @VisibleForTesting
+ Preference createPreference(Tile tile) {
+ return tile instanceof ProviderTile
+ ? new SwitchPreference(getPrefContext())
+ : new Preference(getPrefContext());
+ }
+
+ @VisibleForTesting
+ void registerDynamicDataObservers(List<DynamicDataObserver> observers) {
+ if (observers == null || observers.isEmpty()) {
+ return;
+ }
+ final ContentResolver resolver = getContentResolver();
+ observers.forEach(observer -> registerDynamicDataObserver(resolver, observer));
+ }
+
+ private void registerDynamicDataObserver(ContentResolver resolver,
+ DynamicDataObserver observer) {
+ Log.d(TAG, "register observer: @" + Integer.toHexString(observer.hashCode())
+ + ", uri: " + observer.getUri());
+ resolver.registerContentObserver(observer.getUri(), false, observer);
+ mRegisteredObservers.add(observer);
+ }
+
+ private void unregisterDynamicDataObservers(List<DynamicDataObserver> observers) {
+ if (observers == null || observers.isEmpty()) {
+ return;
+ }
+ final ContentResolver resolver = getContentResolver();
+ observers.forEach(observer -> {
+ Log.d(TAG, "unregister observer: @" + Integer.toHexString(observer.hashCode())
+ + ", uri: " + observer.getUri());
+ mRegisteredObservers.remove(observer);
+ resolver.unregisterContentObserver(observer);
+ });
+ }
}
diff --git a/src/com/android/settings/dashboard/DynamicDataObserver.java b/src/com/android/settings/dashboard/DynamicDataObserver.java
new file mode 100644
index 0000000..f5299be
--- /dev/null
+++ b/src/com/android/settings/dashboard/DynamicDataObserver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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.settings.dashboard;
+
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * Observer for updating injected dynamic data.
+ */
+public abstract class DynamicDataObserver extends ContentObserver {
+
+ protected DynamicDataObserver() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ /** Returns the uri of the callback. */
+ public abstract Uri getUri();
+
+ /** Called when data changes. */
+ public abstract void onDataChanged();
+
+ @Override
+ public void onChange(boolean selfChange) {
+ onDataChanged();
+ }
+}