Loading packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * 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.settingslib.drawer; import android.content.Context; import android.content.Intent; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Parcel; import android.util.Log; import java.util.List; import java.util.Objects; /** * Description of a single dashboard tile which is generated from an activity. */ public class ActivityTile extends Tile { private static final String TAG = "ActivityTile"; public ActivityTile(ComponentInfo info, String category) { super(info, category); setMetaData(info.metaData); } ActivityTile(Parcel in) { super(in); } @Override public int getId() { return Objects.hash(getPackageName(), getComponentName()); } @Override public String getDescription() { return getPackageName() + "/" + getComponentName(); } @Override protected CharSequence getComponentLabel(Context context) { final PackageManager pm = context.getPackageManager(); final ComponentInfo info = getComponentInfo(context); return info == null ? null : info.loadLabel(pm); } @Override protected ComponentInfo getComponentInfo(Context context) { if (mComponentInfo == null) { final PackageManager pm = context.getApplicationContext().getPackageManager(); final Intent intent = getIntent(); final List<ResolveInfo> infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); if (infoList != null && !infoList.isEmpty()) { mComponentInfo = infoList.get(0).activityInfo; setMetaData(mComponentInfo.metaData); } else { Log.e(TAG, "Cannot find package info for " + intent.getComponent().flattenToString()); } } return mComponentInfo; } } packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +47 −63 Original line number Diff line number Diff line Loading @@ -30,9 +30,8 @@ import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Icon; Loading @@ -47,13 +46,11 @@ import androidx.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; /** * Description of a single dashboard tile that the user can select. */ public class Tile implements Parcelable { public abstract class Tile implements Parcelable { private static final String TAG = "Tile"; Loading @@ -64,28 +61,27 @@ public class Tile implements Parcelable { @VisibleForTesting long mLastUpdateTime; private final String mActivityPackage; private final String mActivityName; private final String mComponentPackage; private final String mComponentName; private final Intent mIntent; private ActivityInfo mActivityInfo; protected ComponentInfo mComponentInfo; private CharSequence mSummaryOverride; private Bundle mMetaData; private String mCategory; public Tile(ActivityInfo activityInfo, String category) { mActivityInfo = activityInfo; mActivityPackage = mActivityInfo.packageName; mActivityName = mActivityInfo.name; mMetaData = activityInfo.metaData; public Tile(ComponentInfo info, String category) { mComponentInfo = info; mComponentPackage = mComponentInfo.packageName; mComponentName = mComponentInfo.name; mCategory = category; mIntent = new Intent().setClassName(mActivityPackage, mActivityName); mIntent = new Intent().setClassName(mComponentPackage, mComponentName); } Tile(Parcel in) { mActivityPackage = in.readString(); mActivityName = in.readString(); mIntent = new Intent().setClassName(mActivityPackage, mActivityName); mComponentPackage = in.readString(); mComponentName = in.readString(); mIntent = new Intent().setClassName(mComponentPackage, mComponentName); final int number = in.readInt(); for (int i = 0; i < number; i++) { userHandle.add(UserHandle.CREATOR.createFromParcel(in)); Loading @@ -101,8 +97,8 @@ public class Tile implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mActivityPackage); dest.writeString(mActivityName); dest.writeString(mComponentPackage); dest.writeString(mComponentName); final int size = userHandle.size(); dest.writeInt(size); for (int i = 0; i < size; i++) { Loading @@ -112,16 +108,22 @@ public class Tile implements Parcelable { dest.writeBundle(mMetaData); } public int getId() { return Objects.hash(mActivityPackage, mActivityName); } /** * Unique ID of the tile */ public abstract int getId(); public String getDescription() { return mActivityPackage + "/" + mActivityName; } /** * Human-readable description of the tile */ public abstract String getDescription(); public String getPackageName() { return mActivityPackage; return mComponentPackage; } public String getComponentName() { return mComponentName; } /** Loading Loading @@ -154,7 +156,7 @@ public class Tile implements Parcelable { } /** * Check whether title has order. * Check whether tile has order. */ public boolean hasOrder() { return mMetaData.containsKey(META_DATA_KEY_ORDER) Loading @@ -170,14 +172,14 @@ public class Tile implements Parcelable { final PackageManager packageManager = context.getPackageManager(); if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) { if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) { // If has as uri to provide dynamic summary, skip loading here. UI will later load // If has as uri to provide dynamic title, skip loading here. UI will later load // at tile binding time. return null; } if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) { try { final Resources res = packageManager.getResourcesForApplication(mActivityPackage); packageManager.getResourcesForApplication(mComponentPackage); title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { Log.w(TAG, "Couldn't find info", e); Loading @@ -186,18 +188,15 @@ public class Tile implements Parcelable { title = mMetaData.getString(META_DATA_PREFERENCE_TITLE); } } // Set the preference title to the activity's label if no // meta-data is found // Set the preference title by the component if no meta-data is found if (title == null) { final ActivityInfo activityInfo = getActivityInfo(context); if (activityInfo == null) { return null; } title = activityInfo.loadLabel(packageManager); title = getComponentLabel(context); } return title; } protected abstract CharSequence getComponentLabel(Context context); /** * Overrides the summary. This can happen when injected tile wants to provide dynamic summary. */ Loading Loading @@ -225,7 +224,7 @@ public class Tile implements Parcelable { if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) { try { final Resources res = packageManager.getResourcesForApplication(mActivityPackage); packageManager.getResourcesForApplication(mComponentPackage); summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { Log.d(TAG, "Couldn't find info", e); Loading Loading @@ -281,24 +280,24 @@ public class Tile implements Parcelable { return null; } ensureMetadataNotStale(context); final ActivityInfo activityInfo = getActivityInfo(context); if (activityInfo == null) { Log.w(TAG, "Cannot find ActivityInfo for " + getDescription()); final ComponentInfo componentInfo = getComponentInfo(context); if (componentInfo == null) { Log.w(TAG, "Cannot find ComponentInfo for " + getDescription()); return null; } int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON); // Set the icon if (iconResId == 0) { // Only fallback to activityinfo.icon if metadata does not contain ICON_URI. // Only fallback to componentInfo.icon if metadata does not contain ICON_URI. // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this // level is too complex because we don't have a strong threading contract for this class if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) { iconResId = activityInfo.icon; iconResId = componentInfo.icon; } } if (iconResId != 0) { final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId); final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId); if (isIconTintable(context)) { final TypedArray a = context.obtainStyledAttributes(new int[]{ android.R.attr.colorControlNormal}); Loading Loading @@ -331,41 +330,26 @@ public class Tile implements Parcelable { final PackageManager pm = context.getApplicationContext().getPackageManager(); try { final long lastUpdateTime = pm.getPackageInfo(mActivityPackage, final long lastUpdateTime = pm.getPackageInfo(mComponentPackage, PackageManager.GET_META_DATA).lastUpdateTime; if (lastUpdateTime == mLastUpdateTime) { // All good. Do nothing return; } // App has been updated since we load metadata last time. Reload metadata. mActivityInfo = null; getActivityInfo(context); mComponentInfo = null; getComponentInfo(context); mLastUpdateTime = lastUpdateTime; } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "Can't find package, probably uninstalled."); } } private ActivityInfo getActivityInfo(Context context) { if (mActivityInfo == null) { final PackageManager pm = context.getApplicationContext().getPackageManager(); final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName); final List<ResolveInfo> infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); if (infoList != null && !infoList.isEmpty()) { mActivityInfo = infoList.get(0).activityInfo; mMetaData = mActivityInfo.metaData; } else { Log.e(TAG, "Cannot find package info for " + intent.getComponent().flattenToString()); } } return mActivityInfo; } protected abstract ComponentInfo getComponentInfo(Context context); public static final Creator<Tile> CREATOR = new Creator<Tile>() { public Tile createFromParcel(Parcel source) { return new Tile(source); return new ActivityTile(source); } public Tile[] newArray(int size) { Loading packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +81 −52 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; Loading Loading @@ -127,6 +128,7 @@ public class TileUtils { */ public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT = "com.android.settings.bg.hint"; /** * Name of the meta-data item that should be set in the AndroidManifest.xml * to specify the icon background color as raw ARGB. Loading Loading @@ -220,10 +222,11 @@ public class TileUtils { public static List<DashboardCategory> getCategories(Context context, Map<Pair<String, String>, Tile> cache) { final long startTime = System.currentTimeMillis(); boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; ArrayList<Tile> tiles = new ArrayList<>(); UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); final boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; final ArrayList<Tile> tiles = new ArrayList<>(); final UserManager userManager = (UserManager) context.getSystemService( Context.USER_SERVICE); for (UserHandle user : userManager.getUserProfiles()) { // TODO: Needs much optimization, too many PM queries going on here. if (user.getIdentifier() == ActivityManager.getCurrentUser()) { Loading @@ -240,7 +243,7 @@ public class TileUtils { } } HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); final HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); for (Tile tile : tiles) { final String categoryKey = tile.getCategory(); DashboardCategory category = categoryMap.get(categoryKey); Loading @@ -255,7 +258,7 @@ public class TileUtils { } category.addTile(tile); } ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); final ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); for (DashboardCategory category : categories) { category.sortTiles(); } Loading @@ -275,33 +278,45 @@ public class TileUtils { if (requireSettings) { intent.setPackage(SETTING_PKG); } getActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent); } private static void getActivityTiles(Context context, UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent) { final PackageManager pm = context.getPackageManager(); List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA, user.getIdentifier()); for (ResolveInfo resolved : results) { if (!resolved.system) { // Do not allow any app to add to settings, only system ones. continue; } ActivityInfo activityInfo = resolved.activityInfo; Bundle metaData = activityInfo.metaData; String categoryKey = defaultCategory; final ActivityInfo activityInfo = resolved.activityInfo; final Bundle metaData = activityInfo.metaData; getTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo); } } private static void getTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData, ComponentInfo componentInfo) { String categoryKey = defaultCategory; // Load category if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY)) && categoryKey == null) { Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent " Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent " + intent + " missing metadata " + (metaData == null ? "" : EXTRA_CATEGORY_KEY)); continue; return; } else { categoryKey = metaData.getString(EXTRA_CATEGORY_KEY); } Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name); final Pair<String, String> key = new Pair<>(componentInfo.packageName, componentInfo.name); Tile tile = addedCache.get(key); if (tile == null) { tile = new Tile(activityInfo, categoryKey); tile = new ActivityTile(componentInfo, categoryKey); addedCache.put(key, tile); } else { tile.setMetaData(metaData); Loading @@ -314,6 +329,21 @@ public class TileUtils { outTiles.add(tile); } } /** * Returns the complete uri from the meta data key of the tile. * @param tile Tile which contains meta data * @param metaDataKey Key mapping to the uri in meta data * @return Uri associated with the key */ public static Uri getCompleteUri(Tile tile, String metaDataKey) { final String uriString = tile.getMetaData().getString(metaDataKey); if (TextUtils.isEmpty(uriString)) { return null; } return Uri.parse(uriString); } /** Loading @@ -321,17 +351,17 @@ public class TileUtils { * * @param context context * @param packageName package name of the target activity * @param uriString URI for the content provider * @param uri URI for the content provider * @param providerMap Maps URI authorities to providers * @return package name and resource id of the icon specified */ public static Pair<String, Integer> getIconFromUri(Context context, String packageName, String uriString, Map<String, IContentProvider> providerMap) { Bundle bundle = getBundleFromUri(context, uriString, providerMap); Uri uri, Map<String, IContentProvider> providerMap) { final Bundle bundle = getBundleFromUri(context, uri, providerMap, null); if (bundle == null) { return null; } String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE); final String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE); if (TextUtils.isEmpty(iconPackageName)) { return null; } Loading @@ -342,7 +372,7 @@ public class TileUtils { // Icon can either come from the target package or from the Settings app. if (iconPackageName.equals(packageName) || iconPackageName.equals(context.getPackageName())) { return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0)); return Pair.create(iconPackageName, resId); } return null; } Loading @@ -351,34 +381,33 @@ public class TileUtils { * Gets text associated with the input key from the content provider. * * @param context context * @param uriString URI for the content provider * @param uri URI for the content provider * @param providerMap Maps URI authorities to providers * @param key Key mapping to the text in bundle returned by the content provider * @return Text associated with the key, if returned by the content provider */ public static String getTextFromUri(Context context, String uriString, public static String getTextFromUri(Context context, Uri uri, Map<String, IContentProvider> providerMap, String key) { Bundle bundle = getBundleFromUri(context, uriString, providerMap); final Bundle bundle = getBundleFromUri(context, uri, providerMap, null); return (bundle != null) ? bundle.getString(key) : null; } private static Bundle getBundleFromUri(Context context, String uriString, Map<String, IContentProvider> providerMap) { if (TextUtils.isEmpty(uriString)) { private static Bundle getBundleFromUri(Context context, Uri uri, Map<String, IContentProvider> providerMap, Bundle bundle) { if (uri == null) { return null; } Uri uri = Uri.parse(uriString); String method = getMethodFromUri(uri); final String method = getMethodFromUri(uri); if (TextUtils.isEmpty(method)) { return null; } IContentProvider provider = getProviderFromUri(context, uri, providerMap); final IContentProvider provider = getProviderFromUri(context, uri, providerMap); if (provider == null) { return null; } try { return provider.call(context.getPackageName(), uri.getAuthority(), method, uriString, null); method, uri.toString(), bundle); } catch (RemoteException e) { return null; } Loading @@ -389,7 +418,7 @@ public class TileUtils { if (uri == null) { return null; } String authority = uri.getAuthority(); final String authority = uri.getAuthority(); if (TextUtils.isEmpty(authority)) { return null; } Loading @@ -400,7 +429,7 @@ public class TileUtils { } /** Returns the first path segment of the uri if it exists as the method, otherwise null. */ static String getMethodFromUri(Uri uri) { private static String getMethodFromUri(Uri uri) { if (uri == null) { return null; } Loading packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +11 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java +4 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; import com.android.settingslib.R; import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.Tile; Loading Loading @@ -84,7 +85,7 @@ public class AdaptiveIconTest { @Test public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) .when(tile).getIcon(mContext); Loading @@ -97,7 +98,7 @@ public class AdaptiveIconTest { @Test public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) .when(tile).getIcon(mContext); final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); Loading @@ -110,7 +111,7 @@ public class AdaptiveIconTest { @Test public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, R.color.bt_outline_color); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) Loading Loading
packages/SettingsLib/Tile/src/com/android/settingslib/drawer/ActivityTile.java 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * 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.settingslib.drawer; import android.content.Context; import android.content.Intent; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Parcel; import android.util.Log; import java.util.List; import java.util.Objects; /** * Description of a single dashboard tile which is generated from an activity. */ public class ActivityTile extends Tile { private static final String TAG = "ActivityTile"; public ActivityTile(ComponentInfo info, String category) { super(info, category); setMetaData(info.metaData); } ActivityTile(Parcel in) { super(in); } @Override public int getId() { return Objects.hash(getPackageName(), getComponentName()); } @Override public String getDescription() { return getPackageName() + "/" + getComponentName(); } @Override protected CharSequence getComponentLabel(Context context) { final PackageManager pm = context.getPackageManager(); final ComponentInfo info = getComponentInfo(context); return info == null ? null : info.loadLabel(pm); } @Override protected ComponentInfo getComponentInfo(Context context) { if (mComponentInfo == null) { final PackageManager pm = context.getApplicationContext().getPackageManager(); final Intent intent = getIntent(); final List<ResolveInfo> infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); if (infoList != null && !infoList.isEmpty()) { mComponentInfo = infoList.get(0).activityInfo; setMetaData(mComponentInfo.metaData); } else { Log.e(TAG, "Cannot find package info for " + intent.getComponent().flattenToString()); } } return mComponentInfo; } }
packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +47 −63 Original line number Diff line number Diff line Loading @@ -30,9 +30,8 @@ import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.drawable.Icon; Loading @@ -47,13 +46,11 @@ import androidx.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; /** * Description of a single dashboard tile that the user can select. */ public class Tile implements Parcelable { public abstract class Tile implements Parcelable { private static final String TAG = "Tile"; Loading @@ -64,28 +61,27 @@ public class Tile implements Parcelable { @VisibleForTesting long mLastUpdateTime; private final String mActivityPackage; private final String mActivityName; private final String mComponentPackage; private final String mComponentName; private final Intent mIntent; private ActivityInfo mActivityInfo; protected ComponentInfo mComponentInfo; private CharSequence mSummaryOverride; private Bundle mMetaData; private String mCategory; public Tile(ActivityInfo activityInfo, String category) { mActivityInfo = activityInfo; mActivityPackage = mActivityInfo.packageName; mActivityName = mActivityInfo.name; mMetaData = activityInfo.metaData; public Tile(ComponentInfo info, String category) { mComponentInfo = info; mComponentPackage = mComponentInfo.packageName; mComponentName = mComponentInfo.name; mCategory = category; mIntent = new Intent().setClassName(mActivityPackage, mActivityName); mIntent = new Intent().setClassName(mComponentPackage, mComponentName); } Tile(Parcel in) { mActivityPackage = in.readString(); mActivityName = in.readString(); mIntent = new Intent().setClassName(mActivityPackage, mActivityName); mComponentPackage = in.readString(); mComponentName = in.readString(); mIntent = new Intent().setClassName(mComponentPackage, mComponentName); final int number = in.readInt(); for (int i = 0; i < number; i++) { userHandle.add(UserHandle.CREATOR.createFromParcel(in)); Loading @@ -101,8 +97,8 @@ public class Tile implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mActivityPackage); dest.writeString(mActivityName); dest.writeString(mComponentPackage); dest.writeString(mComponentName); final int size = userHandle.size(); dest.writeInt(size); for (int i = 0; i < size; i++) { Loading @@ -112,16 +108,22 @@ public class Tile implements Parcelable { dest.writeBundle(mMetaData); } public int getId() { return Objects.hash(mActivityPackage, mActivityName); } /** * Unique ID of the tile */ public abstract int getId(); public String getDescription() { return mActivityPackage + "/" + mActivityName; } /** * Human-readable description of the tile */ public abstract String getDescription(); public String getPackageName() { return mActivityPackage; return mComponentPackage; } public String getComponentName() { return mComponentName; } /** Loading Loading @@ -154,7 +156,7 @@ public class Tile implements Parcelable { } /** * Check whether title has order. * Check whether tile has order. */ public boolean hasOrder() { return mMetaData.containsKey(META_DATA_KEY_ORDER) Loading @@ -170,14 +172,14 @@ public class Tile implements Parcelable { final PackageManager packageManager = context.getPackageManager(); if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) { if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) { // If has as uri to provide dynamic summary, skip loading here. UI will later load // If has as uri to provide dynamic title, skip loading here. UI will later load // at tile binding time. return null; } if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) { try { final Resources res = packageManager.getResourcesForApplication(mActivityPackage); packageManager.getResourcesForApplication(mComponentPackage); title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { Log.w(TAG, "Couldn't find info", e); Loading @@ -186,18 +188,15 @@ public class Tile implements Parcelable { title = mMetaData.getString(META_DATA_PREFERENCE_TITLE); } } // Set the preference title to the activity's label if no // meta-data is found // Set the preference title by the component if no meta-data is found if (title == null) { final ActivityInfo activityInfo = getActivityInfo(context); if (activityInfo == null) { return null; } title = activityInfo.loadLabel(packageManager); title = getComponentLabel(context); } return title; } protected abstract CharSequence getComponentLabel(Context context); /** * Overrides the summary. This can happen when injected tile wants to provide dynamic summary. */ Loading Loading @@ -225,7 +224,7 @@ public class Tile implements Parcelable { if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) { try { final Resources res = packageManager.getResourcesForApplication(mActivityPackage); packageManager.getResourcesForApplication(mComponentPackage); summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY)); } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { Log.d(TAG, "Couldn't find info", e); Loading Loading @@ -281,24 +280,24 @@ public class Tile implements Parcelable { return null; } ensureMetadataNotStale(context); final ActivityInfo activityInfo = getActivityInfo(context); if (activityInfo == null) { Log.w(TAG, "Cannot find ActivityInfo for " + getDescription()); final ComponentInfo componentInfo = getComponentInfo(context); if (componentInfo == null) { Log.w(TAG, "Cannot find ComponentInfo for " + getDescription()); return null; } int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON); // Set the icon if (iconResId == 0) { // Only fallback to activityinfo.icon if metadata does not contain ICON_URI. // Only fallback to componentInfo.icon if metadata does not contain ICON_URI. // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this // level is too complex because we don't have a strong threading contract for this class if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) { iconResId = activityInfo.icon; iconResId = componentInfo.icon; } } if (iconResId != 0) { final Icon icon = Icon.createWithResource(activityInfo.packageName, iconResId); final Icon icon = Icon.createWithResource(componentInfo.packageName, iconResId); if (isIconTintable(context)) { final TypedArray a = context.obtainStyledAttributes(new int[]{ android.R.attr.colorControlNormal}); Loading Loading @@ -331,41 +330,26 @@ public class Tile implements Parcelable { final PackageManager pm = context.getApplicationContext().getPackageManager(); try { final long lastUpdateTime = pm.getPackageInfo(mActivityPackage, final long lastUpdateTime = pm.getPackageInfo(mComponentPackage, PackageManager.GET_META_DATA).lastUpdateTime; if (lastUpdateTime == mLastUpdateTime) { // All good. Do nothing return; } // App has been updated since we load metadata last time. Reload metadata. mActivityInfo = null; getActivityInfo(context); mComponentInfo = null; getComponentInfo(context); mLastUpdateTime = lastUpdateTime; } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "Can't find package, probably uninstalled."); } } private ActivityInfo getActivityInfo(Context context) { if (mActivityInfo == null) { final PackageManager pm = context.getApplicationContext().getPackageManager(); final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName); final List<ResolveInfo> infoList = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA); if (infoList != null && !infoList.isEmpty()) { mActivityInfo = infoList.get(0).activityInfo; mMetaData = mActivityInfo.metaData; } else { Log.e(TAG, "Cannot find package info for " + intent.getComponent().flattenToString()); } } return mActivityInfo; } protected abstract ComponentInfo getComponentInfo(Context context); public static final Creator<Tile> CREATOR = new Creator<Tile>() { public Tile createFromParcel(Parcel source) { return new Tile(source); return new ActivityTile(source); } public Tile[] newArray(int size) { Loading
packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +81 −52 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Context; import android.content.IContentProvider; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ComponentInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; Loading Loading @@ -127,6 +128,7 @@ public class TileUtils { */ public static final String META_DATA_PREFERENCE_ICON_BACKGROUND_HINT = "com.android.settings.bg.hint"; /** * Name of the meta-data item that should be set in the AndroidManifest.xml * to specify the icon background color as raw ARGB. Loading Loading @@ -220,10 +222,11 @@ public class TileUtils { public static List<DashboardCategory> getCategories(Context context, Map<Pair<String, String>, Tile> cache) { final long startTime = System.currentTimeMillis(); boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; ArrayList<Tile> tiles = new ArrayList<>(); UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); final boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; final ArrayList<Tile> tiles = new ArrayList<>(); final UserManager userManager = (UserManager) context.getSystemService( Context.USER_SERVICE); for (UserHandle user : userManager.getUserProfiles()) { // TODO: Needs much optimization, too many PM queries going on here. if (user.getIdentifier() == ActivityManager.getCurrentUser()) { Loading @@ -240,7 +243,7 @@ public class TileUtils { } } HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); final HashMap<String, DashboardCategory> categoryMap = new HashMap<>(); for (Tile tile : tiles) { final String categoryKey = tile.getCategory(); DashboardCategory category = categoryMap.get(categoryKey); Loading @@ -255,7 +258,7 @@ public class TileUtils { } category.addTile(tile); } ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); final ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values()); for (DashboardCategory category : categories) { category.sortTiles(); } Loading @@ -275,33 +278,45 @@ public class TileUtils { if (requireSettings) { intent.setPackage(SETTING_PKG); } getActivityTiles(context, user, addedCache, defaultCategory, outTiles, intent); } private static void getActivityTiles(Context context, UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent) { final PackageManager pm = context.getPackageManager(); List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA, user.getIdentifier()); for (ResolveInfo resolved : results) { if (!resolved.system) { // Do not allow any app to add to settings, only system ones. continue; } ActivityInfo activityInfo = resolved.activityInfo; Bundle metaData = activityInfo.metaData; String categoryKey = defaultCategory; final ActivityInfo activityInfo = resolved.activityInfo; final Bundle metaData = activityInfo.metaData; getTile(user, addedCache, defaultCategory, outTiles, intent, metaData, activityInfo); } } private static void getTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData, ComponentInfo componentInfo) { String categoryKey = defaultCategory; // Load category if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY)) && categoryKey == null) { Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent " Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent " + intent + " missing metadata " + (metaData == null ? "" : EXTRA_CATEGORY_KEY)); continue; return; } else { categoryKey = metaData.getString(EXTRA_CATEGORY_KEY); } Pair<String, String> key = new Pair<>(activityInfo.packageName, activityInfo.name); final Pair<String, String> key = new Pair<>(componentInfo.packageName, componentInfo.name); Tile tile = addedCache.get(key); if (tile == null) { tile = new Tile(activityInfo, categoryKey); tile = new ActivityTile(componentInfo, categoryKey); addedCache.put(key, tile); } else { tile.setMetaData(metaData); Loading @@ -314,6 +329,21 @@ public class TileUtils { outTiles.add(tile); } } /** * Returns the complete uri from the meta data key of the tile. * @param tile Tile which contains meta data * @param metaDataKey Key mapping to the uri in meta data * @return Uri associated with the key */ public static Uri getCompleteUri(Tile tile, String metaDataKey) { final String uriString = tile.getMetaData().getString(metaDataKey); if (TextUtils.isEmpty(uriString)) { return null; } return Uri.parse(uriString); } /** Loading @@ -321,17 +351,17 @@ public class TileUtils { * * @param context context * @param packageName package name of the target activity * @param uriString URI for the content provider * @param uri URI for the content provider * @param providerMap Maps URI authorities to providers * @return package name and resource id of the icon specified */ public static Pair<String, Integer> getIconFromUri(Context context, String packageName, String uriString, Map<String, IContentProvider> providerMap) { Bundle bundle = getBundleFromUri(context, uriString, providerMap); Uri uri, Map<String, IContentProvider> providerMap) { final Bundle bundle = getBundleFromUri(context, uri, providerMap, null); if (bundle == null) { return null; } String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE); final String iconPackageName = bundle.getString(EXTRA_PREFERENCE_ICON_PACKAGE); if (TextUtils.isEmpty(iconPackageName)) { return null; } Loading @@ -342,7 +372,7 @@ public class TileUtils { // Icon can either come from the target package or from the Settings app. if (iconPackageName.equals(packageName) || iconPackageName.equals(context.getPackageName())) { return Pair.create(iconPackageName, bundle.getInt(META_DATA_PREFERENCE_ICON, 0)); return Pair.create(iconPackageName, resId); } return null; } Loading @@ -351,34 +381,33 @@ public class TileUtils { * Gets text associated with the input key from the content provider. * * @param context context * @param uriString URI for the content provider * @param uri URI for the content provider * @param providerMap Maps URI authorities to providers * @param key Key mapping to the text in bundle returned by the content provider * @return Text associated with the key, if returned by the content provider */ public static String getTextFromUri(Context context, String uriString, public static String getTextFromUri(Context context, Uri uri, Map<String, IContentProvider> providerMap, String key) { Bundle bundle = getBundleFromUri(context, uriString, providerMap); final Bundle bundle = getBundleFromUri(context, uri, providerMap, null); return (bundle != null) ? bundle.getString(key) : null; } private static Bundle getBundleFromUri(Context context, String uriString, Map<String, IContentProvider> providerMap) { if (TextUtils.isEmpty(uriString)) { private static Bundle getBundleFromUri(Context context, Uri uri, Map<String, IContentProvider> providerMap, Bundle bundle) { if (uri == null) { return null; } Uri uri = Uri.parse(uriString); String method = getMethodFromUri(uri); final String method = getMethodFromUri(uri); if (TextUtils.isEmpty(method)) { return null; } IContentProvider provider = getProviderFromUri(context, uri, providerMap); final IContentProvider provider = getProviderFromUri(context, uri, providerMap); if (provider == null) { return null; } try { return provider.call(context.getPackageName(), uri.getAuthority(), method, uriString, null); method, uri.toString(), bundle); } catch (RemoteException e) { return null; } Loading @@ -389,7 +418,7 @@ public class TileUtils { if (uri == null) { return null; } String authority = uri.getAuthority(); final String authority = uri.getAuthority(); if (TextUtils.isEmpty(authority)) { return null; } Loading @@ -400,7 +429,7 @@ public class TileUtils { } /** Returns the first path segment of the uri if it exists as the method, otherwise null. */ static String getMethodFromUri(Uri uri) { private static String getMethodFromUri(Uri uri) { if (uri == null) { return null; } Loading
packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java +11 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AdaptiveIconTest.java +4 −3 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; import com.android.settingslib.R; import com.android.settingslib.drawer.ActivityTile; import com.android.settingslib.drawer.CategoryKey; import com.android.settingslib.drawer.Tile; Loading Loading @@ -84,7 +85,7 @@ public class AdaptiveIconTest { @Test public void setBackgroundColor_externalTileWithBackgroundColorRawValue_shouldUpdateIcon() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_ARGB, 0xff0000); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) .when(tile).getIcon(mContext); Loading @@ -97,7 +98,7 @@ public class AdaptiveIconTest { @Test public void setBackgroundColor_tileWithoutBackgroundColor_shouldSetDefaultBackgroundColor() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) .when(tile).getIcon(mContext); final AdaptiveIcon icon = new AdaptiveIcon(mContext, new ColorDrawable(Color.BLACK)); Loading @@ -110,7 +111,7 @@ public class AdaptiveIconTest { @Test public void onBindTile_externalTileWithBackgroundColorHint_shouldUpdateIcon() { final Tile tile = spy(new Tile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); final Tile tile = spy(new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE)); mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, R.color.bt_outline_color); doReturn(Icon.createWithResource(mContext, R.drawable.ic_system_update)) Loading