[Settings] Support pure switch of inline toggle of Settings Injection v2

Bug: 132808482
Test: robotest
Change-Id: Ib24614fb46fe990925edad721e3b7d5d032854fc
This commit is contained in:
Jason Chiu
2019-10-24 17:30:34 +08:00
parent 9096329b22
commit bac5987c8e
12 changed files with 664 additions and 262 deletions

View File

@@ -583,12 +583,7 @@ public class SettingsActivity extends SettingsBaseActivity
// Generally the items that are will be changing from these updates will // 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 // not be in the top list of tiles, so run it in the background and the
// SettingsBaseActivity will pick up on the updates automatically. // SettingsBaseActivity will pick up on the updates automatically.
AsyncTask.execute(new Runnable() { AsyncTask.execute(() -> doUpdateTilesList());
@Override
public void run() {
doUpdateTilesList();
}
});
} }
private void doUpdateTilesList() { private void doUpdateTilesList() {
@@ -648,7 +643,6 @@ public class SettingsActivity extends SettingsBaseActivity
|| somethingChanged; || somethingChanged;
if (UserHandle.MU_ENABLED && !isAdmin) { if (UserHandle.MU_ENABLED && !isAdmin) {
// When on restricted users, disable all extra categories (but only the settings ones). // When on restricted users, disable all extra categories (but only the settings ones).
final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories(); final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
synchronized (categories) { synchronized (categories) {

View File

@@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settingslib.applications.InterestingConfigChanges; import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils; import com.android.settingslib.drawer.TileUtils;
@@ -189,16 +190,25 @@ public class CategoryManager {
/** /**
* Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the * 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 @VisibleForTesting
synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) { synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) { for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
final DashboardCategory category = categoryEntry.getValue(); final DashboardCategory category = categoryEntry.getValue();
final int count = category.getTilesCount(); final int count = category.getTilesCount();
final Set<String> descriptions = new ArraySet<>();
final Set<ComponentName> components = new ArraySet<>(); final Set<ComponentName> components = new ArraySet<>();
for (int i = count - 1; i >= 0; i--) { for (int i = count - 1; i >= 0; i--) {
final Tile tile = category.getTile(i); final Tile tile = category.getTile(i);
if (tile instanceof ProviderTile) {
final String desc = tile.getDescription();
if (descriptions.contains(desc)) {
category.removeTile(i);
} else {
descriptions.add(desc);
}
} else {
final ComponentName tileComponent = tile.getIntent().getComponent(); final ComponentName tileComponent = tile.getIntent().getComponent();
if (components.contains(tileComponent)) { if (components.contains(tileComponent)) {
category.removeTile(i); category.removeTile(i);
@@ -209,3 +219,4 @@ public class CategoryManager {
} }
} }
} }
}

View File

@@ -44,19 +44,22 @@ public interface DashboardFeatureProvider {
String getDashboardKeyForTile(Tile tile); 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 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 sourceMetricsCategory The context (source) from which an action is performed
* @param pref The preference to bind data * @param pref The preference to bind data
* @param tile The binding data * @param tile The binding data
* @param key They key for preference. If null, we will generate one from tile 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 * @param baseOrder The order offset value. When binding, pref's order is determined by
* both this value and tile's own priority. * both this value and tile's own priority.
* @return The list of dynamic data observers
*/ */
void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon, List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder); boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
String key, int baseOrder);
/** /**
* Opens a tile to its destination intent. * Opens a tile to its destination intent.

View File

@@ -18,9 +18,18 @@ package com.android.settings.dashboard;
import static android.content.Intent.EXTRA_USER; 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_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; 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_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;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
@@ -40,22 +49,26 @@ import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.widget.Toast;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog; import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils; import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AdaptiveIcon; import com.android.settingslib.widget.AdaptiveIcon;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -106,20 +119,33 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
} }
@Override @Override
public void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon, public List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder) { boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
String key, int baseOrder) {
if (pref == null) { if (pref == null) {
return; return null;
} }
if (!TextUtils.isEmpty(key)) { if (!TextUtils.isEmpty(key)) {
pref.setKey(key); pref.setKey(key);
} else { } else {
pref.setKey(getDashboardKeyForTile(tile)); pref.setKey(getDashboardKeyForTile(tile));
} }
bindTitle(pref, tile); final List<DynamicDataObserver> outObservers = new ArrayList<>();
bindSummary(pref, tile); 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); bindIcon(pref, tile, forceRoundedIcon);
if (tile instanceof ActivityTile) {
final Bundle metadata = tile.getMetaData(); final Bundle metadata = tile.getMetaData();
String clsName = null; String clsName = null;
String action = null; String action = null;
@@ -141,6 +167,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
return true; return true;
}); });
} }
}
if (tile.hasOrder()) { if (tile.hasOrder()) {
final String skipOffsetPackageName = activity.getPackageName(); final String skipOffsetPackageName = activity.getPackageName();
@@ -153,6 +180,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
pref.setOrder(order + baseOrder); pref.setOrder(order + baseOrder);
} }
} }
return outObservers.isEmpty() ? null : outObservers;
} }
@Override @Override
@@ -170,11 +198,35 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY); 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()); final CharSequence title = tile.getTitle(mContext.getApplicationContext());
if (title != null) { if (title != null) {
preference.setTitle(title); preference.setTitle(title);
return; return null;
} }
if (tile.getMetaData() != null && tile.getMetaData().containsKey( if (tile.getMetaData() != null && tile.getMetaData().containsKey(
META_DATA_PREFERENCE_TITLE_URI)) { META_DATA_PREFERENCE_TITLE_URI)) {
@@ -182,9 +234,12 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
// to avoid preference height change. // to avoid preference height change.
preference.setTitle(R.string.summary_placeholder); 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); refreshTitle(uri, preference);
return createDynamicDataObserver(METHOD_GET_DYNAMIC_TITLE, uri, preference);
} }
return null;
} }
private void refreshTitle(Uri uri, Preference preference) { private void refreshTitle(Uri uri, Preference preference) {
@@ -196,7 +251,7 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
}); });
} }
private void bindSummary(Preference preference, Tile tile) { private DynamicDataObserver bindSummaryAndGetObserver(Preference preference, Tile tile) {
final CharSequence summary = tile.getSummary(mContext); final CharSequence summary = tile.getSummary(mContext);
if (summary != null) { if (summary != null) {
preference.setSummary(summary); preference.setSummary(summary);
@@ -206,11 +261,14 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
// to avoid preference height change. // to avoid preference height change.
preference.setSummary(R.string.summary_placeholder); 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); refreshSummary(uri, preference);
return createDynamicDataObserver(METHOD_GET_DYNAMIC_SUMMARY, uri, preference);
} else { } else {
preference.setSummary(R.string.summary_placeholder); preference.setSummary(R.string.summary_placeholder);
} }
return null;
} }
private void refreshSummary(Uri uri, Preference preference) { private void refreshSummary(Uri uri, Preference preference) {
@@ -222,6 +280,70 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
}); });
} }
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 @VisibleForTesting
void bindIcon(Preference preference, Tile tile, boolean forceRoundedIcon) { void bindIcon(Preference preference, Tile tile, boolean forceRoundedIcon) {
// Use preference context instead here when get icon from Tile, as we are using the context // Use preference context instead here when get icon from Tile, as we are using the context
@@ -246,7 +368,8 @@ public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
packageName = intent.getComponent().getPackageName(); packageName = intent.getComponent().getPackageName();
} }
final Map<String, IContentProvider> providerMap = new ArrayMap<>(); 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( final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri(
mContext, packageName, uri, providerMap); mContext, packageName, uri, providerMap);
if (iconInfo == null) { if (iconInfo == null) {

View File

@@ -17,11 +17,11 @@ package com.android.settings.dashboard;
import android.app.Activity; import android.app.Activity;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
@@ -30,6 +30,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
@@ -41,6 +42,7 @@ import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.Indexable; import com.android.settingslib.search.Indexable;
@@ -49,7 +51,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; 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. * Base fragment for dashboard style UI containing a list of static and dynamic setting items.
@@ -60,9 +62,11 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
BasePreferenceController.UiBlockListener { BasePreferenceController.UiBlockListener {
private static final String TAG = "DashboardFragment"; private static final String TAG = "DashboardFragment";
@VisibleForTesting
final ArrayMap<String, List<DynamicDataObserver>> mDashboardTilePrefKeys = new ArrayMap<>();
private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers = private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers =
new ArrayMap<>(); new ArrayMap<>();
private final Set<String> mDashboardTilePrefKeys = new ArraySet<>(); private final List<DynamicDataObserver> mRegisteredObservers = new ArrayList<>();
private final List<AbstractPreferenceController> mControllers = new ArrayList<>(); private final List<AbstractPreferenceController> mControllers = new ArrayList<>();
private DashboardFeatureProvider mDashboardFeatureProvider; private DashboardFeatureProvider mDashboardFeatureProvider;
@@ -171,6 +175,15 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
mListeningToCategoryChange = true; mListeningToCategoryChange = true;
((SettingsBaseActivity) activity).addCategoryListener(this); ((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 @Override
@@ -200,6 +213,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
unregisterDynamicDataObservers(new ArrayList<>(mRegisteredObservers));
if (mListeningToCategoryChange) { if (mListeningToCategoryChange) {
final Activity activity = getActivity(); final Activity activity = getActivity();
if (activity instanceof SettingsBaseActivity) { if (activity instanceof SettingsBaseActivity) {
@@ -325,7 +339,7 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
* Refresh all preference items, including both static prefs from xml, and dynamic items from * Refresh all preference items, including both static prefs from xml, and dynamic items from
* DashboardCategory. * DashboardCategory.
*/ */
private void refreshAllPreferences(final String TAG) { private void refreshAllPreferences(final String tag) {
final PreferenceScreen screen = getPreferenceScreen(); final PreferenceScreen screen = getPreferenceScreen();
// First remove old preferences. // First remove old preferences.
if (screen != null) { if (screen != null) {
@@ -336,11 +350,11 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
// Add resource based tiles. // Add resource based tiles.
displayResourceTiles(); displayResourceTiles();
refreshDashboardTiles(TAG); refreshDashboardTiles(tag);
final Activity activity = getActivity(); final Activity activity = getActivity();
if (activity != null) { if (activity != null) {
Log.d(TAG, "All preferences added, reporting fully drawn"); Log.d(tag, "All preferences added, reporting fully drawn");
activity.reportFullyDrawn(); activity.reportFullyDrawn();
} }
@@ -371,59 +385,62 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
/** /**
* Refresh preference items backed by DashboardCategory. * Refresh preference items backed by DashboardCategory.
*/ */
@VisibleForTesting private void refreshDashboardTiles(final String tag) {
void refreshDashboardTiles(final String TAG) {
final PreferenceScreen screen = getPreferenceScreen(); final PreferenceScreen screen = getPreferenceScreen();
final DashboardCategory category = final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey()); mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) { if (category == null) {
Log.d(TAG, "NO dashboard tiles for " + TAG); Log.d(tag, "NO dashboard tiles for " + tag);
return; return;
} }
final List<Tile> tiles = category.getTiles(); final List<Tile> tiles = category.getTiles();
if (tiles == null) { 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; return;
} }
// Create a list to track which tiles are to be removed. // 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. // Install dashboard tiles.
final boolean forceRoundedIcons = shouldForceRoundedIcon(); final boolean forceRoundedIcons = shouldForceRoundedIcon();
for (Tile tile : tiles) { for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile); final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) { 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; continue;
} }
if (!displayTile(tile)) { if (!displayTile(tile)) {
continue; continue;
} }
if (mDashboardTilePrefKeys.contains(key)) { if (mDashboardTilePrefKeys.containsKey(key)) {
// Have the key already, will rebind. // Have the key already, will rebind.
final Preference preference = screen.findPreference(key); final Preference preference = screen.findPreference(key);
mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons, mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
getMetricsCategory(), preference, tile, key, forceRoundedIcons, getMetricsCategory(), preference, tile, key,
mPlaceholderPreferenceController.getOrder()); mPlaceholderPreferenceController.getOrder());
} else { } else {
// Don't have this key, add it. // Don't have this key, add it.
final Preference pref = new Preference(getPrefContext()); final Preference pref = createPreference(tile);
mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons, final List<DynamicDataObserver> observers =
getMetricsCategory(), pref, tile, key, mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
forceRoundedIcons, getMetricsCategory(), pref, tile, key,
mPlaceholderPreferenceController.getOrder()); mPlaceholderPreferenceController.getOrder());
screen.addPreference(pref); screen.addPreference(pref);
mDashboardTilePrefKeys.add(key); registerDynamicDataObservers(observers);
mDashboardTilePrefKeys.put(key, observers);
} }
remove.remove(key); remove.remove(key);
} }
// Finally remove tiles that are gone. // 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); mDashboardTilePrefKeys.remove(key);
final Preference preference = screen.findPreference(key); final Preference preference = screen.findPreference(key);
if (preference != null) { if (preference != null) {
screen.removePreference(preference); screen.removePreference(preference);
} }
unregisterDynamicDataObservers(entry.getValue());
} }
} }
@@ -431,4 +448,41 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
public void onBlockerWorkFinished(BasePreferenceController controller) { public void onBlockerWorkFinished(BasePreferenceController controller) {
mBlockerController.countDown(controller.getPreferenceKey()); 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);
});
}
} }

