Allow system apps to add to settings dashboard

Allow system apps to add a tile to the top level of settings that
links to an activity through adding a filter for a specific action.
Determine the info for the tile based off manifest info for the
activity. Also allow the same for managed profiles, but show a dialog
in between to select which profile.

The category in which the item is to be placed must be in meta-data.
The icon and title can be specified through meta-data as well or
if unspecified the activity's label and icon will be used.

Also added an optional <external-tiles> tag to the dashboard
category xml, this allows Settings to put external tiles
in the middle of some categories (Personal does this).

Bug: 19443117
Change-Id: Idc9938d1549d181103a3030a8784b527215a8399
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 16037c5..bc6cbed 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -75,7 +75,7 @@
 import android.widget.TabWidget;
 
 import com.android.internal.util.UserIcons;
-import com.android.settings.UserSpinnerAdapter.UserDetails;
+import com.android.settings.UserAdapter.UserDetails;
 import com.android.settings.dashboard.DashboardTile;
 import com.android.settings.drawable.CircleFramedDrawable;
 
@@ -114,19 +114,19 @@
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the icon that should be displayed for the preference.
      */
-    private static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
+    public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
 
     /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the title that should be displayed for the preference.
      */
-    private static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
+    public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
 
     /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the summary text that should be displayed for the preference.
      */
-    private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
+    public static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
 
     private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
 
@@ -198,14 +198,17 @@
         if (intent != null) {
             // Find the activity that is in the system image
             PackageManager pm = context.getPackageManager();
-            List<ResolveInfo> list = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            List<ResolveInfo> list = tile.userHandle.size() != 0
+                    ? pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA,
+                            tile.userHandle.get(0).getIdentifier())
+                    : pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
             int listSize = list.size();
             for (int i = 0; i < listSize; i++) {
                 ResolveInfo resolveInfo = list.get(i);
                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
                         != 0) {
-                    Drawable icon = null;
-                    String title = null;
+                    int icon = 0;
+                    CharSequence title = null;
                     String summary = null;
 
                     // Get the activity's meta-data
@@ -215,14 +218,18 @@
                         Bundle metaData = resolveInfo.activityInfo.metaData;
 
                         if (res != null && metaData != null) {
-                            icon = res.getDrawable(
-                                    metaData.getInt(META_DATA_PREFERENCE_ICON), null);
-                            title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
-                            summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
+                            if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
+                                icon = metaData.getInt(META_DATA_PREFERENCE_ICON);
+                            }
+                            if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+                                title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
+                            }
+                            if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
+                                summary = res.getString(
+                                        metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
+                            }
                         }
-                    } catch (NameNotFoundException e) {
-                        // Ignore
-                    } catch (NotFoundException e) {
+                    } catch (NameNotFoundException | NotFoundException e) {
                         // Ignore
                     }
 
@@ -231,10 +238,13 @@
                     if (TextUtils.isEmpty(title)) {
                         title = resolveInfo.loadLabel(pm).toString();
                     }
+                    if (icon == 0) {
+                        icon = resolveInfo.activityInfo.icon;
+                    }
 
                     // Set icon, title and summary for the preference
-                    // TODO:
-                    //tile.icon = icon;
+                    tile.iconRes = icon;
+                    tile.iconPkg = resolveInfo.activityInfo.packageName;
                     tile.title = title;
                     tile.summary = summary;
                     // Replace the intent with this specific activity
@@ -728,14 +738,14 @@
     }
 
     /**
-     * Creates a {@link UserSpinnerAdapter} if there is more than one profile on the device.
+     * Creates a {@link UserAdapter} if there is more than one profile on the device.
      *
      * <p> The adapter can be used to populate a spinner that switches between the Settings
      * app on the different profiles.
      *
-     * @return a {@link UserSpinnerAdapter} or null if there is only one profile.
+     * @return a {@link UserAdapter} or null if there is only one profile.
      */
-    public static UserSpinnerAdapter createUserSpinnerAdapter(UserManager userManager,
+    public static UserAdapter createUserSpinnerAdapter(UserManager userManager,
             Context context) {
         List<UserHandle> userProfiles = userManager.getUserProfiles();
         if (userProfiles.size() < 2) {
@@ -747,12 +757,17 @@
         userProfiles.remove(myUserHandle);
         userProfiles.add(0, myUserHandle);
 
+        return createUserAdapter(userManager, context, userProfiles);
+    }
+
+    public static UserAdapter createUserAdapter(UserManager userManager,
+            Context context, List<UserHandle> userProfiles) {
         ArrayList<UserDetails> userDetails = new ArrayList<UserDetails>(userProfiles.size());
         final int count = userProfiles.size();
         for (int i = 0; i < count; i++) {
             userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
         }
-        return new UserSpinnerAdapter(context, userDetails);
+        return new UserAdapter(context, userDetails);
     }
 
     /**