Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25518c3b authored by Lifu Tang's avatar Lifu Tang
Browse files

Sort "Location services" alphabetically

* Sort "Location services" items by title

* Throttle the rate of loading status messages

Change-Id: Iecb039a4ab94a810e11ef3e426e4a4b5c8c75c37
parent e873aeb3
Loading
Loading
Loading
Loading
+38 −7
Original line number Diff line number Diff line
@@ -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()) {
+3 −25
Original line number Diff line number Diff line
@@ -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;
    }

    /**
+82 −25
Original line number Diff line number Diff line
@@ -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;
        }

        /**
     * Add settings that other apps have injected.
         * If the current message hasn't been loaded, loads the status messages
         * and set time out for the next message.
         */
    public static void addInjectedSettings(PreferenceGroup group, Context context,
        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);
            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;
    }
}