View File

@@ -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();
}
}

View File

@@ -92,5 +92,6 @@
<!-- List containing the injected tile keys which are suppressed. --> <!-- List containing the injected tile keys which are suppressed. -->
<string-array name="config_suppress_injected_tile_keys" translatable="false"> <string-array name="config_suppress_injected_tile_keys" translatable="false">
<item>injected_tile_key</item> <item>injected_tile_key</item>
<item>injected_tile_key2</item>
</string-array> </string-array>
</resources> </resources>

View File

@@ -151,9 +151,9 @@ public class AccountDetailDashboardFragmentTest {
final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class); final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
final Preference preference = new Preference(mContext); final Preference preference = new Preference(mContext);
dashboardFeatureProvider.bindPreferenceToTile(activity, false /* forceRoundedIcon */, dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity,
MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, preference, tile, null /* key */, false /* forceRoundedIcon */, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY,
Preference.DEFAULT_ORDER); preference, tile, null /* key */, Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
preference.performClick(); preference.performClick();

View File

@@ -18,18 +18,21 @@ package com.android.settings.dashboard;
import static com.android.settingslib.drawer.CategoryKey.CATEGORY_HOMEPAGE; import static com.android.settingslib.drawer.CategoryKey.CATEGORY_HOMEPAGE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.os.Bundle; import android.os.Bundle;
import android.util.Pair; import android.util.Pair;
import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import org.junit.Before; import org.junit.Before;
@@ -132,25 +135,9 @@ public class CategoryManagerTest {
// Create some fake tiles that are not sorted. // Create some fake tiles that are not sorted.
final String testPackage = "com.android.test"; final String testPackage = "com.android.test";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
activityInfo1.metaData = new Bundle(); final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 50);
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100); final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 200);
activityInfo1.packageName = testPackage;
activityInfo1.name = "class1";
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 50);
activityInfo2.packageName = testPackage;
activityInfo2.name = "class2";
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 200);
activityInfo3.packageName = testPackage;
activityInfo3.name = "class3";
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -171,25 +158,9 @@ public class CategoryManagerTest {
final String testPackage1 = "com.android.test1"; final String testPackage1 = "com.android.test1";
final String testPackage2 = "com.android.test2"; final String testPackage2 = "com.android.test2";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage2, "class1", 100);
activityInfo1.metaData = new Bundle(); final Tile tile2 = createActivityTile(category.key, testPackage1, "class2", 100);
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100); final Tile tile3 = createActivityTile(category.key, testPackage1, "class3", 50);
activityInfo1.packageName = testPackage2;
activityInfo1.name = "class1";
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
activityInfo2.packageName = testPackage1;
activityInfo2.name = "class2";
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
activityInfo3.packageName = testPackage1;
activityInfo3.name = "class3";
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -209,24 +180,9 @@ public class CategoryManagerTest {
// Create some fake tiles that are not sorted. // Create some fake tiles that are not sorted.
final String testPackage = mContext.getPackageName(); final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
activityInfo1.packageName = testPackage; final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 100);
activityInfo1.name = "class1"; final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 50);
activityInfo1.metaData = new Bundle();
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.packageName = testPackage;
activityInfo2.name = "class2";
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.packageName = testPackage;
activityInfo3.name = "class3";
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -247,32 +203,10 @@ public class CategoryManagerTest {
final String testPackage = mContext.getPackageName(); final String testPackage = mContext.getPackageName();
final String testPackage2 = "com.google.test2"; final String testPackage2 = "com.google.test2";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 2);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 1);
activityInfo1.packageName = testPackage; final Tile tile3 = createActivityTile(category.key, testPackage2, "class0", 0);
activityInfo1.name = "class1"; final Tile tile4 = createActivityTile(category.key, testPackage, "class3", -1);
activityInfo1.metaData = new Bundle();
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 2);
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.packageName = testPackage;
activityInfo2.name = "class2";
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 1);
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.packageName = testPackage2;
activityInfo3.name = "class0";
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 0);
final ActivityInfo activityInfo4 = new ActivityInfo();
activityInfo4.packageName = testPackage;
activityInfo4.name = "class3";
activityInfo4.metaData = new Bundle();
activityInfo4.metaData.putInt(META_DATA_KEY_ORDER, -1);
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile4 = new ActivityTile(activityInfo4, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -296,24 +230,9 @@ public class CategoryManagerTest {
final String testPackage2 = "com.google.test2"; final String testPackage2 = "com.google.test2";
final String testPackage3 = "com.abcde.test3"; final String testPackage3 = "com.abcde.test3";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage2, "class1", 1);
activityInfo1.packageName = testPackage2; final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 1);
activityInfo1.name = "class1"; final Tile tile3 = createActivityTile(category.key, testPackage3, "class3", 1);
activityInfo1.metaData = new Bundle();
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 1);
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.packageName = testPackage;
activityInfo2.name = "class2";
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 1);
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.packageName = testPackage3;
activityInfo3.name = "class3";
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 1);
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -333,58 +252,36 @@ public class CategoryManagerTest {
// Create some unique tiles // Create some unique tiles
final String testPackage = mContext.getPackageName(); final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
activityInfo1.packageName = testPackage; final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 100);
activityInfo1.name = "class1"; final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 50);
activityInfo1.metaData = new Bundle(); final Tile tile4 = createProviderTile(category.key, testPackage, "class1", "authority1",
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100); "key1", 100);
final ActivityInfo activityInfo2 = new ActivityInfo(); final Tile tile5 = createProviderTile(category.key, testPackage, "class1", "authority2",
activityInfo2.packageName = testPackage; "key2", 100);
activityInfo2.name = "class2"; final Tile tile6 = createProviderTile(category.key, testPackage, "class1", "authority1",
activityInfo2.metaData = new Bundle(); "key2", 50);
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.packageName = testPackage;
activityInfo3.name = "class3";
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
category.addTile(tile4);
category.addTile(tile5);
category.addTile(tile6);
mCategoryByKeyMap.put(CATEGORY_HOMEPAGE, category); mCategoryByKeyMap.put(CATEGORY_HOMEPAGE, category);
mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap); mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
assertThat(category.getTilesCount()).isEqualTo(3); assertThat(category.getTilesCount()).isEqualTo(6);
} }
@Test @Test
public void filterTiles_hasDuplicate_shouldOnlyKeepUniqueTiles() { public void filterTiles_hasDuplicateActivityTiles_shouldOnlyKeepUniqueTiles() {
// Create tiles pointing to same intent. // Create tiles pointing to same intent.
final String testPackage = mContext.getPackageName(); final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE); final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final ActivityInfo activityInfo1 = new ActivityInfo(); final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
activityInfo1.packageName = testPackage; final Tile tile2 = createActivityTile(category.key, testPackage, "class1", 100);
activityInfo1.name = "class1"; final Tile tile3 = createActivityTile(category.key, testPackage, "class1", 50);
activityInfo1.metaData = new Bundle();
activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
final ActivityInfo activityInfo2 = new ActivityInfo();
activityInfo2.packageName = testPackage;
activityInfo2.name = "class1";
activityInfo2.metaData = new Bundle();
activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
final ActivityInfo activityInfo3 = new ActivityInfo();
activityInfo3.packageName = testPackage;
activityInfo3.name = "class1";
activityInfo3.metaData = new Bundle();
activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
final Tile tile1 = new ActivityTile(activityInfo1, category.key);
final Tile tile2 = new ActivityTile(activityInfo2, category.key);
final Tile tile3 = new ActivityTile(activityInfo3, category.key);
category.addTile(tile1); category.addTile(tile1);
category.addTile(tile2); category.addTile(tile2);
category.addTile(tile3); category.addTile(tile3);
@@ -394,4 +291,47 @@ public class CategoryManagerTest {
assertThat(category.getTilesCount()).isEqualTo(1); assertThat(category.getTilesCount()).isEqualTo(1);
} }
@Test
public void filterTiles_hasDuplicateProviderTiles_shouldOnlyKeepUniqueTiles() {
// Create tiles pointing to same authority and key.
final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
final Tile tile1 = createProviderTile(category.key, testPackage, "class1", "authority1",
"key1", 100);
final Tile tile2 = createProviderTile(category.key, testPackage, "class2", "authority1",
"key1", 100);
final Tile tile3 = createProviderTile(category.key, testPackage, "class3", "authority1",
"key1", 50);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
mCategoryByKeyMap.put(CATEGORY_HOMEPAGE, category);
mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
assertThat(category.getTilesCount()).isEqualTo(1);
}
private Tile createActivityTile(String categoryKey, String packageName, String className,
int order) {
final ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = className;
activityInfo.metaData = new Bundle();
activityInfo.metaData.putInt(META_DATA_KEY_ORDER, order);
return new ActivityTile(activityInfo, categoryKey);
}
private Tile createProviderTile(String categoryKey, String packageName, String className,
String authority, String key, int order) {
final ProviderInfo providerInfo = new ProviderInfo();
final Bundle metaData = new Bundle();
providerInfo.packageName = packageName;
providerInfo.name = className;
providerInfo.authority = authority;
metaData.putString(META_DATA_PREFERENCE_KEYHINT, key);
metaData.putInt(META_DATA_KEY_ORDER, order);
return new ProviderTile(providerInfo, categoryKey, metaData);
}
} }

