Merge "resolved conflicts for merge of 31796297 to master"
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4d5238c..2420b84 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -33,6 +33,7 @@
     void enqueueToast(String pkg, ITransientNotification callback, int duration);
     void cancelToast(String pkg, ITransientNotification callback);
     void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
+    void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority, in Notification notification, inout int[] idReceived);
     void cancelNotificationWithTag(String pkg, String tag, int id);
 }
 
diff --git a/core/java/com/android/internal/statusbar/StatusBarNotification.java b/core/java/com/android/internal/statusbar/StatusBarNotification.java
index cb791be..c03ff1a 100644
--- a/core/java/com/android/internal/statusbar/StatusBarNotification.java
+++ b/core/java/com/android/internal/statusbar/StatusBarNotification.java
@@ -63,8 +63,7 @@
         this.initialPid = initialPid;
         this.notification = notification;
 
-        this.priority = ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0)
-            ? PRIORITY_ONGOING : PRIORITY_NORMAL;
+        this.priority = PRIORITY_NORMAL;
     }
 
     public StatusBarNotification(Parcel in) {
diff --git a/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml b/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
index f29259a..22c8002 100644
--- a/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS-xlarge/strings.xml
@@ -13,4 +13,10 @@
     <string name="status_bar_settings_signal_meter_disconnected" msgid="4866302415753953027">"Sin conexión a Internet"</string>
     <!-- XL xlarge -->
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="3832182580451976589">"Wi-Fi conectado"</string>
+
+    <!-- manually translated -->
+    <string name="gps_notification_searching_text">Buscando señal de GPS</string>
+
+    <!-- manually translated -->
+    <string name="gps_notification_found_text">Ubicación establecida por el GPS</string>
 </resources>
diff --git a/packages/SystemUI/res/values-xlarge/strings.xml b/packages/SystemUI/res/values-xlarge/strings.xml
index f7b642d..dfd5851 100644
--- a/packages/SystemUI/res/values-xlarge/strings.xml
+++ b/packages/SystemUI/res/values-xlarge/strings.xml
@@ -38,4 +38,9 @@
     <!-- Separator for PLMN and SPN in network name. -->
     <string name="status_bar_network_name_separator" translatable="false">" – "</string>
 
+    <!-- Notification text: when GPS is getting a fix [CHAR LIMIT=50] -->
+    <string name="gps_notification_searching_text">Searching for GPS</string>
+
+    <!-- Notification text: when GPS has found a fix [CHAR LIMIT=50] -->
+    <string name="gps_notification_found_text">Location set by GPS</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
new file mode 100644
index 0000000..76c05d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 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.systemui.statusbar.policy;
+
+import java.util.ArrayList;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.location.LocationManager;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+// private NM API
+import android.app.INotificationManager;
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.R;
+
+public class LocationController extends BroadcastReceiver {
+    private static final String TAG = "StatusBar.LocationController";
+
+    private static final int GPS_NOTIFICATION_ID = 374203-122084;
+
+    private Context mContext;
+
+    private INotificationManager mNotificationService;
+
+    public LocationController(Context context) {
+        mContext = context;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+        filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+        context.registerReceiver(this, filter);
+
+        NotificationManager nm = (NotificationManager)context.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        mNotificationService = nm.getService();
+    }
+
+    @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) {
+                Notification n = new Notification.Builder(mContext)
+                    .setSmallIcon(iconId)
+                    .setContentTitle(mContext.getText(textResId))
+                    .setOngoing(true)
+                    .getNotification();
+
+                // Notification.Builder will helpfully fill these out for you no matter what you do
+                n.tickerView = null;
+                n.tickerText = null;
+
+                int[] idOut = new int[1];
+                mNotificationService.enqueueNotificationWithTagPriority(
+                        mContext.getPackageName(),
+                        null, 
+                        GPS_NOTIFICATION_ID, 
+                        StatusBarNotification.PRIORITY_SYSTEM, // !!!1!one!!!
+                        n,
+                        idOut);
+            } else {
+                mNotificationService.cancelNotification(
+                        mContext.getPackageName(),
+                        GPS_NOTIFICATION_ID);
+            }
+        } catch (android.os.RemoteException ex) {
+            // well, it was worth a shot
+        }
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 4bac07f..7a13fde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -65,6 +65,7 @@
 import com.android.systemui.statusbar.*;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.recent.RecentApplicationsActivity;
 
@@ -135,6 +136,7 @@
     HeightReceiver mHeightReceiver;
     BatteryController mBatteryController;
     BluetoothController mBluetoothController;
+    LocationController mLocationController;
     NetworkController mNetworkController;
 
     View mBarContents;
