Sort "Location services" alphabetically

* Sort "Location services" items by title

* Throttle the rate of loading status messages

Change-Id: Iecb039a4ab94a810e11ef3e426e4a4b5c8c75c37
This commit is contained in:
Lifu Tang
2013-08-20 19:40:44 -07:00
parent e873aeb3a6
commit 25518c3b45
3 changed files with 124 additions and 58 deletions

View File

@@ -23,6 +23,8 @@ import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.provider.Settings; import android.provider.Settings;
import android.view.Gravity; import android.view.Gravity;
@@ -32,6 +34,10 @@ import android.widget.Switch;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryStatsHelper;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/** /**
* Location access settings. * Location access settings.
*/ */
@@ -97,7 +103,21 @@ public class LocationSettings extends LocationSettingsBase
mStatsHelper.destroy(); mStatsHelper.destroy();
} }
private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
// If there's some items to display, sort the items and add them to the container.
Collections.sort(prefs, new Comparator<Preference>() {
@Override
public int compare(Preference lhs, Preference rhs) {
return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
}
});
for (Preference entry : prefs) {
container.addPreference(entry);
}
}
private PreferenceScreen createPreferenceHierarchy() { private PreferenceScreen createPreferenceHierarchy() {
final PreferenceActivity activity = (PreferenceActivity) getActivity();
PreferenceScreen root = getPreferenceScreen(); PreferenceScreen root = getPreferenceScreen();
if (root != null) { if (root != null) {
root.removeAll(); root.removeAll();
@@ -110,7 +130,6 @@ public class LocationSettings extends LocationSettingsBase
new Preference.OnPreferenceClickListener() { new Preference.OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
PreferenceActivity activity = (PreferenceActivity) getActivity();
activity.startPreferencePanel( activity.startPreferencePanel(
LocationMode.class.getName(), null, LocationMode.class.getName(), null,
R.string.location_mode_screen_title, null, LocationSettings.this, R.string.location_mode_screen_title, null, LocationSettings.this,
@@ -118,15 +137,28 @@ public class LocationSettings extends LocationSettingsBase
return true; return true;
} }
}); });
final PreferenceManager preferenceManager = getPreferenceManager();
mRecentLocationRequests = mRecentLocationRequests =
(PreferenceCategory) root.findPreference(KEY_RECENT_LOCATION_REQUESTS); (PreferenceCategory) root.findPreference(KEY_RECENT_LOCATION_REQUESTS);
mLocationServices = (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES);
PreferenceActivity activity = (PreferenceActivity) getActivity();
RecentLocationApps recentApps = new RecentLocationApps(activity, mStatsHelper); RecentLocationApps recentApps = new RecentLocationApps(activity, mStatsHelper);
recentApps.fillAppList(mRecentLocationRequests); List<Preference> recentLocationRequests = recentApps.getAppList(preferenceManager);
if (recentLocationRequests.size() > 0) {
addPreferencesSorted(recentLocationRequests, mRecentLocationRequests);
} else {
// If there's no item to display, add a "No recent apps" item.
PreferenceScreen screen = preferenceManager.createPreferenceScreen(activity);
screen.setTitle(R.string.location_no_recent_apps);
screen.setSelectable(false);
screen.setEnabled(false);
mRecentLocationRequests.addPreference(screen);
}
SettingsInjector.addInjectedSettings(mLocationServices, activity, getPreferenceManager()); mLocationServices = (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES);
List<Preference> locationServices = SettingsInjector.getInjectedSettings(
activity, preferenceManager);
addPreferencesSorted(locationServices, mLocationServices);
// Only show the master switch when we're not in multi-pane mode, and not being used as // Only show the master switch when we're not in multi-pane mode, and not being used as
// Setup Wizard. // Setup Wizard.
@@ -174,7 +206,6 @@ public class LocationSettings extends LocationSettingsBase
boolean enabled = (mode != Settings.Secure.LOCATION_MODE_OFF); boolean enabled = (mode != Settings.Secure.LOCATION_MODE_OFF);
mLocationMode.setEnabled(enabled); mLocationMode.setEnabled(enabled);
mRecentLocationRequests.setEnabled(enabled);
mLocationServices.setEnabled(enabled); mLocationServices.setEnabled(enabled);
if (enabled != mSwitch.isChecked()) { if (enabled != mSwitch.isChecked()) {

View File

@@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.util.Log; import android.util.Log;
@@ -35,8 +34,6 @@ import com.android.settings.fuelgauge.BatterySipper;
import com.android.settings.fuelgauge.BatteryStatsHelper; import com.android.settings.fuelgauge.BatteryStatsHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -143,7 +140,7 @@ public class RecentLocationApps {
* Fills a list of applications which queried location recently within * Fills a list of applications which queried location recently within
* specified time. * specified time.
*/ */
public void fillAppList(PreferenceCategory container) { public List<Preference> getAppList(PreferenceManager preferenceManager) {
// Retrieve Uid-based battery blaming info and generate a package to BatterySipper HashMap // Retrieve Uid-based battery blaming info and generate a package to BatterySipper HashMap
// for later faster looking up. // for later faster looking up.
mStatsHelper.refreshStats(); mStatsHelper.refreshStats();
@@ -166,10 +163,9 @@ public class RecentLocationApps {
AppOpsManager.OP_MONITOR_LOCATION, AppOpsManager.OP_MONITOR_LOCATION,
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION, AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
}); });
PreferenceManager preferenceManager = container.getPreferenceManager();
// Process the AppOps list and generate a preference list. // Process the AppOps list and generate a preference list.
ArrayList<PreferenceScreen> prefs = new ArrayList<PreferenceScreen>(); ArrayList<Preference> prefs = new ArrayList<Preference>();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
for (AppOpsManager.PackageOps ops : appOps) { for (AppOpsManager.PackageOps ops : appOps) {
BatterySipperWrapper wrapper = sipperMap.get(ops.getUid()); BatterySipperWrapper wrapper = sipperMap.get(ops.getUid());
@@ -179,25 +175,7 @@ public class RecentLocationApps {
} }
} }
if (prefs.size() > 0) { return prefs;
// If there's some items to display, sort the items and add them to the container.
Collections.sort(prefs, new Comparator<PreferenceScreen>() {
@Override
public int compare(PreferenceScreen lhs, PreferenceScreen rhs) {
return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
}
});
for (PreferenceScreen entry : prefs) {
container.addPreference(entry);
}
} else {
// If there's no item to display, add a "No recent apps" item.
PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
screen.setTitle(R.string.location_no_recent_apps);
screen.setSelectable(false);
screen.setEnabled(false);
container.addPreference(screen);
}
} }
/** /**

View File

@@ -16,7 +16,6 @@
package com.android.settings.location; package com.android.settings.location;
import android.R;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -32,7 +31,6 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.text.TextUtils; import android.text.TextUtils;
@@ -42,6 +40,8 @@ import android.util.Xml;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import com.android.settings.R;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -61,9 +61,10 @@ import java.util.List;
* {@link SettingInjectorService#UPDATE_INTENT}. * {@link SettingInjectorService#UPDATE_INTENT}.
*/ */
class SettingsInjector { class SettingsInjector {
private static final String TAG = "SettingsInjector"; private static final String TAG = "SettingsInjector";
private static final long INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS = 1000;
/** /**
* Intent action marking the receiver as injecting a setting * Intent action marking the receiver as injecting a setting
*/ */
@@ -86,8 +87,6 @@ class SettingsInjector {
* *
* Duplicates some code from {@link android.content.pm.RegisteredServicesCache}. * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
* *
* TODO: sort alphabetically
*
* TODO: unit test * TODO: unit test
*/ */
public static List<InjectedSetting> getSettings(Context context) { public static List<InjectedSetting> getSettings(Context context) {
@@ -167,15 +166,17 @@ class SettingsInjector {
private static InjectedSetting parseAttributes( private static InjectedSetting parseAttributes(
String packageName, String className, Resources res, AttributeSet attrs) { String packageName, String className, Resources res, AttributeSet attrs) {
TypedArray sa = res.obtainAttributes(attrs, R.styleable.InjectedLocationSetting); TypedArray sa = res.obtainAttributes(attrs, android.R.styleable.InjectedLocationSetting);
try { try {
// Note that to help guard against malicious string injection, we do not allow dynamic // Note that to help guard against malicious string injection, we do not allow dynamic
// specification of the label (setting title) // specification of the label (setting title)
final int labelId = sa.getResourceId(R.styleable.InjectedLocationSetting_label, 0); final int labelId = sa.getResourceId(
final String label = sa.getString(R.styleable.InjectedLocationSetting_label); android.R.styleable.InjectedLocationSetting_label, 0);
final int iconId = sa.getResourceId(R.styleable.InjectedLocationSetting_icon, 0); final String label = sa.getString(android.R.styleable.InjectedLocationSetting_label);
final int iconId = sa.getResourceId(
android.R.styleable.InjectedLocationSetting_icon, 0);
final String settingsActivity = final String settingsActivity =
sa.getString(R.styleable.InjectedLocationSetting_settingsActivity); sa.getString(android.R.styleable.InjectedLocationSetting_settingsActivity);
if (Log.isLoggable(TAG, Log.DEBUG)) { if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "parsed labelId: " + labelId + ", label: " + label Log.d(TAG, "parsed labelId: " + labelId + ", label: " + label
+ ", iconId: " + iconId); + ", iconId: " + iconId);
@@ -190,32 +191,84 @@ class SettingsInjector {
} }
} }
private static final class StatusLoader {
private final Context mContext;
private final Intent mIntent;
private final StatusLoader mPrev;
private boolean mLoaded = false;
/**
* Creates a loader and chains with the previous loader.
*/
public StatusLoader(Context context, Intent intent, StatusLoader prev) {
mContext = context;
mIntent = intent;
mPrev = prev;
}
/**
* If the current message hasn't been loaded, loads the status messages
* and set time out for the next message.
*/
public void loadIfNotLoaded() {
if (mLoaded) {
return;
}
mContext.startService(mIntent);
if (mPrev != null) {
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Continue with the next item in the chain.
mPrev.loadIfNotLoaded();
}
};
// Ensure that we start loading the previous setting in the chain if the current
// setting hasn't loaded before the timeout
handler.sendMessageDelayed(
Message.obtain(handler), INJECTED_STATUS_UPDATE_TIMEOUT_MILLIS);
}
mLoaded = true;
}
}
/** /**
* Add settings that other apps have injected. * Gets a list of preferences that other apps have injected.
*
* TODO: extract InjectedLocationSettingGetter that returns an iterable over
* InjectedSetting objects, so that this class can focus on UI
*/ */
public static void addInjectedSettings(PreferenceGroup group, Context context, public static List<Preference> getInjectedSettings(Context context,
PreferenceManager preferenceManager) { PreferenceManager preferenceManager) {
Iterable<InjectedSetting> settings = getSettings(context); Iterable<InjectedSetting> settings = getSettings(context);
ArrayList<Preference> prefs = new ArrayList<Preference>();
StatusLoader loader = null;
for (InjectedSetting setting : settings) { for (InjectedSetting setting : settings) {
Preference pref = addServiceSetting(context, group, setting, preferenceManager); Preference pref = addServiceSetting(context, prefs, setting, preferenceManager);
Intent intent = createUpdatingIntent(context, pref, setting, loader);
// TODO: to prevent churn from multiple live broadcast receivers, don't trigger loader = new StatusLoader(context, intent, loader);
// the next update until the sooner of: the current update completes or 1-2 seconds
// after the current update was started.
updateSetting(context, pref, setting);
} }
// Start a thread to load each list item status.
if (loader != null) {
loader.loadIfNotLoaded();
}
return prefs;
} }
/** /**
* Adds an injected setting to the root with status "Loading...". * Adds an injected setting to the root with status "Loading...".
*/ */
private static PreferenceScreen addServiceSetting(Context context, private static PreferenceScreen addServiceSetting(Context context,
PreferenceGroup group, InjectedSetting info, PreferenceManager preferenceManager) { List<Preference> prefs, InjectedSetting info, PreferenceManager preferenceManager) {
PreferenceScreen screen = preferenceManager.createPreferenceScreen(context); PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);
screen.setTitle(info.title); screen.setTitle(info.title);
screen.setSummary("Loading..."); screen.setSummary(R.string.location_loading_injected_setting);
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
Drawable icon = pm.getDrawable(info.packageName, info.iconId, null); Drawable icon = pm.getDrawable(info.packageName, info.iconId, null);
screen.setIcon(icon); screen.setIcon(icon);
@@ -224,15 +277,17 @@ class SettingsInjector {
settingIntent.setClassName(info.packageName, info.settingsActivity); settingIntent.setClassName(info.packageName, info.settingsActivity);
screen.setIntent(settingIntent); screen.setIntent(settingIntent);
group.addPreference(screen); prefs.add(screen);
return screen; return screen;
} }
/** /**
* Ask the receiver for the current status for the setting, and display it when it replies. * Creates an Intent to ask the receiver for the current status for the setting, and display it
* when it replies.
*/ */
private static void updateSetting(Context context, private static Intent createUpdatingIntent(Context context,
final Preference pref, final InjectedSetting info) { final Preference pref, final InjectedSetting info, final StatusLoader prev) {
final Intent receiverIntent = info.getServiceIntent();
Handler handler = new Handler() { Handler handler = new Handler() {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
@@ -244,14 +299,16 @@ class SettingsInjector {
} }
pref.setSummary(status); pref.setSummary(status);
pref.setEnabled(enabled); pref.setEnabled(enabled);
if (prev != null) {
prev.loadIfNotLoaded();
}
} }
}; };
Messenger messenger = new Messenger(handler); Messenger messenger = new Messenger(handler);
Intent receiverIntent = info.getServiceIntent();
receiverIntent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger); receiverIntent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger);
if (Log.isLoggable(TAG, Log.DEBUG)) { if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, info + ": sending rcv-intent: " + receiverIntent + ", handler: " + handler); Log.d(TAG, info + ": sending rcv-intent: " + receiverIntent + ", handler: " + handler);
} }
context.startService(receiverIntent); return receiverIntent;
} }
} }