View File

@@ -18,10 +18,12 @@ package com.android.settings.dashboard;
import static android.content.Intent.EXTRA_USER; import static android.content.Intent.EXTRA_USER;
import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
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;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
@@ -43,6 +45,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
@@ -52,6 +55,7 @@ import android.os.UserManager;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R; import com.android.settings.R;
@@ -62,6 +66,7 @@ import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils; import com.android.settingslib.drawer.TileUtils;
@@ -81,11 +86,15 @@ import org.robolectric.shadows.ShadowActivity;
import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowUserManager.class) @Config(shadows = ShadowUserManager.class)
public class DashboardFeatureProviderImplTest { public class DashboardFeatureProviderImplTest {
private static final String KEY = "key";
private static final String SWITCH_URI = "content://com.android.settings/tile_switch";
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private FragmentActivity mActivity; private FragmentActivity mActivity;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -96,6 +105,8 @@ public class DashboardFeatureProviderImplTest {
private Context mContext; private Context mContext;
private ActivityInfo mActivityInfo; private ActivityInfo mActivityInfo;
private ProviderInfo mProviderInfo;
private Bundle mSwitchMetaData;
private DashboardFeatureProviderImpl mImpl; private DashboardFeatureProviderImpl mImpl;
private boolean mForceRoundedIcon; private boolean mForceRoundedIcon;
@@ -112,6 +123,17 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_SUMMARY, mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_SUMMARY,
R.string.about_settings_summary); R.string.about_settings_summary);
mProviderInfo = new ProviderInfo();
mProviderInfo.packageName = mContext.getPackageName();
mProviderInfo.name = "provider";
mProviderInfo.authority = "com.android.settings";
mSwitchMetaData = new Bundle();
mSwitchMetaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
mSwitchMetaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
mSwitchMetaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
mSwitchMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, SWITCH_URI);
doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mPackageManager).when(mContext).getPackageManager();
when(mPackageManager.resolveActivity(any(Intent.class), anyInt())) when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
.thenReturn(new ResolveInfo()); .thenReturn(new ResolveInfo());
@@ -132,8 +154,8 @@ public class DashboardFeatureProviderImplTest {
doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
.when(tile).getIcon(any(Context.class)); .when(tile).getIcon(any(Context.class));
mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI");
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label)); assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
assertThat(preference.getSummary()) assertThat(preference.getSummary())
@@ -144,14 +166,35 @@ public class DashboardFeatureProviderImplTest {
assertThat(preference.getOrder()).isEqualTo(tile.getOrder()); assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
} }
@Test
public void bindPreference_shouldBindAllSwitchData() {
final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
final Tile tile = spy(new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
mSwitchMetaData));
mSwitchMetaData.putInt(META_DATA_KEY_ORDER, 10);
doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
.when(tile).getIcon(any(Context.class));
final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, preference, tile,
null /* key*/, Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
assertThat(preference.getSummary())
.isEqualTo(mContext.getText(R.string.about_settings_summary));
assertThat(preference.getKey()).isEqualTo(KEY);
assertThat(preference.getIcon()).isNotNull();
assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
assertThat(observers.get(0).getUri().toString()).isEqualTo(SWITCH_URI);
}
@Test @Test
public void bindPreference_noFragmentMetadata_shouldBindIntent() { public void bindPreference_noFragmentMetadata_shouldBindIntent() {
final Preference preference = new Preference(RuntimeEnvironment.application); final Preference preference = new Preference(RuntimeEnvironment.application);
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getFragment()).isNull(); assertThat(preference.getFragment()).isNull();
assertThat(preference.getOnPreferenceClickListener()).isNotNull(); assertThat(preference.getOnPreferenceClickListener()).isNotNull();
@@ -166,8 +209,8 @@ public class DashboardFeatureProviderImplTest {
tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class));
tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class));
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null); preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mActivity).getSupportFragmentManager(); verify(mActivity).getSupportFragmentManager();
@@ -183,8 +226,8 @@ public class DashboardFeatureProviderImplTest {
when(mActivity.getSystemService(Context.USER_SERVICE)) when(mActivity.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager); .thenReturn(mUserManager);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null); preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent( verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
@@ -203,8 +246,8 @@ public class DashboardFeatureProviderImplTest {
tile.userHandle = new ArrayList<>(); tile.userHandle = new ArrayList<>();
tile.userHandle.add(mock(UserHandle.class)); tile.userHandle.add(mock(UserHandle.class));
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null); preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent( verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
any(Context.class), any(Context.class),
@@ -217,8 +260,8 @@ public class DashboardFeatureProviderImplTest {
@Test @Test
public void bindPreference_nullPreference_shouldIgnore() { public void bindPreference_nullPreference_shouldIgnore() {
final Tile tile = mock(Tile.class); final Tile tile = mock(Tile.class);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
null, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, null, tile, "123", Preference.DEFAULT_ORDER);
verifyZeroInteractions(tile); verifyZeroInteractions(tile);
} }
@@ -227,8 +270,9 @@ public class DashboardFeatureProviderImplTest {
public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() { public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() {
final Preference preference = new Preference(RuntimeEnvironment.application); final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, null /*key */, Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isNotNull(); assertThat(preference.getKey()).isNotNull();
assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
@@ -241,8 +285,9 @@ public class DashboardFeatureProviderImplTest {
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, null /*key */, Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
Preference.DEFAULT_ORDER);
assertThat(preference.getSummary()) assertThat(preference.getSummary())
.isEqualTo(RuntimeEnvironment.application.getString(R.string.summary_placeholder)); .isEqualTo(RuntimeEnvironment.application.getString(R.string.summary_placeholder));
@@ -250,30 +295,99 @@ public class DashboardFeatureProviderImplTest {
@Test @Test
@Config(shadows = {ShadowTileUtils.class}) @Config(shadows = {ShadowTileUtils.class})
public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProvider() { public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver() {
final Preference preference = new Preference(RuntimeEnvironment.application); final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, final String uriString = "content://com.android.settings/tile_summary";
"content://com.android.settings/tile_summary"); mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
preference, tile, null /*key */, Preference.DEFAULT_ORDER); mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
} }
@Test @Test
@Config(shadows = {ShadowTileUtils.class}) @Config(shadows = {ShadowTileUtils.class})
public void bindPreference_hasTitleUri_shouldLoadFromContentProvider() { public void bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver() {
final Preference preference = new Preference(RuntimeEnvironment.application); final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, final String uriString = "content://com.android.settings/tile_title";
"content://com.android.settings/tile_title"); mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
preference, tile, null /*key */, Preference.DEFAULT_ORDER); mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY); assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
}
@Test
@Config(shadows = {ShadowTileUtils.class})
public void bindPreference_onCheckedChanged_shouldPutStateToContentProvider() {
final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
mSwitchMetaData);
final Bundle bundle = new Bundle();
bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false);
ShadowTileUtils.setResultBundle(bundle);
mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
Preference.DEFAULT_ORDER);
preference.callChangeListener(false);
assertThat(ShadowTileUtils.getProviderChecked()).isFalse();
preference.callChangeListener(true);
assertThat(ShadowTileUtils.getProviderChecked()).isTrue();
}
@Test
@Config(shadows = {ShadowTileUtils.class})
public void bindPreference_onCheckedChangedError_shouldRevertCheckedState() {
final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
mSwitchMetaData);
final Bundle bundle = new Bundle();
bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true);
ShadowTileUtils.setResultBundle(bundle);
mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
Preference.DEFAULT_ORDER);
preference.callChangeListener(true);
assertThat(preference.isChecked()).isFalse();
preference.callChangeListener(false);
assertThat(preference.isChecked()).isTrue();
}
@Test
@Config(shadows = {ShadowTileUtils.class})
public void bindPreference_callbackOnChanged_shouldLoadFromContentProvider() {
final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
mSwitchMetaData);
final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
null /*key */, Preference.DEFAULT_ORDER);
ShadowTileUtils.setProviderChecked(false);
observers.get(0).onDataChanged();
assertThat(preference.isChecked()).isFalse();
ShadowTileUtils.setProviderChecked(true);
observers.get(0).onDataChanged();
assertThat(preference.isChecked()).isTrue();
} }
@Test @Test
@@ -281,8 +395,9 @@ public class DashboardFeatureProviderImplTest {
final Preference preference = new Preference(RuntimeEnvironment.application); final Preference preference = new Preference(RuntimeEnvironment.application);
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, null /* key */, Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, preference, tile, null /* key */,
Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext)); assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
} }
@@ -308,8 +423,8 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", baseOrder); MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", baseOrder);
assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder); assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder);
} }
@@ -321,8 +436,8 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder); mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder);
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(testOrder); assertThat(preference.getOrder()).isEqualTo(testOrder);
} }
@@ -333,8 +448,8 @@ public class DashboardFeatureProviderImplTest {
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE); final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello"); mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello");
mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER); assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
} }
@@ -347,8 +462,8 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key"); mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null; tile.userHandle = null;
mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick(); preference.performClick();
ShadowActivity shadowActivity = Shadows.shadowOf(activity); ShadowActivity shadowActivity = Shadows.shadowOf(activity);
@@ -371,8 +486,8 @@ public class DashboardFeatureProviderImplTest {
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction"); mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null; tile.userHandle = null;
mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon,
preference, tile, "123", Preference.DEFAULT_ORDER); MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick(); preference.performClick();
final ShadowActivity.IntentForResult launchIntent = final ShadowActivity.IntentForResult launchIntent =

