Merge "Developer option to force RTL layout direction" into klp-dev
diff --git a/res/drawable-hdpi/ic_appwidget_settings_gps_off_holo.png b/res/drawable-hdpi/ic_appwidget_settings_location_off_holo.png
similarity index 100%
rename from res/drawable-hdpi/ic_appwidget_settings_gps_off_holo.png
rename to res/drawable-hdpi/ic_appwidget_settings_location_off_holo.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_appwidget_settings_gps_on_holo.png b/res/drawable-hdpi/ic_appwidget_settings_location_on_holo.png
similarity index 100%
rename from res/drawable-hdpi/ic_appwidget_settings_gps_on_holo.png
rename to res/drawable-hdpi/ic_appwidget_settings_location_on_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_appwidget_settings_gps_off_holo.png b/res/drawable-mdpi/ic_appwidget_settings_location_off_holo.png
similarity index 100%
rename from res/drawable-mdpi/ic_appwidget_settings_gps_off_holo.png
rename to res/drawable-mdpi/ic_appwidget_settings_location_off_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_appwidget_settings_gps_on_holo.png b/res/drawable-mdpi/ic_appwidget_settings_location_on_holo.png
similarity index 100%
rename from res/drawable-mdpi/ic_appwidget_settings_gps_on_holo.png
rename to res/drawable-mdpi/ic_appwidget_settings_location_on_holo.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_appwidget_settings_gps_off_holo.png b/res/drawable-xhdpi/ic_appwidget_settings_location_off_holo.png
similarity index 100%
rename from res/drawable-xhdpi/ic_appwidget_settings_gps_off_holo.png
rename to res/drawable-xhdpi/ic_appwidget_settings_location_off_holo.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_appwidget_settings_gps_on_holo.png b/res/drawable-xhdpi/ic_appwidget_settings_location_on_holo.png
similarity index 100%
rename from res/drawable-xhdpi/ic_appwidget_settings_gps_on_holo.png
rename to res/drawable-xhdpi/ic_appwidget_settings_location_on_holo.png
Binary files differ
diff --git a/res/layout/widget.xml b/res/layout/widget.xml
index d000a46..a125d80 100644
--- a/res/layout/widget.xml
+++ b/res/layout/widget.xml
@@ -95,7 +95,7 @@
         />
 
     <LinearLayout
-        android:id="@+id/btn_gps"
+        android:id="@+id/btn_location"
         android:layout_width="0dip"
         android:layout_weight="1"
         android:layout_height="match_parent"
@@ -106,7 +106,7 @@
         android:orientation="vertical">
 
         <ImageView
-            android:id="@+id/img_gps"
+            android:id="@+id/img_location"
             android:layout_width="match_parent"
             android:layout_height="0dip"
             android:layout_weight="1"
@@ -115,7 +115,7 @@
             />
 
         <ImageView
-            android:id="@+id/ind_gps"
+            android:id="@+id/ind_location"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:scaleType="fitXY"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a4e51e0..b3a68ae 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2368,6 +2368,10 @@
     <string name="location_category_recent_location_requests">Recent location requests</string>
     <!-- [CHAR LIMIT=30] Location settings screen, sub category for location services -->
     <string name="location_category_location_services">Location services</string>
+    <!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
+    <string name="location_high_battery_use">High battery use</string>
+    <!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
+    <string name="location_low_battery_use">Low battery use</string>
     <!-- [CHAR LIMIT=30] Location mode screen, screen title -->
     <string name="location_mode_screen_title">Location mode</string>
     <!-- [CHAR LIMIT=130] Location mode screen, description for high accuracy mode -->
@@ -3694,8 +3698,8 @@
     <string name="gadget_wifi">Wi\u2011Fi</string>
     <!-- Spoken description for the Bluetooth power control gadget. -->
     <string name="gadget_bluetooth">Bluetooth</string>
-    <!-- Spoken description for the GPS power control gadget. -->
-    <string name="gadget_gps">GPS</string>
+    <!-- Spoken description for the Location power control gadget. -->
+    <string name="gadget_location">Location</string>
     <!-- Spoken description for the data synchronization gadget. -->
     <string name="gadget_sync">Sync</string>
     <!-- Template for brightness gadget toggle button description. -->
