Tablet ticker.

Change-Id: Ia3db5cc29eac1703123de3e1c6dc7c22e7d024eb
diff --git a/api/current.xml b/api/current.xml
index c581f34..374e26f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32558,6 +32558,27 @@
  visibility="public"
 >
 </field>
+<field name="tickerIcons"
+ type="android.graphics.Bitmap[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="tickerSubtitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="tickerText"
  type="java.lang.CharSequence"
  transient="false"
@@ -32568,6 +32589,16 @@
  visibility="public"
 >
 </field>
+<field name="tickerTitle"
+ type="java.lang.CharSequence"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="vibrate"
  type="long[]"
  transient="false"
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index da000e5..e602518 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Parcel;
@@ -124,11 +125,47 @@
 
     /**
      * Text to scroll across the screen when this item is added to
-     * the status bar.
+     * the status bar on large and smaller devices.
+     *
+     * <p>This field is provided separately from the other ticker fields
+     * both for compatibility and to allow an application to choose different
+     * text for when the text scrolls in and when it is displayed all at once
+     * in conjunction with one or more icons.
+     *
+     * @see #tickerTitle
+     * @see #tickerSubtitle
+     * @see #tickerIcons
      */
     public CharSequence tickerText;
 
     /**
+     * The title line for the ticker over a the fat status bar on xlarge devices.
+     *
+     * @see #tickerText
+     * @see #tickerSubtitle
+     * @see #tickerIcons
+     */
+    public CharSequence tickerTitle;
+
+    /**
+     * The subtitle line for the ticker over a the fat status bar on xlarge devices.
+     *
+     * @see #tickerText
+     * @see #tickerTitle
+     * @see #tickerIcons
+     */
+    public CharSequence tickerSubtitle;
+
+    /**
+     * The icons to show to the left of the other ticker fields.
+     *
+     * @see #tickerText
+     * @see #tickerTitle
+     * @see #tickerSubtitle
+     */
+    public Bitmap[] tickerIcons;
+
+    /**
      * The view that will represent this notification in the expanded status bar.
      */
     public RemoteViews contentView;
@@ -347,6 +384,21 @@
             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         }
         if (parcel.readInt() != 0) {
+            tickerTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
+        if (parcel.readInt() != 0) {
+            tickerSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        }
+        final int tickerIconCount = parcel.readInt();
+        if (tickerIconCount >= 0) {
+            tickerIcons = new Bitmap[tickerIconCount];
+            for (int i=0; i<tickerIconCount; i++) {
+                if (parcel.readInt() != 0) {
+                    tickerIcons[i] = Bitmap.CREATOR.createFromParcel(parcel);
+                }
+            }
+        }
+        if (parcel.readInt() != 0) {
             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
         }
         defaults = parcel.readInt();
@@ -382,6 +434,19 @@
         if (this.tickerText != null) {
             that.tickerText = this.tickerText.toString();
         }
+        if (this.tickerTitle != null) {
+            that.tickerTitle = this.tickerTitle.toString();
+        }
+        if (this.tickerSubtitle != null) {
+            that.tickerSubtitle = this.tickerSubtitle.toString();
+        }
+        if (this.tickerIcons != null) {
+            final int N = this.tickerIcons.length;
+            that.tickerIcons = new Bitmap[N];
+            for (int i=0; i<N; i++) {
+                that.tickerIcons[i] = Bitmap.createBitmap(this.tickerIcons[i]);
+            }
+        }
         if (this.contentView != null) {
             that.contentView = this.contentView.clone();
         }
@@ -438,6 +503,32 @@
         } else {
             parcel.writeInt(0);
         }
