diff --git a/api/current.xml b/api/current.xml
index c581f34b34aed..374e26f69a2df 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32558,6 +32558,27 @@
visibility="public"
>
+
+
+
+
+
+
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.
*/
@@ -346,6 +383,21 @@ public class Notification implements Parcelable
if (parcel.readInt() != 0) {
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
@@ -65,41 +62,16 @@
-
-
-
-
-
-
-
+ android:gravity="center_vertical"
+ android:animateLayoutChanges="true"
+ />
+
+
+
+
+
+
+
+
+
+
+
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 0000000000000..79c754330c350
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_compat.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
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 0000000000000..9efa98743d2c1
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/ticker_icon.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
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 5ba1fab41025f..c4819a4336870 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 @@ package com.android.systemui.statusbar.tablet;
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.Handler;
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.View;
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 @@ public class TabletStatusBarService extends StatusBarService {
NotificationIconArea.IconLayout mIconLayout;
- KickerController mKicker;
- View mKickerView;
+ TabletTicker mTicker;
+ View mTickerView;
boolean mTicking;
boolean mExpandedVisible;
@@ -163,7 +166,7 @@ public class TabletStatusBarService extends StatusBarService {
// 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 @@ public class TabletStatusBarService extends StatusBarService {
} catch (PendingIntent.CanceledException e) {
}
} else {
- // tick()
+ tick(notification);
}
}
@@ -344,7 +347,7 @@ public class TabletStatusBarService extends StatusBarService {
removeNotificationViews(key);
addNotificationViews(key, notification);
}
- // TODO: kicker; immersive mode
+ // TODO: ticker; immersive mode
}
public void removeNotification(IBinder key) {
@@ -368,7 +371,7 @@ public class TabletStatusBarService extends StatusBarService {
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 @@ public class TabletStatusBarService extends StatusBarService {
} 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 @@ public class TabletStatusBarService extends StatusBarService {
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 @@ public class TabletStatusBarService extends StatusBarService {
}
} 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 0000000000000..7ac7919c53c48
--- /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 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 16b3001df4411..7b6e2d285a9dc 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 @@ public class NotificationTestList extends TestActivity
}
},
+ 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 @@ public class NotificationTestList extends TestActivity
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 @@ public class NotificationTestList extends TestActivity
time, label, "" + new java.util.Date(time), null));
}
+
+ Bitmap loadBitmap(int resId) {
+ BitmapDrawable bd = (BitmapDrawable)getResources().getDrawable(resId);
+ return Bitmap.createBitmap(bd.getBitmap());
+ }
}