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;
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.
*/
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();
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,
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.
boolean enabled = (mode != Settings.Secure.LOCATION_MODE_OFF);
mLocationMode.setEnabled(enabled);
- mRecentLocationRequests.setEnabled(enabled);
mLocationServices.setEnabled(enabled);
if (enabled != mSwitch.isChecked()) {
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;
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;
* 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();
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());
}
}
- 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;
}
/**
package com.android.settings.location;
-import android.R;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
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;
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;
* {@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
*/
*
* Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
*
- * TODO: sort alphabetically
- *
* TODO: unit test
*/
public static List<InjectedSetting> getSettings(Context context) {
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);
}
}
+ 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) {
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);
+ Preference pref = addServiceSetting(context, prefs, setting, preferenceManager);
+ Intent intent = createUpdatingIntent(context, pref, setting, loader);
+ loader = new StatusLoader(context, intent, loader);
+ }
- // 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);
+ // 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);
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) {
}
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;
}
}