Update gps status icon to be a "high power" location icon.
Move icon to right side of the screen and synchronize status with
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION.
Change-Id: Iea2570501cb18be0489669fd4ea240dc63f9567a
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b9840e2..0908f36 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -26,7 +26,7 @@
<item><xliff:g id="id">ime</xliff:g></item>
<item><xliff:g id="id">sync_failing</xliff:g></item>
<item><xliff:g id="id">sync_active</xliff:g></item>
- <item><xliff:g id="id">gps</xliff:g></item>
+ <item><xliff:g id="id">location</xliff:g></item>
<item><xliff:g id="id">bluetooth</xliff:g></item>
<item><xliff:g id="id">nfc</xliff:g></item>
<item><xliff:g id="id">tty</xliff:g></item>
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 989178a..e5f1cf5 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -177,6 +177,17 @@
*/
public static final String EXTRA_GPS_ENABLED = "enabled";
+ /**
+ * Broadcast intent action indicating that a high power location requests
+ * has either started or stopped being active. The current state of
+ * active location requests should be read from AppOpsManager using
+ * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
+ *
+ * @hide
+ */
+ public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
+ "android.location.HIGH_POWER_REQUEST_CHANGE";
+
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2267372..5e198a2 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<!-- Networking and telephony -->
<uses-permission android:name="android.permission.BLUETOOTH" />
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..657a612
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..80fc24b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png
new file mode 100644
index 0000000..fd8ad64
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png
Binary files differ
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0073e60..33a85c3a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -418,6 +418,9 @@
<!-- Notification text: when GPS has found a fix [CHAR LIMIT=50] -->
<string name="gps_notification_found_text">Location set by GPS</string>
+ <!-- Accessibility text describing the presence of active location requests by one or more apps -->
+ <string name="accessibility_location_active">Location requests active</string>
+
<!-- Content description of the clear button in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_clear_all">Clear all notifications.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 3f8043d..91ddf0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -16,10 +16,8 @@
package com.android.systemui.statusbar.policy;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import android.app.AppOpsManager;
+import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -28,36 +26,39 @@
import android.database.ContentObserver;
import android.location.LocationManager;
import android.os.Handler;
-import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import com.android.systemui.R;
import java.util.ArrayList;
+import java.util.List;
+/**
+ * A controller to manage changes of location related states and update the views accordingly.
+ */
public class LocationController extends BroadcastReceiver {
- private static final String TAG = "StatusBar.LocationController";
+ // The name of the placeholder corresponding to the location request status icon.
+ // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml.
+ private static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location";
+ private static final int LOCATION_STATUS_ICON_ID
+ = R.drawable.stat_sys_device_access_location_found;
- private static final int GPS_NOTIFICATION_ID = 374203-122084;
+ private static final int[] mHighPowerRequestAppOpArray
+ = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION};
private Context mContext;
- private INotificationManager mNotificationService;
+ private AppOpsManager mAppOpsManager;
+ private StatusBarManager mStatusBarManager;
- private ArrayList<LocationGpsStateChangeCallback> mChangeCallbacks =
- new ArrayList<LocationGpsStateChangeCallback>();
+ private boolean mAreActiveLocationRequests;
+ private boolean mIsAirplaneMode;
+
private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks =
new ArrayList<LocationSettingsChangeCallback>();
/**
- * A callback for change in gps status (enabled/disabled, have lock, etc).
- */
- public interface LocationGpsStateChangeCallback {
- public void onLocationGpsStateChanged(boolean inUse, String description);
- }
-
- /**
* A callback for change in location settings (the user has enabled/disabled location).
*/
public interface LocationSettingsChangeCallback {
@@ -74,13 +75,15 @@
mContext = context;
IntentFilter filter = new IntentFilter();
- filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
- filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+ filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+ // Listen for a change in the airplane mode setting so we can defensively turn off the
+ // high power location icon when radios are disabled.
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
context.registerReceiver(this, filter);
- NotificationManager nm = (NotificationManager)context.getSystemService(
- Context.NOTIFICATION_SERVICE);
- mNotificationService = nm.getService();
+ mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mStatusBarManager
+ = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE);
// Register to listen for changes to the location settings
context.getContentResolver().registerContentObserver(
@@ -94,13 +97,11 @@
}
}
});
- }
- /**
- * Add a callback to listen for changes in gps status.
- */
- public void addStateChangedCallback(LocationGpsStateChangeCallback cb) {
- mChangeCallbacks.add(cb);
+ // Examine the current location state and initialize the status view.
+ updateActiveLocationRequests();
+ updateAirplaneMode();
+ refreshViews();
}
/**
@@ -145,76 +146,77 @@
return isGpsEnabled || isNetworkEnabled;
}
+ /**
+ * Returns true if there currently exist active high power location requests.
+ */
+ private boolean areActiveHighPowerLocationRequests() {
+ List<AppOpsManager.PackageOps> packages
+ = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray);
+ // AppOpsManager can return null when there is no requested data.
+ if (packages != null) {
+ final int numPackages = packages.size();
+ for (int packageInd = 0; packageInd < numPackages; packageInd++) {
+ AppOpsManager.PackageOps packageOp = packages.get(packageInd);
+ List<AppOpsManager.OpEntry> opEntries = packageOp.getOps();
+ if (opEntries != null) {
+ final int numOps = opEntries.size();
+ for (int opInd = 0; opInd < numOps; opInd++) {
+ AppOpsManager.OpEntry opEntry = opEntries.get(opInd);
+ // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because
+ // of the mHighPowerRequestAppOpArray filter, but checking defensively.
+ if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
+ if (opEntry.isRunning()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Updates the status view based on the current state of location requests and airplane mode.
+ private void refreshViews() {
+ // The airplane mode check is defensive - there shouldn't be any active high power
+ // location requests when airplane mode is on.
+ if (!mIsAirplaneMode && mAreActiveLocationRequests) {
+ mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0,
+ mContext.getString(R.string.accessibility_location_active));
+ } else {
+ mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER);
+ }
+ }
+
+ // Reads the active location requests and updates the status view if necessary.
+ private void updateActiveLocationRequests() {
+ boolean hadActiveLocationRequests = mAreActiveLocationRequests;
+ mAreActiveLocationRequests = areActiveHighPowerLocationRequests();
+ if (mAreActiveLocationRequests != hadActiveLocationRequests) {
+ refreshViews();
+ }
+ }
+
+ // Reads the airplane mode setting and updates the status view if necessary.
+ private void updateAirplaneMode() {
+ boolean wasAirplaneMode = mIsAirplaneMode;
+ // TODO This probably warrants a utility method in Settings.java.
+ mIsAirplaneMode = (Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
+ if (mIsAirplaneMode != wasAirplaneMode) {
+ refreshViews();
+ }
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
-
- boolean visible;
- int iconId, textResId;
-
- if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
- // GPS is getting fixes
- iconId = com.android.internal.R.drawable.stat_sys_gps_on;
- textResId = R.string.gps_notification_found_text;
- visible = true;
- } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
- // GPS is off
- visible = false;
- iconId = textResId = 0;
- } else {
- // GPS is on, but not receiving fixes
- iconId = R.drawable.stat_sys_gps_acquiring_anim;
- textResId = R.string.gps_notification_searching_text;
- visible = true;
- }
-
- try {
- if (visible) {
- Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
- gpsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
- gpsIntent, 0, null, UserHandle.CURRENT);
- String text = mContext.getText(textResId).toString();
-
- Notification n = new Notification.Builder(mContext)
- .setSmallIcon(iconId)
- .setContentTitle(text)
- .setOngoing(true)
- .setContentIntent(pendingIntent)
- .getNotification();
-
- // Notification.Builder will helpfully fill these out for you no matter what you do
- n.tickerView = null;
- n.tickerText = null;
-
- n.priority = Notification.PRIORITY_HIGH;
-
- int[] idOut = new int[1];
- mNotificationService.enqueueNotificationWithTag(
- mContext.getPackageName(), mContext.getBasePackageName(),
- null,
- GPS_NOTIFICATION_ID,
- n,
- idOut,
- UserHandle.USER_ALL);
-
- for (LocationGpsStateChangeCallback cb : mChangeCallbacks) {
- cb.onLocationGpsStateChanged(true, text);
- }
- } else {
- mNotificationService.cancelNotificationWithTag(
- mContext.getPackageName(), null,
- GPS_NOTIFICATION_ID, UserHandle.USER_ALL);
-
- for (LocationGpsStateChangeCallback cb : mChangeCallbacks) {
- cb.onLocationGpsStateChanged(false, null);
- }
- }
- } catch (android.os.RemoteException ex) {
- // well, it was worth a shot
+ if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) {
+ updateActiveLocationRequests();
+ } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+ updateAirplaneMode();
}
}
}
-
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index cde84dc..6175268 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -552,8 +552,14 @@
allowHighPower = false;
}
}
+ boolean wasHighPowerMonitoring = mOpHighPowerMonitoring;
mOpHighPowerMonitoring = updateMonitoring(allowHighPower, mOpHighPowerMonitoring,
AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION);
+ if (mOpHighPowerMonitoring != wasHighPowerMonitoring) {
+ // send an intent to notify that a high power request has been added/removed.
+ Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
}
/**