View File

@@ -17,9 +17,11 @@ package com.android.settings.dashboard;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.DASHBOARD_CONTAINER; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.DASHBOARD_CONTAINER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -29,14 +31,18 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ProviderInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
@@ -47,7 +53,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.ProviderTile;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -57,6 +63,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList; import java.util.ArrayList;
@@ -70,24 +78,37 @@ public class DashboardFragmentTest {
@Mock @Mock
private FakeFeatureFactory mFakeFeatureFactory; private FakeFeatureFactory mFakeFeatureFactory;
private ActivityInfo mActivityInfo;
private DashboardCategory mDashboardCategory; private DashboardCategory mDashboardCategory;
private Context mContext; private Context mContext;
private TestFragment mTestFragment; private TestFragment mTestFragment;
private List<AbstractPreferenceController> mControllers; private List<AbstractPreferenceController> mControllers;
private ActivityTile mActivityTile;
private ProviderTile mProviderTile;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
mActivityInfo = new ActivityInfo(); final ActivityInfo activityInfo = new ActivityInfo();
mActivityInfo.packageName = "pkg"; activityInfo.packageName = "pkg";
mActivityInfo.name = "class"; activityInfo.name = "class";
mActivityInfo.metaData = new Bundle(); activityInfo.metaData = new Bundle();
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key"); activityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key");
mFakeFeatureFactory = FakeFeatureFactory.setupForTest(); mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mDashboardCategory = new DashboardCategory("key"); mDashboardCategory = new DashboardCategory("key");
mDashboardCategory.addTile(new ActivityTile(mActivityInfo, mDashboardCategory.key)); mActivityTile = new ActivityTile(activityInfo, mDashboardCategory.key);
mDashboardCategory.addTile(mActivityTile);
final ProviderInfo providerInfo = new ProviderInfo();
providerInfo.packageName = "pkg";
providerInfo.name = "provider";
providerInfo.authority = "authority";
final Bundle metaData = new Bundle();
metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2");
metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "uri");
mProviderTile = new ProviderTile(providerInfo, mDashboardCategory.key, metaData);
mDashboardCategory.addTile(mProviderTile);
mTestFragment = new TestFragment(RuntimeEnvironment.application); mTestFragment = new TestFragment(RuntimeEnvironment.application);
when(mFakeFeatureFactory.dashboardFeatureProvider when(mFakeFeatureFactory.dashboardFeatureProvider
.getTilesForCategory(nullable(String.class))) .getTilesForCategory(nullable(String.class)))
@@ -127,11 +148,14 @@ public class DashboardFragmentTest {
.getTilesForCategory(nullable(String.class))) .getTilesForCategory(nullable(String.class)))
.thenReturn(mDashboardCategory); .thenReturn(mDashboardCategory);
when(mFakeFeatureFactory.dashboardFeatureProvider when(mFakeFeatureFactory.dashboardFeatureProvider
.getDashboardKeyForTile(nullable(Tile.class))) .getDashboardKeyForTile(any(ActivityTile.class)))
.thenReturn("test_key"); .thenReturn("test_key");
when(mFakeFeatureFactory.dashboardFeatureProvider
.getDashboardKeyForTile(any(ProviderTile.class)))
.thenReturn("test_key2");
mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
verify(mTestFragment.mScreen).addPreference(nullable(Preference.class)); verify(mTestFragment.mScreen, times(2)).addPreference(nullable(Preference.class));
} }
@Test @Test
@@ -156,8 +180,11 @@ public class DashboardFragmentTest {
.getTilesForCategory(nullable(String.class))) .getTilesForCategory(nullable(String.class)))
.thenReturn(mDashboardCategory); .thenReturn(mDashboardCategory);
when(mFakeFeatureFactory.dashboardFeatureProvider when(mFakeFeatureFactory.dashboardFeatureProvider
.getDashboardKeyForTile(nullable(Tile.class))) .getDashboardKeyForTile(any(ActivityTile.class)))
.thenReturn("test_key"); .thenReturn("test_key");
when(mFakeFeatureFactory.dashboardFeatureProvider
.getDashboardKeyForTile(any(ProviderTile.class)))
.thenReturn("test_key2");
mTestFragment.onCreatePreferences(new Bundle(), "rootKey"); mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class)); verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class));
@@ -171,6 +198,29 @@ public class DashboardFragmentTest {
assertThat(controller).isNotNull(); assertThat(controller).isNotNull();
} }
@Test
@Config(shadows = ShadowPreferenceFragmentCompat.class)
public void onStart_shouldRegisterDynamicDataObservers() {
final DynamicDataObserver observer = new TestDynamicDataObserver();
mTestFragment.mDashboardTilePrefKeys.put("key", Arrays.asList(observer));
mTestFragment.onStart();
verify(mTestFragment.getContentResolver()).registerContentObserver(observer.getUri(), false,
observer);
}
@Test
@Config(shadows = ShadowPreferenceFragmentCompat.class)
public void onStop_shouldUnregisterDynamicDataObservers() {
final DynamicDataObserver observer = new TestDynamicDataObserver();
mTestFragment.registerDynamicDataObservers(Arrays.asList(observer));
mTestFragment.onStop();
verify(mTestFragment.getContentResolver()).unregisterContentObserver(observer);
}
@Test @Test
public void updateState_skipUnavailablePrefs() { public void updateState_skipUnavailablePrefs() {
final List<AbstractPreferenceController> preferenceControllers = mTestFragment.mControllers; final List<AbstractPreferenceController> preferenceControllers = mTestFragment.mControllers;
@@ -266,7 +316,14 @@ public class DashboardFragmentTest {
assertThat(mTestFragment.mBlockerController).isNull(); assertThat(mTestFragment.mBlockerController).isNull();
} }
public static class TestPreferenceController extends AbstractPreferenceController @Test
public void createPreference_isProviderTile_returnSwitchPreference() {
final Preference pref = mTestFragment.createPreference(mProviderTile);
assertThat(pref).isInstanceOf(SwitchPreference.class);
}
private static class TestPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin { implements PreferenceControllerMixin {
private TestPreferenceController(Context context) { private TestPreferenceController(Context context) {
@@ -293,18 +350,20 @@ public class DashboardFragmentTest {
} }
} }
public static class TestFragment extends DashboardFragment { private static class TestFragment extends DashboardFragment {
public final PreferenceScreen mScreen;
private final PreferenceManager mPreferenceManager; private final PreferenceManager mPreferenceManager;
private final Context mContext; private final Context mContext;
private final List<AbstractPreferenceController> mControllers; private final List<AbstractPreferenceController> mControllers;
private final ContentResolver mContentResolver;
public final PreferenceScreen mScreen;
public TestFragment(Context context) { public TestFragment(Context context) {
mContext = context; mContext = context;
mPreferenceManager = mock(PreferenceManager.class); mPreferenceManager = mock(PreferenceManager.class);
mScreen = mock(PreferenceScreen.class); mScreen = mock(PreferenceScreen.class);
mContentResolver = mock(ContentResolver.class);
mControllers = new ArrayList<>(); mControllers = new ArrayList<>();
when(mPreferenceManager.getContext()).thenReturn(mContext); when(mPreferenceManager.getContext()).thenReturn(mContext);
@@ -346,5 +405,36 @@ public class DashboardFragmentTest {
public PreferenceManager getPreferenceManager() { public PreferenceManager getPreferenceManager() {
return mPreferenceManager; return mPreferenceManager;
} }
@Override
protected ContentResolver getContentResolver() {
return mContentResolver;
}
}
private static class TestDynamicDataObserver extends DynamicDataObserver {
@Override
public Uri getUri() {
return Uri.parse("content://abc");
}
@Override
public void onDataChanged() {
}
}
@Implements(PreferenceFragmentCompat.class)
public static class ShadowPreferenceFragmentCompat {
@Implementation
public void onStart() {
// do nothing
}
@Implementation
public void onStop() {
// do nothing
}
} }
} }

View File

@@ -19,6 +19,7 @@ package com.android.settings.testutils.shadow;
import android.content.Context; import android.content.Context;
import android.content.IContentProvider; import android.content.IContentProvider;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.util.Pair; import android.util.Pair;
import com.android.settings.R; import com.android.settings.R;
@@ -35,6 +36,9 @@ public class ShadowTileUtils {
public static final String MOCK_SUMMARY = "summary"; public static final String MOCK_SUMMARY = "summary";
private static boolean sChecked;
private static Bundle sResult;
@Implementation @Implementation
protected static String getTextFromUri(Context context, Uri uri, protected static String getTextFromUri(Context context, Uri uri,
Map<String, IContentProvider> providerMap, String key) { Map<String, IContentProvider> providerMap, String key) {
@@ -46,4 +50,29 @@ public class ShadowTileUtils {
Uri uri, Map<String, IContentProvider> providerMap) { Uri uri, Map<String, IContentProvider> providerMap) {
return Pair.create(RuntimeEnvironment.application.getPackageName(), R.drawable.ic_settings_accent); return Pair.create(RuntimeEnvironment.application.getPackageName(), R.drawable.ic_settings_accent);
} }
@Implementation
public static boolean getBooleanFromUri(Context context, Uri uri,
Map<String, IContentProvider> providerMap, String key) {
return sChecked;
}
@Implementation
public static Bundle putBooleanToUri(Context context, Uri uri,
Map<String, IContentProvider> providerMap, String key, boolean value) {
sChecked = value;
return sResult;
}
public static boolean getProviderChecked() {
return sChecked;
}
public static void setProviderChecked(boolean value) {
sChecked = value;
}
public static void setResultBundle(Bundle result) {
sResult = result;
}
} }