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
This commit is contained in:
Jason Monk
2015-02-13 15:23:19 -05:00
parent 5528d8952b
commit 2ebc8a0169
14 changed files with 328 additions and 37 deletions

View File

@@ -16,6 +16,8 @@
package com.android.settings;
import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
@@ -49,8 +51,10 @@ import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.transition.TransitionManager;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.util.Xml;
import android.view.Menu;
@@ -81,9 +85,6 @@ import com.android.settings.deviceinfo.Memory;
import com.android.settings.deviceinfo.UsbSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.notification.OtherSoundSettings;
import com.android.settings.search.DynamicIndexableContentMonitor;
import com.android.settings.search.Index;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
import com.android.settings.inputmethod.SpellCheckersSettings;
@@ -96,9 +97,12 @@ import com.android.settings.notification.ConditionProviderSettings;
import com.android.settings.notification.NotificationAccessSettings;
import com.android.settings.notification.NotificationSettings;
import com.android.settings.notification.NotificationStation;
import com.android.settings.notification.OtherSoundSettings;
import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintJobSettingsFragment;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.search.DynamicIndexableContentMonitor;
import com.android.settings.search.Index;
import com.android.settings.sim.SimSettings;
import com.android.settings.tts.TextToSpeechSettings;
import com.android.settings.users.UserSettings;
@@ -110,16 +114,16 @@ import com.android.settings.wifi.AdvancedWifiSettings;
import com.android.settings.wifi.SavedAccessPointsWifiSettings;
import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
public class SettingsActivity extends Activity
implements PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback,
@@ -201,6 +205,35 @@ public class SettingsActivity extends Activity
private static final String EMPTY_QUERY = "";
/**
* Settings will search for system activities of this action and add them as a top level
* settings tile using the following parameters.
*
* <p>A category must be specified in the meta-data for the activity named
* {@link #EXTRA_CATEGORY_KEY}
*
* <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
* otherwise the label for the activity will be used.
*
* <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
* otherwise the icon for the activity will be used.
*
* <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
*/
private static final String EXTRA_SETTINGS_ACTION =
"com.android.settings.action.EXTRA_SETTINGS";
/**
* The key used to get the category from metadata of activities of action
* {@link #EXTRA_SETTINGS_ACTION}
* The value must be one of:
* <li>com.android.settings.category.wireless</li>
* <li>com.android.settings.category.device</li>
* <li>com.android.settings.category.personal</li>
* <li>com.android.settings.category.system</li>
*/
private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
private static boolean sShowNoHomeNotice = false;
private String mFragmentClass;
@@ -1035,6 +1068,17 @@ public class SettingsActivity extends Activity
}
}
sa.recycle();
sa = obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference);
tv = sa.peekValue(
com.android.internal.R.styleable.Preference_key);
if (tv != null && tv.type == TypedValue.TYPE_STRING) {
if (tv.resourceId != 0) {
category.key = getString(tv.resourceId);
} else {
category.key = tv.string.toString();
}
}
sa.recycle();
final int innerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1110,6 +1154,8 @@ public class SettingsActivity extends Activity
category.addTile(tile);
}
} else if (innerNodeName.equals("external-tiles")) {
category.externalIndex = category.getTilesCount();
} else {
XmlUtils.skipCurrentTag(parser);
}
@@ -1231,6 +1277,73 @@ public class SettingsActivity extends Activity
n--;
}
}
addExternalTiles(target);
}
private void addExternalTiles(List<DashboardCategory> target) {
Map<Pair<String, String>, DashboardTile> addedCache =
new ArrayMap<Pair<String, String>, DashboardTile>();
UserManager userManager = UserManager.get(this);
for (UserHandle user : userManager.getUserProfiles()) {
addExternalTiles(target, user, addedCache);
}
}
private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
Map<Pair<String, String>, DashboardTile> addedCache) {
PackageManager pm = getPackageManager();
Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
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;
if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
+ EXTRA_SETTINGS_ACTION + " missing metadata " +
(metaData == null ? "" : EXTRA_CATEGORY_KEY));
continue;
}
String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
DashboardCategory category = getCategory(target, categoryKey);
if (category == null) {
Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
+ "category key " + categoryKey);
continue;
}
Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
activityInfo.name);
DashboardTile tile = addedCache.get(key);
if (tile == null) {
tile = new DashboardTile();
tile.intent = new Intent().setClassName(
activityInfo.packageName, activityInfo.name);
Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
if (category.externalIndex == -1) {
// If no location for external tiles has been specified for this category,
// then just put them at the end.
category.addTile(tile);
} else {
category.addTile(category.externalIndex, tile);
}
addedCache.put(key, tile);
}
tile.userHandle.add(user);
}
}
private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
for (DashboardCategory category : target) {
if (categoryKey.equals(category.key)) {
return category;
}
}
return null;
}
private boolean updateHomeSettingTiles(DashboardTile tile) {