From c750c1fb83fbdec895e236dda7207db4da14ec49 Mon Sep 17 00:00:00 2001 From: David Christie Date: Thu, 8 Aug 2013 12:56:57 -0700 Subject: [PATCH] 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 --- core/res/res/values/config.xml | 2 +- .../android/location/LocationManager.java | 11 + packages/SystemUI/AndroidManifest.xml | 1 + .../stat_sys_device_access_location_found.png | Bin 0 -> 1222 bytes .../stat_sys_device_access_location_found.png | Bin 0 -> 814 bytes .../stat_sys_device_access_location_found.png | Bin 0 -> 1638 bytes packages/SystemUI/res/values/strings.xml | 3 + .../statusbar/policy/LocationController.java | 192 +++++++++--------- .../server/LocationManagerService.java | 6 + 9 files changed, 119 insertions(+), 96 deletions(-) create mode 100644 packages/SystemUI/res/drawable-hdpi/stat_sys_device_access_location_found.png create mode 100644 packages/SystemUI/res/drawable-mdpi/stat_sys_device_access_location_found.png create mode 100644 packages/SystemUI/res/drawable-xhdpi/stat_sys_device_access_location_found.png diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b9840e23312b4..0908f36ffb654 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -26,7 +26,7 @@ ime sync_failing sync_active - gps + location bluetooth nfc tty diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 989178a86f7a3..e5f1cf5e43d8c 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -177,6 +177,17 @@ public class LocationManager { */ 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 mListeners = new HashMap(); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2267372bda5a1..5e198a2f1639e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -24,6 +24,7 @@ + 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 0000000000000000000000000000000000000000..657a612fc6ba5b379a98891893ce79f1c03b030b GIT binary patch literal 1222 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSkfJR9T^xl_H+M9WCijWi-X*q z7}lMWc?skwBzpw;GB8xBF)%c=FfjZA3N^f7U???UV0e|lz+eS5K)hhiu0R{01Y44~ zy9>jA5L~c#`DCC7XMsm#F#`j)5C}6~x?A^$fq}Wm)5S3);_%fOr@LcZMcVhz3O|$W z(V}4IA<)#NreW0eze&4u$JK`DkB!of0Y`i%_0Ikw-m!q2St;_zfw>(WeF-8lKa(c4 za%IV=PM-Pl{88JhwR^sO`SNy)8+TXnt1D}Z>%Xqv^?c5|LoL2%4+ovCb=I8w=WqJ$ zBey+xL=*HcFo*pxvb-ug<^Xe|6^u-=4HQr!XyJrq2g2B)ypJU>JO(aqUKi ztqo^Ficj=Tczxh$%6#6v;n(|_bD|yA?heX$$8~`5LEeLNABtr^sCG8#Uj5Ar^;fQ6wc2d$^97-Q)4!&E{T5-o`dmo;;wVX0wYMAC-!S+w^s_6AH*7vo z^n!`Kd28~4tp}t&C>3bce35*2B&1vNitE?6H~v&t(#mfC$0`sEZ=9r4%V+CDSE1zS#6J=0e;=uVvN|Jn8K&7>T)d5(AG z4ZXg;ELnbx`6^R!ywx)2#MKA$m;ajaYu~TEzj}YI`{l7`gGzw=mHUhC$K1czx{&L| zRE94ZR(4lrU+GWED?Slk7It^~4VPNIXMSnZ+++in?r*YO@NeC^6}vAP7r$zMSmS$_ zLt*cQ>&frdUVD}o@IPRGX!(+zlMH#7BhG*SS5RYh?CMG3#Qkrst!=7WarDy64f?sI zImZ8{E{tTm_}k+|v7))N-0E!iIW@dS{M(MrZ`q^#WRGvryUixY6+GXu*)8DAn4S^D ze4t#>yutRsMZSiHvS{y=O%o0N*xUV1Z2b76l6LDU~%yA>#wX| z9nO3n>@9oQZ{6vKi>r%#7c2{{yW(RX`SW5@W<+DzLA9G@nit~wJND=<$eg<2dFG)* zy$5U$FdwK}7J4Y};FYVopV|#f-=trNHSzpWIqSi9Cf|}cwmM%P>DuVDV~^HuUHxn4 zw@W`(U&!}%4{rRT{41nRBh8&rhgmRU!O0gb`@|Z|ZofXBIk)b1`f;-tOFZX^AKr6r zhWqBH)@o5l@Ann@pIH69{EYq8Wk)Yu`FH8J@SGc}4?Ut*I{~w#Xo+h?iE~kEVo7Fx zo&k%&R6z<-5=&C8l0m9c8H@}Jjdcyob&U-}42`W!Ev$^p zfLtpBgFBzCLD?RmAvZrIGp!Q0hPCUO8-N;QKsE&Dr!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YPV z+ueoXKL{?^yL>WGgtNdSvY3H^TL^?1FWs&C12q1Yr;B5V$ML;W&wGaiiX7jcfA5UM zV~#+97puY+);qR5&?*Su!c(sL?x_7DU)2>ommlQ0beR2R*F6;Gc){+M^88kVgBrJ*sfRRPsk*u z57r0FADDk&t6;KWE|Waz{-CU6nc0s;j=v5}T_9I^PrY$rt=OwcPxwCD?GRbUFoR*& z#ao+d^N+slIyhP3l>dRx4^}U0oPHquLAUaL!8h+2-XE3{Tbg#}mtFyb&(Hro+}s5` zDckIsufHw6Tsn(;-3QJ^%f#6ayl(i>%E-?OFD>PoZ-^*Ajy<$c(-DORGEKc=!3P5ulaX( zEXat--*Mx9qiI&(>m?m36G9fNZ+cj5`{kF@!FwDARc^Bl9p5D^I^(!xTR+!{%}Nh0 z>1%k;U7B3`&;9?RC;U_EUiWG-^YR0uUbMtDqQtoi z#dYOC1*#wgDTyViR>>e$sSHL2hQ_)E=DNm)A%@0QrWRI4Wa>QWe}Xi&D$;i?WLqoP*6??Ag=gTe~DWM4fp#f2R literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fd8ad641798f4fe5b01ccd76e546643c9ea53def GIT binary patch literal 1638 zcmZ`(eLNF*82@e9uoh>yyd+&{xY*0tG;f(PQz+YHlDthV%N%p$SW`z{>RhCvd7tEB z-j=QxHR37rY1~P4?~yFt3ds@yRSO{O?e1qxQ2>D5(21XAbDXrp(5sy-T}S%AYi|bI>-LCzW4Nl-Xz#f1Al3kA3MnZFIh-*wFeP3f!ft9x!`tQC> z3$mtI5C(OeZ4Xw+AR$^I+Jlj!4wY^p>ZTf2rnkre_~yU;vr0>!N<*s>Te!dsZl*4Q z5ajpL_0~N3;~lb9nb{kLNod-UNZPE#;B}r)3Qf)n_QvP`7<`?BKNBQZQlPzA=q6ncW)a5ezfMb(;YJ+^Qsl03|~Gp@$s>Wxy%@WA+_p72npeySHw{j?>Rc zXb{6KknAfb{|xj=AyDFas51lKJddXvVKe49W z`OsDSHALP??DN9yY`tGzKSo&G=aNT8yj-Qfpb854MWZa^rDAce<@WfkD~)QXesGIs zd`0@WdsOrqA?;vAer$h7~hYCN8^-&{L-Yyu&&74;vL*PwaNM5ZVm7L)34Ki zv3Ig_muHD}28&rVq?$rH(h|Gw`(eD~v(fzS@*R)|MQ%8A_)u~j7><)&&kd3x{s}Sh ztP4!FCRnyjWlVWaP4t^IugBA>|;YKFS%R<7R}WiQPy@> zIUCnQhyzv*sJF4J8@uOs{dVKxNRQeQg$7wfbzI7v(~BEiagZIAkw)9JgOI7-w*Qo% z%&{R_sa&1hW!f_BjZW@}VhIy>d$cDPq@UOrB1yZA7&~DBkXO$O``sh_MKPDq;=-7c($XMze zxx)eBtd_ceD%k-!YC1Lu{g6dP*T68^TSot@b11Vm2NSiXrO=msG6GqBOt}xSOA@|nmhdd!+F#a2_>AwVK%D1D?lna{Yp7m?Y8Hy@V3=qGYelCUb`%%pVJ%@>9%Dm?w~Uj$E-6IREspb z2?Hcf^g6$_X!X_;8y!?gHRq+9aLW>uvFImHH8do11_i(T)LB&h_Jn}VotKARM|sbT{h28-W=A@0Fr8CX2Y z!I6X`s3-}88Ju_8e9QkCPVu5R(Wn34P%k+rR2fV+FVK0>{G{-BHgJ#PMX=4iIPvTx aUVKb~xd)vv{-F7SN(X43eiR8g?Cie~y3VNp literal 0 HcmV?d00001 diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 0073e60b45ef8..33a85c3a8ed6a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -418,6 +418,9 @@ Location set by GPS + + Location requests active + Clear all notifications. 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 3f8043d3f5260..91ddf0feee4bc 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,35 +26,38 @@ import android.content.IntentFilter; 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 boolean mAreActiveLocationRequests; + private boolean mIsAirplaneMode; - private ArrayList mChangeCallbacks = - new ArrayList(); private ArrayList mSettingsChangeCallbacks = new ArrayList(); - /** - * 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). */ @@ -74,13 +75,15 @@ public class LocationController extends BroadcastReceiver { 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 @@ public class LocationController extends BroadcastReceiver { } } }); - } - /** - * 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 @@ public class LocationController extends BroadcastReceiver { return isGpsEnabled || isNetworkEnabled; } + /** + * Returns true if there currently exist active high power location requests. + */ + private boolean areActiveHighPowerLocationRequests() { + List 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 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 cde84dc0c3e39..61752684e1b29 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -552,8 +552,14 @@ public class LocationManagerService extends ILocationManager.Stub { 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); + } } /**