+        if (tickerTitle != null) {
+            parcel.writeInt(1);
+            TextUtils.writeToParcel(tickerTitle, parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
+        if (tickerSubtitle != null) {
+            parcel.writeInt(1);
+            TextUtils.writeToParcel(tickerSubtitle, parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
+        if (tickerIcons != null) {
+            final int N = tickerIcons.length;
+            parcel.writeInt(N);
+            for (int i=0; i<N; i++) {
+                if (tickerIcons[i] != null) {
+                    parcel.writeInt(1);
+                    tickerIcons[i].writeToParcel(parcel, flags);
+                } else {
+                    parcel.writeInt(0);
+                }
+            }
+        } else {
+            parcel.writeInt(-1);
+        }
         if (contentView != null) {
             parcel.writeInt(1);
             contentView.writeToParcel(parcel, 0);
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index e0b34cc..ba7029d9 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -1,21 +1,18 @@
 <?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.
-*/
+ * Copyright (C) 2010 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" -->
@@ -65,41 +62,16 @@
 
         </com.android.systemui.statusbar.tablet.NotificationIconArea>
 
-        <LinearLayout android:id="@+id/ticker"
-            android:layout_width="300dip"
+        <FrameLayout
+            android:id="@+id/ticker"
+            android:layout_width="wrap_content"
             android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@+id/systemInfo"
             android:paddingLeft="6dip"
-            android:animationCache="false"
-            android:layout_alignLeft="@id/notificationIcons"
-            android:layout_alignTop="@id/notificationIcons"
-            android:orientation="horizontal"
-            android:visibility="gone"
-            >
-            <ImageView android:id="@+id/tickerIcon"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_marginRight="8dip"
-                />
-            <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
-                android:layout_width="0dip"
-                android:layout_weight="1"
-                android:layout_height="wrap_content"
-                android:paddingTop="2dip"
-                android:paddingRight="10dip">
-                <TextView
-                    android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:singleLine="true"
-                    />
-                <TextView
-                    android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:singleLine="true"
-                    />
-            </com.android.systemui.statusbar.TickerView>
-        </LinearLayout>
+            android:gravity="center_vertical"
+            android:animateLayoutChanges="true"
+            />
 
         <include layout="@layout/status_bar_center"
             android:layout_width="256dip"
diff --git a/packages/SystemUI/res/layout-xlarge/ticker.xml b/packages/SystemUI/res/layout-xlarge/ticker.xml
new file mode 100644
index 0000000..c8d855f
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="#ff000000"
+    >
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingLeft="12dp"
+        >
+
+        <TextView android:id="@+id/title"
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textStyle="bold"
+            android:maxLines="1"
+            />
+        <TextView android:id="@+id/subtitle"
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker_compat.xml b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
new file mode 100644
index 0000000..79c7543
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    android:background="#ff000000"
+    >
+
+    <TextView android:id="@+id/text"
+        android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="12dp"
+        android:gravity="center_vertical"
+        android:maxLines="2"
+        />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/ticker_icon.xml b/packages/SystemUI/res/layout-xlarge/ticker_icon.xml
new file mode 100644
index 0000000..9efa987
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_icon.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 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.
+-->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="30dp"
+    android:layout_height="30dp"
+    android:layout_gravity="center"
+    android:layout_marginLeft="6dp"
+    />
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index 5ba1fab..c4819a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManagerNative;
 import android.app.PendingIntent;
+import android.app.Notification;
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.content.Intent;
@@ -28,6 +29,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
 import android.view.Gravity;
@@ -36,6 +38,7 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
@@ -81,8 +84,8 @@
 
     NotificationIconArea.IconLayout mIconLayout;
 
-    KickerController mKicker;
-    View mKickerView;
+    TabletTicker mTicker;
+    View mTickerView;
     boolean mTicking;
     boolean mExpandedVisible;
 
@@ -163,7 +166,7 @@
         // where the icons go
         mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
 
-        mKicker = new KickerController((Context)this, mStatusBarView);
+        mTicker = new TabletTicker((Context)this, (FrameLayout)sb.findViewById(R.id.ticker));
 
         // System info (center)
         mBatteryMeter = (ImageView) sb.findViewById(R.id.battery);
@@ -274,7 +277,7 @@
             } catch (PendingIntent.CanceledException e) {
             }
         } else {
-            // tick()
+            tick(notification);
         }
     }
 
@@ -344,7 +347,7 @@
             removeNotificationViews(key);
             addNotificationViews(key, notification);
         }
-        // TODO: kicker; immersive mode
+        // TODO: ticker; immersive mode
     }
 
     public void removeNotification(IBinder key) {
@@ -368,7 +371,7 @@
             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
                 if (mTicking) {
-                    mKicker.halt();
+                    mTicker.halt();
                 } else {
                     mNotificationIconArea.setVisibility(View.INVISIBLE);
                 }
@@ -381,7 +384,7 @@
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
                 Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
-                mKicker.halt();
+                mTicker.halt();
             }
         }
         */
@@ -405,7 +408,7 @@
                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
                 if (mTicking) {
                     mNotificationIconArea.setVisibility(View.INVISIBLE);
-                    mKicker.halt();
+                    mTicker.halt();
                 } else {
                     mNotificationIconArea.setVisibility(View.INVISIBLE);
                 }
@@ -417,65 +420,31 @@
             }
         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
             if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
