Intruder alert! (First try at the immersive-mode alert bar.)

When a FLAG_HIGH_PRIORITY notification is posted and the
foreground activity is immersive, this window will be shown
to the user. It disappears after a while (currently 10s,
which is far too long to be usable but is very handy for
testing) and can be dismissed by a tap.

Artwork is extremely rough; please ignore the aesthetics.

Still TODO:
  - sticky alerts for ongoing priority notifications
  - tap to launch PendingIntent associated with the
    notification

Change-Id: Ief4a98b84cc836d33359bd7d65de9909f5186317
diff --git a/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png b/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png
new file mode 100644
index 0000000..7000eee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/alert_bar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/alert_bar_background.9.png b/packages/SystemUI/res/drawable-mdpi/alert_bar_background.9.png
new file mode 100644
index 0000000..258de13
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/alert_bar_background.9.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/intruder_alert.xml b/packages/SystemUI/res/layout/intruder_alert.xml
new file mode 100644
index 0000000..58dc333
--- /dev/null
+++ b/packages/SystemUI/res/layout/intruder_alert.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<!--    android:background="@drawable/status_bar_closed_default_background" -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:background="@drawable/alert_bar_background"
+    android:orientation="horizontal"
+    android:focusable="true"
+    android:descendantFocusability="afterDescendants"
+    >
+        
+    <LinearLayout 
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingLeft="6dip"
+        android:animationCache="false"
+        android:orientation="horizontal"
+        >
+
+        <ImageView
+            android:id="@+id/alertIcon"
+            android:layout_width="25dip"
+            android:layout_height="25dip"
+            android:layout_marginRight="8dip"
+            />
+        <TextView
+            android:id="@+id/alertText"
+            android:textAppearance="@*android:style/TextAppearance.StatusBar.EventContent"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            />
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 5d16e93..3c14fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -57,6 +57,7 @@
 import android.view.WindowManagerImpl;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.ScrollView;
@@ -84,6 +85,11 @@
 
     private static final int MSG_ANIMATE = 1000;
     private static final int MSG_ANIMATE_REVEAL = 1001;
+    private static final int MSG_SHOW_INTRUDER = 1002;
+    private static final int MSG_HIDE_INTRUDER = 1003;
+
+    // will likely move to a resource or other tunable param at some point
+    private static final int INTRUDER_ALERT_DECAY_MS = 10000;
 
     private class ExpandedDialog extends Dialog {
         ExpandedDialog(Context context) {
@@ -180,6 +186,9 @@
     // for disabling the status bar
     int mDisabled = 0;
 
+    // for immersive activities
+    private View mIntruderAlertView;
+
     /**
      * Construct the service, add the status bar view to the window manager
      */
@@ -208,6 +217,17 @@
         ExpandedView expanded = (ExpandedView)View.inflate(context,
                 R.layout.status_bar_expanded, null);
         expanded.mService = this;
+
+        mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
+        mIntruderAlertView.setVisibility(View.GONE);
+        mIntruderAlertView.setClickable(true);
+        mIntruderAlertView.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Slog.d(TAG, "Intruder Alert clicked!");
+                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+            }
+        });
+
         StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
         sb.mService = this;
 
@@ -286,6 +306,23 @@
         // TODO lp.windowAnimations = R.style.Animation_StatusBar;
 
         WindowManagerImpl.getDefault().addView(view, lp);
+
+        lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                mHeight,
+                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                PixelFormat.TRANSLUCENT);
+        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        lp.y += mHeight * 1.5; // for now
+        lp.setTitle("IntruderAlert");
+        lp.windowAnimations = android.R.style.Animation_Dialog;
+
+        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
     }
 
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
@@ -310,7 +347,8 @@
     }
 
     public void addNotification(IBinder key, StatusBarNotification notification) {
-        addNotificationViews(key, notification);
+        StatusBarIconView iconView = addNotificationViews(key, notification);
+        if (iconView == null) return;
 
         boolean immersive = false;
         try {
@@ -322,21 +360,22 @@
             if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
                 Slog.d(TAG, "Presenting high-priority notification in immersive activity");
                 // @@@ special new transient ticker mode
-                /*
-                // 1. Populate mAlertBarView
+                // 1. Populate mIntruderAlertView
 
-                ImageView alertIcon = (ImageView) mAlertBarView.findViewById(R.id.alertIcon);
-                TextView alertText = (TextView) mAlertBarView.findViewById(R.id.alertText);
+                ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
+                TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
                 alertIcon.setImageDrawable(StatusBarIconView.getIcon(
                     alertIcon.getContext(), 
                     iconView.getStatusBarIcon()));
                 alertText.setText(notification.notification.tickerText);
 
-                // 2. Animate mAlertBarView in
-                mAlertBarView.setVisibility(View.VISIBLE);
+                // 2. Animate mIntruderAlertView in
+                mHandler.removeMessages(MSG_HIDE_INTRUDER);
+                mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
+                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
 
                 // 3. Set alarm to age the notification off (TODO)
-                */
+                
             }
         } else if (notification.notification.fullScreenIntent != null) {
             // not immersive & a full-screen alert should be shown
@@ -502,7 +541,7 @@
         return new View[] { row, content, expanded };
     }
 
-    void addNotificationViews(IBinder key, StatusBarNotification notification) {
+    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
         NotificationData list;
         ViewGroup parent;
         final boolean isOngoing = notification.isOngoing();
@@ -518,7 +557,7 @@
         if (views == null) {
             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
                     + notification);
-            return;
+            return null;
         }
         final View row = views[0];
         final View content = views[1];
@@ -530,7 +569,7 @@
                     notification.notification.iconLevel, notification.notification.number);
         if (!iconView.set(ic)) {
             handleNotificationError(key, notification, "Coulding create icon: " + ic);
-            return;
+            return null;
         }
         // Add the expanded view.
         final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
@@ -539,6 +578,8 @@
         final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
         mNotificationIcons.addView(iconView, iconIndex,
                 new LinearLayout.LayoutParams(mIconWidth, mHeight));
+
+        return iconView;
     }
 
     StatusBarNotification removeNotificationViews(IBinder key) {
@@ -628,6 +669,12 @@
                 case MSG_ANIMATE_REVEAL:
                     doRevealAnimation();
                     break;
+                case MSG_SHOW_INTRUDER:
+                    setIntruderAlertVisibility(true);
+                    break;
+                case MSG_HIDE_INTRUDER:
+                    setIntruderAlertVisibility(false);
+                    break;
             }
         }
     }
@@ -1440,6 +1487,10 @@
         }
     };
 
+    private void setIntruderAlertVisibility(boolean vis) {
+        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
+    }
+
     /**
      * Reload some of our resources when the configuration changes.
      *