diff --git a/src/com/android/settings/accessibility/AccessibilityUtils.java b/src/com/android/settings/accessibility/AccessibilityUtils.java
index 66a3ed2..7282bce 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtils.java
@@ -33,7 +33,8 @@
  */
 class AccessibilityUtils {
     /**
-     * @return the set of enabled accessibility services
+     * @return the set of enabled accessibility services. If there are not services
+     * it returned the unmodifiable {@link Collections#emptySet()}.
      */
     static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
         final String enabledServicesSetting = Settings.Secure.getString(
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 3059dcc..0c568f0 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -39,6 +39,8 @@
 import com.android.settings.R;
 import com.android.settings.accessibility.ToggleSwitch.OnBeforeCheckedChangeListener;
 
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -81,6 +83,10 @@
         Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
                 getActivity());
 
+        if (enabledServices == (Set<?>) Collections.emptySet()) {
+            enabledServices = new HashSet<ComponentName>();
+        }
+
         // Determine enabled services and accessibility state.
         ComponentName toggledService = ComponentName.unflattenFromString(preferenceKey);
         boolean accessibilityEnabled = false;
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index 1f012e4..97907de 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -36,8 +36,11 @@
 public class LocationSettings extends LocationSettingsBase
         implements CompoundButton.OnCheckedChangeListener {
     private static final String TAG = LocationSettings.class.getSimpleName();
+    /** Key for preference screen "Mode" */
     private static final String KEY_LOCATION_MODE = "location_mode";
+    /** Key for preference category "Recent location requests" */
     private static final String KEY_RECENT_LOCATION_REQUESTS = "recent_location_requests";
+    /** Key for preference category "Location services" */
     private static final String KEY_LOCATION_SERVICES = "location_services";
 
     private Switch mSwitch;
@@ -100,6 +103,9 @@
 
         Activity activity = getActivity();
 
+        RecentLocationApps recentApps = new RecentLocationApps(activity);
+        recentApps.fillAppList(mRecentLocationRequests);
+
         if (activity instanceof PreferenceActivity) {
             PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
             // Only show the master switch when we're not in multi-pane mode, and not being used as
diff --git a/src/com/android/settings/location/RecentLocationApps.java b/src/com/android/settings/location/RecentLocationApps.java
new file mode 100644
index 0000000..113928f
--- /dev/null
+++ b/src/com/android/settings/location/RecentLocationApps.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.location;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceManager;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+
+import com.android.settings.R;
+
+import java.util.List;
+
+/**
+ * Retrieves the information of applications which accessed location recently.
+ */
+public class RecentLocationApps {
+    private static final String TAG = RecentLocationApps.class.getSimpleName();
+
+    private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
+
+    private Context mContext;
+    PackageManager mPackageManager;
+
+    public RecentLocationApps(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    /**
+     * Fills a list of applications which queried location recently within
+     * specified time.
+     */
+    public void fillAppList(PreferenceCategory container) {
+        AppOpsManager aoManager =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
+                new int[] {
+                    AppOpsManager.OP_MONITOR_LOCATION,
+                AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
+                });
+        PreferenceManager preferenceManager = container.getPreferenceManager();
+        long now = System.currentTimeMillis();
+        for (AppOpsManager.PackageOps ops : appOps) {
+            processPackageOps(now, container, preferenceManager, ops);
+        }
+    }
+
+    private void processPackageOps(
+            long now,
+            PreferenceCategory container,
+            PreferenceManager preferenceManager,
+            AppOpsManager.PackageOps ops) {
+        String packageName = ops.getPackageName();
+        List<AppOpsManager.OpEntry> entries = ops.getOps();
+        boolean highBattery = false;
+        boolean normalBattery = false;
+        for (AppOpsManager.OpEntry entry : entries) {
+            // If previous location activity is older than designated interval, ignore this app.
+            if (now - entry.getTime() <= RECENT_TIME_INTERVAL_MILLIS) {
+                switch (entry.getOp()) {
+                    case AppOpsManager.OP_MONITOR_LOCATION:
+                        normalBattery = true;
+                        break;
+                    case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
+                        highBattery = true;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        if (!highBattery && !normalBattery) {
+            if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                Log.v(TAG, packageName + " hadn't used location within the time interval.");
+            }
+            return;
+        }
+
+        try {
+            PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+            ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
+                    packageName, PackageManager.GET_META_DATA);
+            screen.setIcon(mPackageManager.getApplicationIcon(appInfo));
+            screen.setTitle(mPackageManager.getApplicationLabel(appInfo));
+            // if used both high and low battery within the time interval, show as "high
+            // battery"
+            if (highBattery) {
+                screen.setSummary(R.string.location_high_battery_use);
+            } else {
+                screen.setSummary(R.string.location_low_battery_use);
+            }
+            container.addPreference(screen);
+        } catch (PackageManager.NameNotFoundException e) {
+            // ignore the current app and move on to the next.
+        }
+    }
+}
diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
index 5d581d8..7beb17c 100644
--- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java
+++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
@@ -59,7 +59,7 @@
     private static final int BUTTON_WIFI = 0;
     private static final int BUTTON_BRIGHTNESS = 1;
     private static final int BUTTON_SYNC = 2;
-    private static final int BUTTON_GPS = 3;
+    private static final int BUTTON_LOCATION = 3;
     private static final int BUTTON_BLUETOOTH = 4;
 
     // This widget keeps track of two sets of states:
@@ -102,7 +102,7 @@
 
     private static final StateTracker sWifiState = new WifiStateTracker();
     private static final StateTracker sBluetoothState = new BluetoothStateTracker();
-    private static final StateTracker sGpsState = new GpsStateTracker();
+    private static final StateTracker sLocationState = new LocationStateTracker();
     private static final StateTracker sSyncState = new SyncStateTracker();
     private static SettingsObserver sSettingsObserver;
 
@@ -506,24 +506,23 @@
     }
 
     /**
-     * Subclass of StateTracker for GPS state.
+     * Subclass of StateTracker for location state.
      */
-    private static final class GpsStateTracker extends StateTracker {
-        public int getContainerId() { return R.id.btn_gps; }
-        public int getButtonId() { return R.id.img_gps; }
-        public int getIndicatorId() { return R.id.ind_gps; }
-        public int getButtonDescription() { return R.string.gadget_gps; }
+    private static final class LocationStateTracker extends StateTracker {
+        public int getContainerId() { return R.id.btn_location; }
+        public int getButtonId() { return R.id.img_location; }
+        public int getIndicatorId() { return R.id.ind_location; }
+        public int getButtonDescription() { return R.string.gadget_location; }
         public int getButtonImageId(boolean on) {
-            return on ? R.drawable.ic_appwidget_settings_gps_on_holo
-                    : R.drawable.ic_appwidget_settings_gps_off_holo;
+            return on ? R.drawable.ic_appwidget_settings_location_on_holo
+                    : R.drawable.ic_appwidget_settings_location_off_holo;
         }
 
         @Override
         public int getActualState(Context context) {
             ContentResolver resolver = context.getContentResolver();
-            boolean on = Settings.Secure.isLocationProviderEnabled(
-                resolver, LocationManager.GPS_PROVIDER);
-            return on ? STATE_ENABLED : STATE_DISABLED;
+            return Settings.Secure.isLocationMasterSwitchEnabled(resolver)
+                    ? STATE_ENABLED : STATE_DISABLED;
         }
 
         @Override
@@ -542,15 +541,11 @@
                     final UserManager um =
                             (UserManager) context.getSystemService(Context.USER_SERVICE);
                     if (!um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) {
-                        Settings.Secure.setLocationProviderEnabled(
-                            resolver,
-                            LocationManager.GPS_PROVIDER,
-                            desiredState);
+                        Settings.Secure.setLocationMasterSwitchEnabled(resolver, desiredState);
                         return desiredState;
                     }
-                    return Settings.Secure.isLocationProviderEnabled(
-                            resolver,
-                            LocationManager.GPS_PROVIDER);
+
+                    return Settings.Secure.isLocationMasterSwitchEnabled(resolver);
                 }
 
                 @Override
@@ -669,8 +664,8 @@
         views.setOnClickPendingIntent(R.id.btn_sync,
                 getLaunchPendingIntent(context,
                         BUTTON_SYNC));
-        views.setOnClickPendingIntent(R.id.btn_gps,
-                getLaunchPendingIntent(context, BUTTON_GPS));
+        views.setOnClickPendingIntent(R.id.btn_location,
+                getLaunchPendingIntent(context, BUTTON_LOCATION));
         views.setOnClickPendingIntent(R.id.btn_bluetooth,
                 getLaunchPendingIntent(context,
                         BUTTON_BLUETOOTH));
@@ -701,7 +696,7 @@
     private static void updateButtons(RemoteViews views, Context context) {
         sWifiState.setImageViewResources(context, views);
         sBluetoothState.setImageViewResources(context, views);
-        sGpsState.setImageViewResources(context, views);
+        sLocationState.setImageViewResources(context, views);
         sSyncState.setImageViewResources(context, views);
 
         if (getBrightnessMode(context)) {
@@ -782,7 +777,7 @@
         } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
             sBluetoothState.onActualStateChange(context, intent);
         } else if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(action)) {
-            sGpsState.onActualStateChange(context, intent);
+            sLocationState.onActualStateChange(context, intent);
         } else if (ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED.equals(action)) {
             sSyncState.onActualStateChange(context, intent);
         } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
@@ -794,8 +789,8 @@
                 toggleBrightness(context);
             } else if (buttonId == BUTTON_SYNC) {
                 sSyncState.toggleState(context);
-            } else if (buttonId == BUTTON_GPS) {
-                sGpsState.toggleState(context);
+            } else if (buttonId == BUTTON_LOCATION) {
+                sLocationState.toggleState(context);
             } else if (buttonId == BUTTON_BLUETOOTH) {
                 sBluetoothState.toggleState(context);
             }