-                mKicker.halt();
+                mTicker.halt();
             }
         }
         */
     }
 
+    private boolean hasTicker(Notification n) {
+        return !TextUtils.isEmpty(n.tickerText)
+                || !TextUtils.isEmpty(n.tickerTitle)
+                || !TextUtils.isEmpty(n.tickerSubtitle);
+    }
+
     private void tick(StatusBarNotification n) {
         // Show the ticker if one is requested. Also don't do this
         // until status bar window is attached to the window manager,
         // because...  well, what's the point otherwise?  And trying to
         // run a ticker without being attached will crash!
-        if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+        if (hasTicker(n.notification) && mStatusBarView.getWindowToken() != null) {
             if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
                             | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
-                mKicker.addEntry(n);
+                mTicker.add(n);
             }
         }
     }
 
-    private class KickerController {
-        View mView;
-        ImageView mKickerIcon;
-        TextSwitcher mKickerText;
-
-        public KickerController(Context context, View sb) {
-            mView = sb.findViewById(R.id.ticker);
-            mKickerIcon = (ImageView) mView.findViewById(R.id.tickerIcon);
-            mKickerText = (TextSwitcher) mView.findViewById(R.id.tickerText);
-        }
-
-        public void halt() {
-            tickerHalting();
-        }
-
-        public void addEntry(StatusBarNotification n) {
-            mKickerIcon.setImageResource(n.notification.icon);
-            mKickerText.setCurrentText(n.notification.tickerText);
-            tickerStarting();
-        }
-
-        public void tickerStarting() {
-            mTicking = true;
-            mIconLayout.setVisibility(View.GONE);
-            mKickerView.setVisibility(View.VISIBLE);
-        }
-
-        public void tickerDone() {
-            mIconLayout.setVisibility(View.VISIBLE);
-            mKickerView.setVisibility(View.GONE);
-            mTicking = false;
-        }
-
-        public void tickerHalting() {
-            mIconLayout.setVisibility(View.VISIBLE);
-            mKickerView.setVisibility(View.GONE);
-            mTicking = false;
-        }
-    }
-
     public void animateExpand() {
         mHandler.removeMessages(H.MSG_OPEN_NOTIFICATION_PANEL);
         mHandler.sendEmptyMessage(H.MSG_OPEN_NOTIFICATION_PANEL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
new file mode 100644
index 0000000..7ac7919
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 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.tablet;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.statusbar.StatusBarNotification;
+
+import com.android.systemui.R;
+
+import java.util.Arrays;
+
+public class TabletTicker extends Handler {
+    private static final String TAG = "StatusBar.TabletTicker";
+
+    private static final int MSG_ADVANCE = 1;
+
+    private static final int ADVANCE_DELAY = 5000; // 5 seconds
+
+    private Context mContext;
+    private FrameLayout mParent;
+
+    private StatusBarNotification mCurrentNotification;
+    private View mCurrentView;
+
+    private StatusBarNotification[] mQueue;
+    private int mQueuePos;
+
+    public TabletTicker(Context context, FrameLayout parent) {
+        mContext = context;
+        mParent = parent;
+
+        // TODO: Make this a configuration value.
+        // 3 is enough to let us see most cases, but not get so far behind that it's annoying.
+        int mQueuePos = 0;
+        mQueue = new StatusBarNotification[3];
+    }
+
+    public void add(StatusBarNotification notification) {
+        if (false) {
+            Slog.d(TAG, "add mCurrentNotification=" + mCurrentNotification
+                    + " mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
+        }
+        mQueue[mQueuePos] = notification;
+
+        // If nothing is running now, start the next one
+        if (mCurrentNotification == null) {
+            sendEmptyMessage(MSG_ADVANCE);
+        }
+
+        if (mQueuePos < mQueue.length - 1) {
+            mQueuePos++;
+        }
+    }
+
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case MSG_ADVANCE:
+                advance();
+                break;
+        }
+    }
+
+    private void advance() {
+        // Out with the old...
+        if (mCurrentView != null) {
+            mParent.removeView(mCurrentView);
+            mCurrentView = null;
+            mCurrentNotification = null;
+        }
+
+        // In with the new...
+        final StatusBarNotification next = dequeue();
+        if (next != null) {
+            mCurrentNotification = next;
+            mCurrentView = makeTickerView(next);
+            mParent.addView(mCurrentView);
+            sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+        }
+    }
+
+    private StatusBarNotification dequeue() {
+        StatusBarNotification notification = mQueue[0];
+        if (false) {
+            Slog.d(TAG, "dequeue mQueuePos=" + mQueuePos + " mQueue=" + Arrays.toString(mQueue));
+        }
+        final int N = mQueuePos;
+        for (int i=0; i<N; i++) {
+            mQueue[i] = mQueue[i+1];
+        }
+        mQueue[N] = null;
+        if (mQueuePos > 0) {
+            mQueuePos--;
+        }
+        return notification;
+    }
+
+    private View makeTickerView(StatusBarNotification notification) {
+        final Notification n = notification.notification;
+
+        LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        int layoutId;
+        ViewGroup group;
+        if (n.tickerTitle != null || n.tickerSubtitle != null) {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker, mParent, false);
+            if (n.tickerTitle != null) {
+                final TextView title = (TextView)group.findViewById(R.id.title);
+                title.setText(n.tickerTitle);
+            }
+            if (n.tickerSubtitle != null) {
+                final TextView subtitle = (TextView)group.findViewById(R.id.subtitle);
+                subtitle.setText(n.tickerSubtitle);
+            }
+        } else {
+            group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mParent, false);
+            TextView tv = (TextView)group.findViewById(R.id.text);
+            tv.setText(n.tickerText);
+        }
+
+        // No more than 2 icons.
+        if (n.tickerIcons != null) {
+            int N = n.tickerIcons.length;
+            if (N > 2) {
+                N = 2;
+            }
+            for (int i=N-1; i>= 0; i--) {
+                Bitmap b = n.tickerIcons[i];
+                if (b != null) {
+                    ImageView iv = (ImageView)inflater.inflate(R.layout.ticker_icon, group, false);
+                    iv.setImageBitmap(b);
+                    group.addView(iv, 0);
+                }
+            }
+        }
+
+        return group;
+    }
+}
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 16b3001..7b6e2d2 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -16,12 +16,14 @@
 
 package com.android.statusbartest;
 
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.ContentResolver;
 import android.content.Intent;