@@ -359,6 +361,8 @@
         mTicker = new TabletTicker(this);
 
         // The icons
+        mLocationController = new LocationController(mContext); // will post a notification
+
         mBatteryController = new BatteryController(mContext);
         mBatteryController.addIconView((ImageView)sb.findViewById(R.id.battery));
         mBluetoothController = new BluetoothController(mContext);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 0490190..47dce41 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -156,10 +156,11 @@
         final int id;
         final int uid;
         final int initialPid;
+        final int priority;
         final Notification notification;
         IBinder statusBarKey;
 
-        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+        NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int priority,
                 Notification notification)
         {
             this.pkg = pkg;
@@ -167,6 +168,7 @@
             this.id = id;
             this.uid = uid;
             this.initialPid = initialPid;
+            this.priority = priority;
             this.notification = notification;
         }
 
@@ -194,7 +196,9 @@
                 + Integer.toHexString(System.identityHashCode(this))
                 + " pkg=" + pkg
                 + " id=" + Integer.toHexString(id)
-                + " tag=" + tag + "}";
+                + " tag=" + tag 
+                + " pri=" + priority 
+                + "}";
         }
     }
 
@@ -649,11 +653,27 @@
                 tag, id, notification, idOut);
     }
 
+    public void enqueueNotificationWithTagPriority(String pkg, String tag, int id, int priority,
+            Notification notification, int[] idOut)
+    {
+        enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
+                tag, id, priority, notification, idOut);
+    }
+
     // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
     // uid/pid of another application)
     public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
             String tag, int id, Notification notification, int[] idOut)
     {
+        enqueueNotificationInternal(pkg, callingUid, callingPid, tag, id, 
+                ((notification.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                    ? StatusBarNotification.PRIORITY_ONGOING
+                    : StatusBarNotification.PRIORITY_NORMAL,
+                notification, idOut);
+    }
+    public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
+            String tag, int id, int priority, Notification notification, int[] idOut)
+    {
         checkIncomingCall(pkg);
 
         // Limit the number of notifications that any given package except the android
@@ -695,8 +715,10 @@
         }
 
         synchronized (mNotificationList) {
-            NotificationRecord r = new NotificationRecord(pkg, tag, id,
-                    callingUid, callingPid, notification);
+            NotificationRecord r = new NotificationRecord(pkg, tag, id, 
+                    callingUid, callingPid, 
+                    priority,
+                    notification);
             NotificationRecord old = null;
 
             int index = indexOfNotificationLocked(pkg, tag, id);
@@ -722,6 +744,8 @@
             if (notification.icon != 0) {
                 StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
                         r.uid, r.initialPid, notification);
+                n.priority = r.priority;
+
                 if (old != null && old.statusBarKey != null) {
                     r.statusBarKey = old.statusBarKey;
                     long identity = Binder.clearCallingIdentity();
@@ -743,6 +767,7 @@
                 }
                 sendAccessibilityEvent(notification, pkg);
             } else {
+                Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
                 if (old != null && old.statusBarKey != null) {
                     long identity = Binder.clearCallingIdentity();
                     try {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 90c2a1a..f463a19 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -35,6 +35,10 @@
 import android.widget.ProgressBar;
 import android.os.PowerManager;
 
+// private NM API
+import android.app.INotificationManager;
+import com.android.internal.statusbar.StatusBarNotification;
+
 public class NotificationTestList extends TestActivity
 {
     private final static String TAG = "NotificationTestList";
@@ -205,6 +209,15 @@
             }
         },
 
+        new Test("Null Icon #1 (when=now)") {
+            public void run() {
+                Notification n = new Notification(0, null, System.currentTimeMillis());
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is the same notification!!!", makeIntent());
+                mNM.notify(1, n);
+            }
+        },
+
         new Test("Bad resource #1 (when=create)") {
             public void run() {
                 Notification n = new Notification(R.drawable.icon2,
@@ -752,6 +765,30 @@
             }
         },
 
+        new Test("System priority notification") {
+            public void run() {
+                Notification n = new Notification.Builder(NotificationTestList.this)
+                    .setSmallIcon(R.drawable.notification1)
+                    .setContentTitle("System priority")
+                    .setContentText("This should appear before all others")
+                    .getNotification();
+
+                int[] idOut = new int[1];
+                try {
+                    INotificationManager directLine = mNM.getService();
+                    directLine.enqueueNotificationWithTagPriority(
+                            getPackageName(),
+                            null, 
+                            1, 
+                            StatusBarNotification.PRIORITY_SYSTEM,
+                            n,
+                            idOut);
+                } catch (android.os.RemoteException ex) {
+                    // oh well
+                }
+            }
+        },
+
         new Test("Crash") {
             public void run()
             {