Cache the summary and icons for Security Settings injected tiles.

This helps the summary and icons load more smoothly when resuming
from recents.

Bug: 35994047
Test: make RunSettingsRoboTests
Change-Id: I763f7dd19f64007959e1281f0ff04ecd19bbfbfc
This commit is contained in:
William Luh
2017-03-16 10:05:04 -07:00
parent 0ef2a55b08
commit 45f4e1241d
2 changed files with 67 additions and 10 deletions

View File

@@ -39,6 +39,7 @@ import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils; import com.android.settingslib.drawer.TileUtils;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.TreeMap;
import java.util.Map; import java.util.Map;
/** Implementation for {@code SecurityFeatureProvider}. */ /** Implementation for {@code SecurityFeatureProvider}. */
@@ -51,6 +52,12 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
@VisibleForTesting @VisibleForTesting
static final String DEFAULT_SUMMARY = " "; static final String DEFAULT_SUMMARY = " ";
@VisibleForTesting
static Map<String, Pair<String, Integer>> sIconCache = new TreeMap<>();
@VisibleForTesting
static Map<String, String> sSummaryCache = new TreeMap<>();
/** Update preferences with data from associated tiles. */ /** Update preferences with data from associated tiles. */
public void updatePreferences(final Context context, final PreferenceScreen preferenceScreen, public void updatePreferences(final Context context, final PreferenceScreen preferenceScreen,
final DashboardCategory dashboardCategory) { final DashboardCategory dashboardCategory) {
@@ -89,11 +96,33 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
if (matchingPref == null) { if (matchingPref == null) {
continue; continue;
} }
// Remove any icons that may be loaded before we inject the final icon. // Either remove an icon by replacing them with nothing, or use the cached one since
matchingPref.setIcon(DEFAULT_ICON); // there is a delay in fetching the injected icon, and we don't want an inappropriate
// Reserve room for the summary. This prevents the title from having to shift when the // icon to be displayed while waiting for the injected icon.
// final title is injected. final String iconUri =
matchingPref.setSummary(DEFAULT_SUMMARY); tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null);
Drawable drawable = DEFAULT_ICON;
if ((iconUri != null) && sIconCache.containsKey(iconUri)) {
Pair<String, Integer> icon = sIconCache.get(iconUri);
try {
drawable = context.getPackageManager()
.getResourcesForApplication(icon.first /* package name */)
.getDrawable(icon.second /* res id */,
context.getTheme());
} catch (PackageManager.NameNotFoundException e) {
// Ignore and just load the default icon.
}
}
matchingPref.setIcon(drawable);
// Either reserve room for the summary or load the cached one. This prevents the title
// from shifting when the final summary is injected.
final String summaryUri =
tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null);
String summary = DEFAULT_SUMMARY;
if ((summaryUri != null) && sSummaryCache.containsKey(summaryUri)) {
summary = sSummaryCache.get(summaryUri);
}
matchingPref.setSummary(summary);
} }
} }
@@ -115,8 +144,9 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
continue; continue;
} }
// Check if the tile has content providers for dynamically updatable content. // Check if the tile has content providers for dynamically updatable content.
String iconUri = tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null); final String iconUri =
String summaryUri = tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null);
final String summaryUri =
tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null); tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null);
if (!TextUtils.isEmpty(iconUri)) { if (!TextUtils.isEmpty(iconUri)) {
String packageName = null; String packageName = null;
@@ -131,6 +161,7 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
Pair<String, Integer> icon = Pair<String, Integer> icon =
TileUtils.getIconFromUri(context, packageName, iconUri, providerMap); TileUtils.getIconFromUri(context, packageName, iconUri, providerMap);
if (icon != null) { if (icon != null) {
sIconCache.put(iconUri, icon);
// Icon is only returned if the icon belongs to Settings or the target app. // Icon is only returned if the icon belongs to Settings or the target app.
// setIcon must be called on the UI thread. // setIcon must be called on the UI thread.
new Handler(Looper.getMainLooper()).post(new Runnable() { new Handler(Looper.getMainLooper()).post(new Runnable() {
@@ -153,6 +184,7 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider {
if (!TextUtils.isEmpty(summaryUri)) { if (!TextUtils.isEmpty(summaryUri)) {
String summary = TileUtils.getTextFromUri(context, summaryUri, providerMap, String summary = TileUtils.getTextFromUri(context, summaryUri, providerMap,
TileUtils.META_DATA_PREFERENCE_SUMMARY); TileUtils.META_DATA_PREFERENCE_SUMMARY);
sSummaryCache.put(summaryUri, summary);
// setSummary must be called on UI thread. // setSummary must be called on UI thread.
new Handler(Looper.getMainLooper()).post(new Runnable() { new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override @Override

View File

@@ -77,7 +77,7 @@ public class SecurityFeatureProviderImplTest {
private SecurityFeatureProviderImpl mImpl; private SecurityFeatureProviderImpl mImpl;
@Implements(com.android.settingslib.drawer.TileUtils.class) @Implements(com.android.settingslib.drawer.TileUtils.class)
public static class TileUtilsMock { public static class ShadowTileUtils {
@Implementation @Implementation
public static Pair getIconFromUri(Context context, String packageName, String uriString, public static Pair getIconFromUri(Context context, String packageName, String uriString,
Map<String, IContentProvider> providerMap) { Map<String, IContentProvider> providerMap) {
@@ -131,7 +131,7 @@ public class SecurityFeatureProviderImplTest {
@Test @Test
@Config(shadows = { @Config(shadows = {
TileUtilsMock.class, ShadowTileUtils.class,
}) })
public void updateTilesData_shouldUpdateMatchingPreference() { public void updateTilesData_shouldUpdateMatchingPreference() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@@ -151,7 +151,7 @@ public class SecurityFeatureProviderImplTest {
@Test @Test
@Config(shadows = { @Config(shadows = {
TileUtilsMock.class, ShadowTileUtils.class,
}) })
public void updateTilesData_shouldNotUpdateAlreadyUpdatedPreference() { public void updateTilesData_shouldNotUpdateAlreadyUpdatedPreference() {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@@ -183,6 +183,31 @@ public class SecurityFeatureProviderImplTest {
.setSummary(SecurityFeatureProviderImpl.DEFAULT_SUMMARY); .setSummary(SecurityFeatureProviderImpl.DEFAULT_SUMMARY);
} }
@Test
@Config(shadows = {
ShadowTileUtils.class,
})
public void initPreferences_shouldLoadCached() {
Bundle bundle = new Bundle();
bundle.putString(TileUtils.META_DATA_PREFERENCE_ICON_URI, URI_GET_ICON);
bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, URI_GET_SUMMARY);
PreferenceScreen screen = getPreferenceScreen();
DashboardCategory dashboardCategory = getDashboardCategory();
dashboardCategory.getTile(0).metaData = bundle;
SecurityFeatureProviderImpl.sIconCache.put(
URI_GET_ICON,
ShadowTileUtils.getIconFromUri(null, null, null, null));
SecurityFeatureProviderImpl.sSummaryCache.put(
URI_GET_SUMMARY,
MOCK_SUMMARY);
mImpl.initPreferences(mContext, screen, dashboardCategory);
verify(screen.findPreference(MOCK_KEY)).setIcon(mMockDrawable);
verify(screen.findPreference(MOCK_KEY)).setSummary(MOCK_SUMMARY);
}
private PreferenceScreen getPreferenceScreen() { private PreferenceScreen getPreferenceScreen() {
final PreferenceScreen screen = mock(PreferenceScreen.class); final PreferenceScreen screen = mock(PreferenceScreen.class);
final Preference pref = mock(Preference.class); final Preference pref = mock(Preference.class);