-import android.app.Notification;
-import android.app.NotificationManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.Environment;
 import android.os.Vibrator;
 import android.os.Handler;
@@ -77,6 +79,112 @@
             }
         },
 
+        new Test("Cancel #1") {
+            public void run()
+            {
+                mNM.cancel(1);
+            }
+        },
+
+        new Test("Ticker 1 line") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker 1 line & icon") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerIcons = new Bitmap[1];
+                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker 2 lines") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "tick tick tick\ntock tock",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerSubtitle = "and a subtitle";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title & subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title it is really really longggggg long long long long";
+                n.tickerSubtitle = "and a subtitle it is really really longggggg long long long long long long long long long long long long long long long long";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker text, title & subtitle") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, "not visible",
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                n.tickerSubtitle = "and a subtitle";
+                mNM.notify(1, n);
+            }
+        },
+
+        new Test("Ticker title, subtitle & 2 icons") {
+            public void run() {
+                Notification n = new Notification(R.drawable.icon1, null,
+                        mActivityCreateTime);
+                n.setLatestEventInfo(NotificationTestList.this, "Persistent #1",
+                            "This is a notification!!!", makeIntent());
+                n.tickerTitle = "This is a title";
+                n.tickerSubtitle = "and a subtitle";
+
+                n.tickerIcons = new Bitmap[2];
+                n.tickerIcons[0] = loadBitmap(R.drawable.icon3);
+                n.tickerIcons[1] = loadBitmap(R.drawable.app_gmail);
+
+                mNM.notify(1, n);
+                /*
+                n.tickerIcons[0].recycle();
+                n.tickerIcons[1].recycle();
+                */
+            }
+        },
+
         new Test("No view") {
             public void run() {
                 Notification n = new Notification(R.drawable.icon1, "No view",
@@ -479,7 +587,7 @@
 
         new Test("Persistent #3") {
             public void run() {
-                Notification n = new Notification(R.drawable.icon2, "tock tock tock",
+                Notification n = new Notification(R.drawable.icon2, "tock tock tock\nmooooo",
                         System.currentTimeMillis());
                 n.setLatestEventInfo(NotificationTestList.this, "Persistent #3",
                             "Notify me!!!", makeIntent());
@@ -701,5 +809,10 @@
                     time, label, "" + new java.util.Date(time), null));
 
     }
+
+    Bitmap loadBitmap(int resId) {
+        BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(resId);
+        return Bitmap.createBitmap(bd.getBitmap());
+    }
 }