diff --git a/src/com/android/settings/security/SecurityFeatureProviderImpl.java b/src/com/android/settings/security/SecurityFeatureProviderImpl.java index 7be2d662061..5a2ff2e2581 100644 --- a/src/com/android/settings/security/SecurityFeatureProviderImpl.java +++ b/src/com/android/settings/security/SecurityFeatureProviderImpl.java @@ -39,6 +39,7 @@ import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; import java.util.concurrent.Executors; +import java.util.TreeMap; import java.util.Map; /** Implementation for {@code SecurityFeatureProvider}. */ @@ -51,6 +52,12 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { @VisibleForTesting static final String DEFAULT_SUMMARY = " "; + @VisibleForTesting + static Map> sIconCache = new TreeMap<>(); + + @VisibleForTesting + static Map sSummaryCache = new TreeMap<>(); + /** Update preferences with data from associated tiles. */ public void updatePreferences(final Context context, final PreferenceScreen preferenceScreen, final DashboardCategory dashboardCategory) { @@ -89,11 +96,33 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { if (matchingPref == null) { continue; } - // Remove any icons that may be loaded before we inject the final icon. - matchingPref.setIcon(DEFAULT_ICON); - // Reserve room for the summary. This prevents the title from having to shift when the - // final title is injected. - matchingPref.setSummary(DEFAULT_SUMMARY); + // Either remove an icon by replacing them with nothing, or use the cached one since + // there is a delay in fetching the injected icon, and we don't want an inappropriate + // icon to be displayed while waiting for the injected icon. + final String iconUri = + tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null); + Drawable drawable = DEFAULT_ICON; + if ((iconUri != null) && sIconCache.containsKey(iconUri)) { + Pair 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; } // Check if the tile has content providers for dynamically updatable content. - String iconUri = tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null); - String summaryUri = + final String iconUri = + tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_URI, null); + final String summaryUri = tile.metaData.getString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, null); if (!TextUtils.isEmpty(iconUri)) { String packageName = null; @@ -131,6 +161,7 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { Pair icon = TileUtils.getIconFromUri(context, packageName, iconUri, providerMap); if (icon != null) { + sIconCache.put(iconUri, icon); // Icon is only returned if the icon belongs to Settings or the target app. // setIcon must be called on the UI thread. new Handler(Looper.getMainLooper()).post(new Runnable() { @@ -153,6 +184,7 @@ public class SecurityFeatureProviderImpl implements SecurityFeatureProvider { if (!TextUtils.isEmpty(summaryUri)) { String summary = TileUtils.getTextFromUri(context, summaryUri, providerMap, TileUtils.META_DATA_PREFERENCE_SUMMARY); + sSummaryCache.put(summaryUri, summary); // setSummary must be called on UI thread. new Handler(Looper.getMainLooper()).post(new Runnable() { @Override diff --git a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java index 50debb46e9b..77f5ecc2ea2 100644 --- a/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/security/SecurityFeatureProviderImplTest.java @@ -77,7 +77,7 @@ public class SecurityFeatureProviderImplTest { private SecurityFeatureProviderImpl mImpl; @Implements(com.android.settingslib.drawer.TileUtils.class) - public static class TileUtilsMock { + public static class ShadowTileUtils { @Implementation public static Pair getIconFromUri(Context context, String packageName, String uriString, Map providerMap) { @@ -131,7 +131,7 @@ public class SecurityFeatureProviderImplTest { @Test @Config(shadows = { - TileUtilsMock.class, + ShadowTileUtils.class, }) public void updateTilesData_shouldUpdateMatchingPreference() { Bundle bundle = new Bundle(); @@ -151,7 +151,7 @@ public class SecurityFeatureProviderImplTest { @Test @Config(shadows = { - TileUtilsMock.class, + ShadowTileUtils.class, }) public void updateTilesData_shouldNotUpdateAlreadyUpdatedPreference() { Bundle bundle = new Bundle(); @@ -183,6 +183,31 @@ public class SecurityFeatureProviderImplTest { .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() { final PreferenceScreen screen = mock(PreferenceScreen.class); final Preference pref = mock(Preference.class);