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:
@@ -23,6 +23,8 @@ import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.provider.Settings;
|
||||
import android.view.Gravity;
|
||||
@@ -32,6 +34,10 @@ import android.widget.Switch;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.fuelgauge.BatteryStatsHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Location access settings.
|
||||
*/
|
||||
@@ -97,7 +103,21 @@ public class LocationSettings extends LocationSettingsBase
|
||||
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() {
|
||||
final PreferenceActivity activity = (PreferenceActivity) getActivity();
|
||||
PreferenceScreen root = getPreferenceScreen();
|
||||
if (root != null) {
|
||||
root.removeAll();
|
||||
@@ -110,7 +130,6 @@ public class LocationSettings extends LocationSettingsBase
|
||||
new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
PreferenceActivity activity = (PreferenceActivity) getActivity();
|
||||
activity.startPreferencePanel(
|
||||
LocationMode.class.getName(), null,
|
||||
R.string.location_mode_screen_title, null, LocationSettings.this,
|
||||
@@ -118,15 +137,28 @@ public class LocationSettings extends LocationSettingsBase
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final PreferenceManager preferenceManager = getPreferenceManager();
|
||||
|
||||
mRecentLocationRequests =
|
||||
(PreferenceCategory) root.findPreference(KEY_RECENT_LOCATION_REQUESTS);
|
||||
mLocationServices = (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES);
|
||||
|
||||
PreferenceActivity activity = (PreferenceActivity) getActivity();
|
||||
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
|
||||
// Setup Wizard.
|
||||
@@ -174,7 +206,6 @@ public class LocationSettings extends LocationSettingsBase
|
||||
|
||||
boolean enabled = (mode != Settings.Secure.LOCATION_MODE_OFF);
|
||||
mLocationMode.setEnabled(enabled);
|
||||
mRecentLocationRequests.setEnabled(enabled);
|
||||
mLocationServices.setEnabled(enabled);
|
||||
|
||||
if (enabled != mSwitch.isChecked()) {
|
||||
|
@@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.util.Log;
|
||||
@@ -35,8 +34,6 @@ import com.android.settings.fuelgauge.BatterySipper;
|
||||
import com.android.settings.fuelgauge.BatteryStatsHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@@ -143,7 +140,7 @@ public class RecentLocationApps {
|
||||
* Fills a list of applications which queried location recently within
|
||||
* 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
|
||||
// for later faster looking up.
|
||||
mStatsHelper.refreshStats();
|
||||
@@ -166,10 +163,9 @@ public class RecentLocationApps {
|
||||
AppOpsManager.OP_MONITOR_LOCATION,
|
||||
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
|
||||
});
|
||||
PreferenceManager preferenceManager = container.getPreferenceManager();
|
||||
|
||||
// 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();
|
||||
for (AppOpsManager.PackageOps ops : appOps) {
|
||||
BatterySipperWrapper wrapper = sipperMap.get(ops.getUid());
|
||||
@@ -179,25 +175,7 @@ public class RecentLocationApps {
|
||||
}
|
||||
}
|
||||
|
||||
if (prefs.size() > 0) {
|
||||
// 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);
|
||||
}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.settings.location;
|
||||
|
||||
import android.R;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -32,7 +31,6 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.text.TextUtils;
|
||||
@@ -42,6 +40,8 @@ import android.util.Xml;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -61,9 +61,10 @@ import java.util.List;
|
||||
* {@link SettingInjectorService#UPDATE_INTENT}.
|
||||
*/
|
||||
class 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
|
||||
*/
|
||||
@@ -86,8 +87,6 @@ class SettingsInjector {
|
||||
*
|
||||
* Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
|
||||
*
|
||||
* TODO: sort alphabetically
|
||||
*
|
||||
* TODO: unit test
|
||||
*/
|
||||
public static List<InjectedSetting> getSettings(Context context) {
|
||||
@@ -167,15 +166,17 @@ class SettingsInjector {
|
||||
private static InjectedSetting parseAttributes(
|
||||
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 {
|
||||
// Note that to help guard against malicious string injection, we do not allow dynamic
|
||||
// specification of the label (setting title)
|
||||
final int labelId = sa.getResourceId(R.styleable.InjectedLocationSetting_label, 0);
|
||||
final String label = sa.getString(R.styleable.InjectedLocationSetting_label);
|
||||
final int iconId = sa.getResourceId(R.styleable.InjectedLocationSetting_icon, 0);
|
||||
final int labelId = sa.getResourceId(
|
||||
android.R.styleable.InjectedLocationSetting_label, 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 =
|
||||
sa.getString(R.styleable.InjectedLocationSetting_settingsActivity);
|
||||
sa.getString(android.R.styleable.InjectedLocationSetting_settingsActivity);
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, "parsed labelId: " + labelId + ", label: " + label
|
||||
+ ", 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;
|
||||
|
||||
/**
|
||||
* Add settings that other apps have injected.
|
||||
* Creates a loader and chains with the previous loader.
|
||||
*/
|
||||
public static void addInjectedSettings(PreferenceGroup group, Context context,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 List<Preference> getInjectedSettings(Context context,
|
||||
PreferenceManager preferenceManager) {
|
||||
|
||||
Iterable<InjectedSetting> settings = getSettings(context);
|
||||
ArrayList<Preference> prefs = new ArrayList<Preference>();
|
||||
StatusLoader loader = null;
|
||||
for (InjectedSetting setting : settings) {
|
||||
Preference pref = addServiceSetting(context, group, setting, preferenceManager);
|
||||
|
||||
// TODO: to prevent churn from multiple live broadcast receivers, don't trigger
|
||||
// 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);
|
||||
Preference pref = addServiceSetting(context, prefs, setting, preferenceManager);
|
||||
Intent intent = createUpdatingIntent(context, pref, setting, loader);
|
||||
loader = new StatusLoader(context, intent, loader);
|
||||
}
|
||||
|
||||
// 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...".
|
||||
*/
|
||||
private static PreferenceScreen addServiceSetting(Context context,
|
||||
PreferenceGroup group, InjectedSetting info, PreferenceManager preferenceManager) {
|
||||
List<Preference> prefs, InjectedSetting info, PreferenceManager preferenceManager) {
|
||||
|
||||
PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);
|
||||
screen.setTitle(info.title);
|
||||
screen.setSummary("Loading...");
|
||||
screen.setSummary(R.string.location_loading_injected_setting);
|
||||
PackageManager pm = context.getPackageManager();
|
||||
Drawable icon = pm.getDrawable(info.packageName, info.iconId, null);
|
||||
screen.setIcon(icon);
|
||||
@@ -224,15 +277,17 @@ class SettingsInjector {
|
||||
settingIntent.setClassName(info.packageName, info.settingsActivity);
|
||||
screen.setIntent(settingIntent);
|
||||
|
||||
group.addPreference(screen);
|
||||
prefs.add(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,
|
||||
final Preference pref, final InjectedSetting info) {
|
||||
private static Intent createUpdatingIntent(Context context,
|
||||
final Preference pref, final InjectedSetting info, final StatusLoader prev) {
|
||||
final Intent receiverIntent = info.getServiceIntent();
|
||||
Handler handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
@@ -244,14 +299,16 @@ class SettingsInjector {
|
||||
}
|
||||
pref.setSummary(status);
|
||||
pref.setEnabled(enabled);
|
||||
if (prev != null) {
|
||||
prev.loadIfNotLoaded();
|
||||
}
|
||||
}
|
||||
};
|
||||
Messenger messenger = new Messenger(handler);
|
||||
Intent receiverIntent = info.getServiceIntent();
|
||||
receiverIntent.putExtra(SettingInjectorService.MESSENGER_KEY, messenger);
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, info + ": sending rcv-intent: " + receiverIntent + ", handler: " + handler);
|
||||
}
|
||||
context.startService(receiverIntent);
|
||||
return receiverIntent;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user