OSDN Git Service

Sort "Location services" alphabetically
authorLifu Tang <lifu@google.com>
Wed, 21 Aug 2013 02:40:44 +0000 (19:40 -0700)
committerLifu Tang <lifu@google.com>
Wed, 21 Aug 2013 23:52:14 +0000 (16:52 -0700)
* Sort "Location services" items by title

* Throttle the rate of loading status messages

Change-Id: Iecb039a4ab94a810e11ef3e426e4a4b5c8c75c37

src/com/android/settings/location/LocationSettings.java
src/com/android/settings/location/RecentLocationApps.java
src/com/android/settings/location/SettingsInjector.java

index 3ab6c50..4970d37 100644 (file)
@@ -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()) {
index 9a59563..cbf8351 100644 (file)
@@ -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;
     }
 
     /**
index ea760f5..0b7c172 100644 (file)
@@ -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;
+
+        /**
+         * 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);
@@ -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;
     }
 }