Update the look of the notifications. Includes adding a new Notification.Builder class.
Change-Id: I7c57b81c45defe77d6f3d22472e9cf8432bbed03
diff --git a/api/current.xml b/api/current.xml
index 3ccb4e9..495d11d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -32205,7 +32205,7 @@
type="android.app.Notification"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="icon" type="int">
@@ -32254,7 +32254,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -32524,6 +32524,16 @@
visibility="public"
>
</field>
+<field name="largeIcon"
+ type="android.graphics.Bitmap"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ledARGB"
type="int"
transient="false"
@@ -32574,27 +32584,6 @@
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"
@@ -32605,8 +32594,8 @@
visibility="public"
>
</field>
-<field name="tickerTitle"
- type="java.lang.CharSequence"
+<field name="tickerView"
+ type="android.widget.RemoteViews"
transient="false"
volatile="false"
static="false"
@@ -32637,6 +32626,321 @@
>
</field>
</class>
+<class name="Notification.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Notification.Builder"
+ type="android.app.Notification.Builder"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="getNotification"
+ return="android.app.Notification"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAutoCancel"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoCancel" type="boolean">
+</parameter>
+</method>
+<method name="setContent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
+<method name="setContentInfo"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="info" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setContentIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="setContentText"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setContentTitle"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="title" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setDefaults"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="defaults" type="int">
+</parameter>
+</method>
+<method name="setDeleteIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+</method>
+<method name="setFullScreenIntent"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="highPriority" type="boolean">
+</parameter>
+</method>
+<method name="setLargeIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="android.graphics.Bitmap">
+</parameter>
+</method>
+<method name="setLights"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="argb" type="int">
+</parameter>
+<parameter name="onMs" type="int">
+</parameter>
+<parameter name="offMs" type="int">
+</parameter>
+</method>
+<method name="setOngoing"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ongoing" type="boolean">
+</parameter>
+</method>
+<method name="setOnlyAlertOnce"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="onlyAlertOnce" type="boolean">
+</parameter>
+</method>
+<method name="setSmallIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="int">
+</parameter>
+</method>
+<method name="setSmallIcon"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="icon" type="int">
+</parameter>
+<parameter name="level" type="int">
+</parameter>
+</method>
+<method name="setSmallIconNumber"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="number" type="int">
+</parameter>
+</method>
+<method name="setSound"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sound" type="android.net.Uri">
+</parameter>
+<parameter name="streamType" type="int">
+</parameter>
+</method>
+<method name="setTicker"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tickerText" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setTicker"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tickerText" type="java.lang.CharSequence">
+</parameter>
+<parameter name="views" type="android.widget.RemoteViews">
+</parameter>
+</method>
+<method name="setVibrate"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pattern" type="long[]">
+</parameter>
+</method>
+<method name="setWhen"
+ return="android.app.Notification.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="when" type="long">
+</parameter>
+</method>
+</class>
<class name="NotificationManager"
extends="java.lang.Object"
abstract="false"
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e602518..09319b9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -29,6 +29,7 @@
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
+import android.util.Slog;
import android.widget.RemoteViews;
/**
@@ -41,6 +42,8 @@
*/
public class Notification implements Parcelable
{
+ private static final String TAG = "Notification";
+
/**
* Use all default values (where applicable).
*/
@@ -84,6 +87,15 @@
public int icon;
/**
+ * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
+ * leave it at its default value of 0.
+ *
+ * @see android.widget.ImageView#setImageLevel
+ * @see android.graphics.drawable#setLevel
+ */
+ public int iconLevel;
+
+ /**
* The number of events that this notification represents. For example, in a new mail
* notification, this could be the number of unread messages. This number is superimposed over
* the icon in the status bar. If the number is 0 or negative, it is not shown in the status
@@ -132,38 +144,15 @@
* 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
+ * @see #tickerView
*/
public CharSequence tickerText;
/**
- * The title line for the ticker over a the fat status bar on xlarge devices.
- *
- * @see #tickerText
- * @see #tickerSubtitle
- * @see #tickerIcons
+ * The view to show as the ticker in the status bar when the notification
+ * is posted.
*/
- 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;
+ public RemoteViews tickerView;
/**
* The view that will represent this notification in the expanded status bar.
@@ -171,13 +160,9 @@
public RemoteViews contentView;
/**
- * If the icon in the status bar is to have more than one level, you can set this. Otherwise,
- * leave it at its default value of 0.
- *
- * @see android.widget.ImageView#setImageLevel
- * @see android.graphics.drawable#setLevel
+ * The bitmap that may escape the bounds of the panel and bar.
*/
- public int iconLevel;
+ public Bitmap largeIcon;
/**
* The sound to play.
@@ -327,6 +312,7 @@
/**
* Constructs a Notification object with everything set to 0.
+ * You might want to consider using {@link Builder} instead.
*/
public Notification()
{
@@ -334,7 +320,6 @@
}
/**
- * @deprecated use {@link #Notification(int,CharSequence,long)} and {@link #setLatestEventInfo}.
* @hide
*/
public Notification(Context context, int icon, CharSequence tickerText, long when,
@@ -356,7 +341,10 @@
* activates.
* @param when The time to show in the time field. In the System.currentTimeMillis
* timebase.
+ *
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public Notification(int icon, CharSequence tickerText, long when)
{
this.icon = icon;
@@ -384,19 +372,7 @@
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);
- }
- }
+ tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
}
if (parcel.readInt() != 0) {
contentView = RemoteViews.CREATOR.createFromParcel(parcel);
@@ -434,18 +410,8 @@
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.tickerView != null) {
+ that.tickerView = this.tickerView.clone();
}
if (this.contentView != null) {
that.contentView = this.contentView.clone();
@@ -503,32 +469,12 @@
} else {
parcel.writeInt(0);
}
- if (tickerTitle != null) {
+ if (tickerView != null) {
parcel.writeInt(1);
- TextUtils.writeToParcel(tickerTitle, parcel, flags);
+ tickerView.writeToParcel(parcel, 0);
} 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);
@@ -591,7 +537,10 @@
* {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
* that you take care of task management as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application Fundamentals: Activities and Tasks</a>.
+ *
+ * @deprecated Use {@link Builder} instead.
*/
+ @Deprecated
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
RemoteViews contentView = new RemoteViews(context.getPackageName(),
@@ -651,4 +600,214 @@
sb.append(")");
return sb.toString();
}
+
+ public static class Builder {
+ private Context mContext;
+
+ private long mWhen;
+ private int mSmallIcon;
+ private int mSmallIconLevel;
+ private int mSmallIconNumber;
+ private CharSequence mContentTitle;
+ private CharSequence mContentText;
+ private CharSequence mContentInfo;
+ private PendingIntent mContentIntent;
+ private RemoteViews mContentView;
+ private PendingIntent mDeleteIntent;
+ private PendingIntent mFullScreenIntent;
+ private CharSequence mTickerText;
+ private RemoteViews mTickerView;
+ private Bitmap mLargeIcon;
+ private Uri mSound;
+ private int mAudioStreamType;
+ private long[] mVibrate;
+ private int mLedArgb;
+ private int mLedOnMs;
+ private int mLedOffMs;
+ private int mDefaults;
+ private int mFlags;
+
+ public Builder(Context context) {
+ mContext = context;
+ mWhen = System.currentTimeMillis();
+ }
+
+ public Builder setWhen(long when) {
+ mWhen = when;
+ return this;
+ }
+
+ public Builder setSmallIcon(int icon) {
+ mSmallIcon = icon;
+ return this;
+ }
+
+ public Builder setSmallIcon(int icon, int level) {
+ mSmallIcon = icon;
+ mSmallIconLevel = level;
+ return this;
+ }
+
+ public Builder setSmallIconNumber(int number) {
+ mSmallIconNumber = number;
+ return this;
+ }
+
+ public Builder setContentTitle(CharSequence title) {
+ mContentTitle = title;
+ return this;
+ }
+
+ public Builder setContentText(CharSequence text) {
+ mContentText = text;
+ return this;
+ }
+
+ public Builder setContentInfo(CharSequence info) {
+ mContentInfo = info;
+ return this;
+ }
+
+ public Builder setContent(RemoteViews views) {
+ mContentView = views;
+ return this;
+ }
+
+ public Builder setContentIntent(PendingIntent intent) {
+ mContentIntent = intent;
+ return this;
+ }
+
+ public Builder setDeleteIntent(PendingIntent intent) {
+ mDeleteIntent = intent;
+ return this;
+ }
+
+ public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
+ mFullScreenIntent = intent;
+ setFlag(FLAG_HIGH_PRIORITY, highPriority);
+ return this;
+ }
+
+ public Builder setTicker(CharSequence tickerText) {
+ mTickerText = tickerText;
+ return this;
+ }
+
+ public Builder setTicker(CharSequence tickerText, RemoteViews views) {
+ mTickerText = tickerText;
+ mTickerView = views;
+ return this;
+ }
+
+ public Builder setLargeIcon(Bitmap icon) {
+ mLargeIcon = icon;
+ return this;
+ }
+
+ public Builder setSound(Uri sound, int streamType) {
+ mSound = sound;
+ mAudioStreamType = streamType;
+ return this;
+ }
+
+ public Builder setVibrate(long[] pattern) {
+ mVibrate = pattern;
+ return this;
+ }
+
+ public Builder setLights(int argb, int onMs, int offMs) {
+ mLedArgb = argb;
+ mLedOnMs = onMs;
+ mLedOffMs = offMs;
+ mFlags |= FLAG_SHOW_LIGHTS;
+ return this;
+ }
+
+ public Builder setOngoing(boolean ongoing) {
+ setFlag(FLAG_ONGOING_EVENT, ongoing);
+ return this;
+ }
+
+ public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
+ setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
+ return this;
+ }
+
+ public Builder setAutoCancel(boolean autoCancel) {
+ setFlag(FLAG_ONLY_ALERT_ONCE, autoCancel);
+ return this;
+ }
+
+ public Builder setDefaults(int defaults) {
+ mDefaults = defaults;
+ return this;
+ }
+
+ private void setFlag(int mask, boolean value) {
+ if (value) {
+ mFlags |= mask;
+ } else {
+ mFlags &= ~mask;
+ }
+ }
+
+ private RemoteViews makeContentView() {
+ if (mContentView != null) {
+ return mContentView;
+ } else {
+ RemoteViews contentView = new RemoteViews(mContext.getPackageName(),
+ com.android.internal.R.layout.status_bar_latest_event_content);
+ if (mSmallIcon != 0) {
+ contentView.setImageViewResource(com.android.internal.R.id.icon, mSmallIcon);
+ }
+ if (mContentTitle != null) {
+ contentView.setTextViewText(com.android.internal.R.id.title, mContentTitle);
+ }
+ if (mContentText != null) {
+ contentView.setTextViewText(com.android.internal.R.id.text, mContentText);
+ }
+ //TODO
+ //if (mContentInfo) {
+ // contentVeiw.setTextViewText(com.android.internal.R.id.info, mContentInfo);
+ //}
+ if (mWhen != 0) {
+ contentView.setLong(com.android.internal.R.id.time, "setTime", mWhen);
+ }
+ return contentView;
+ }
+ }
+
+ private RemoteViews makeTickerView() {
+ if (mTickerView != null) {
+ return mTickerView;
+ } else {
+ return makeContentView();
+ }
+ }
+
+ public Notification getNotification() {
+ Notification n = new Notification();
+ n.when = mWhen;
+ n.icon = mSmallIcon;
+ n.iconLevel = mSmallIconLevel;
+ n.number = mSmallIconNumber;
+ n.contentView = makeContentView();
+ n.contentIntent = mContentIntent;
+ n.deleteIntent = mDeleteIntent;
+ n.fullScreenIntent = mFullScreenIntent;
+ n.tickerText = mTickerText;
+ n.tickerView = makeTickerView();
+ n.largeIcon = mLargeIcon;
+ n.sound = mSound;
+ n.audioStreamType = mAudioStreamType;
+ n.vibrate = mVibrate;
+ n.ledARGB = mLedArgb;
+ n.ledOnMS = mLedOnMs;
+ n.ledOffMS = mLedOffMs;
+ n.defaults = mDefaults;
+ n.flags = mFlags;
+ return n;
+ }
+ }
}
diff --git a/packages/SystemUI/res/drawable/ticker_background.xml b/packages/SystemUI/res/drawable/ticker_background.xml
new file mode 100644
index 0000000..7320fa0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ticker_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<layer-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:opacity="translucent"
+ >
+ <!-- the large icon extends 12dp beyond the edge of the status bar -->
+ <item
+ android:drawable="@drawable/notification_item_background_color"
+ android:top="12dp"
+ />
+</layer-list>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index d11e6da..e9661a6 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -242,7 +242,7 @@
<!-- ticker: transient incoming notification information -->
<FrameLayout
android:id="@+id/ticker"
- android:layout_width="wrap_content"
+ android:layout_width="392dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/systemInfo"
diff --git a/packages/SystemUI/res/layout-xlarge/ticker.xml b/packages/SystemUI/res/layout-xlarge/ticker.xml
index c8d855f..1506848 100644
--- a/packages/SystemUI/res/layout-xlarge/ticker.xml
+++ b/packages/SystemUI/res/layout-xlarge/ticker.xml
@@ -18,36 +18,13 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#ff000000"
>
+ <!--
+ android:background="@drawable/ticker_background"
+ -->
- <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/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 233ac45..b917324 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -464,7 +464,7 @@
boolean immersive = false;
try {
immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
- Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
+ //Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
} catch (RemoteException ex) {
}
if (false && immersive) {
@@ -603,9 +603,7 @@
}
private boolean hasTicker(Notification n) {
- return !TextUtils.isEmpty(n.tickerText)
- || !TextUtils.isEmpty(n.tickerTitle)
- || !TextUtils.isEmpty(n.tickerSubtitle);
+ return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
}
private void tick(StatusBarNotification n) {
@@ -1005,7 +1003,6 @@
} catch (RemoteException ex) {
// system process is dead if we're here.
}
- // animateCollapse();
}
});
} else {
@@ -1034,7 +1031,7 @@
exception = e;
}
if (expanded == null) {
- String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
+ final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
return false;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 3c3139f..051c208 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -108,12 +108,16 @@
}
// In with the new...
- final StatusBarNotification next = dequeue();
- if (next != null) {
+ StatusBarNotification next = dequeue();
+ while (next != null) {
mCurrentNotification = next;
mCurrentView = makeTickerView(next);
- mParent.addView(mCurrentView);
- sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+ if (mCurrentView != null) {
+ mParent.addView(mCurrentView);
+ sendEmptyMessageDelayed(MSG_ADVANCE, ADVANCE_DELAY);
+ break;
+ }
+ next = dequeue();
}
}
@@ -139,40 +143,36 @@
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);
+ int layoutId;
+ if (n.tickerView != null) {
+ group = (ViewGroup)inflater.inflate(R.layout.ticker, null, false);
+ View expanded = null;
+ Exception exception = null;
+ try {
+ expanded = n.tickerView.apply(mContext, group);
}
- if (n.tickerSubtitle != null) {
- final TextView subtitle = (TextView)group.findViewById(R.id.subtitle);
- subtitle.setText(n.tickerSubtitle);
+ catch (RuntimeException e) {
+ exception = e;
}
- } else {
+ if (expanded == null) {
+ final String ident = notification.pkg
+ + "/0x" + Integer.toHexString(notification.id);
+ Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
+ return null;
+ }
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f);
+ lp.gravity = Gravity.BOTTOM;
+ group.addView(expanded, lp);
+ } else if (n.tickerText != null) {
group = (ViewGroup)inflater.inflate(R.layout.ticker_compat, mParent, false);
TextView tv = (TextView)group.findViewById(R.id.text);
tv.setText(n.tickerText);
+ } else {
+ throw new RuntimeException("tickerView==null && tickerText==null");
}
-
- // 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);
- }
- }
- }
-
+ // TODO: Add Large icon
return group;
}
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 1081a20..6de7e6a 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -272,11 +272,13 @@
public void onNotificationClick(String pkg, String tag, int id) {
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE);
+ Notification.FLAG_FOREGROUND_SERVICE, true);
}
public void onNotificationClear(String pkg, String tag, int id) {
- cancelNotification(pkg, tag, id, 0, 0); // maybe add some flags?
+ cancelNotification(pkg, tag, id, 0,
+ Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
+ true);
}
public void onPanelRevealed() {
@@ -312,7 +314,7 @@
int uid, int initialPid, String message) {
Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
- cancelNotification(pkg, tag, id, 0, 0);
+ cancelNotification(pkg, tag, id, 0, 0, false);
long ident = Binder.clearCallingIdentity();
try {
ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -855,7 +857,20 @@
manager.sendAccessibilityEvent(event);
}
- private void cancelNotificationLocked(NotificationRecord r) {
+ private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete) {
+ // tell the app
+ if (sendDelete) {
+ if (r.notification.deleteIntent != null) {
+ try {
+ r.notification.deleteIntent.send();
+ } catch (PendingIntent.CanceledException ex) {
+ // do nothing - there's no relevant way to recover, and
+ // no reason to let this propagate
+ Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
+ }
+ }
+ }
+
// status bar
if (r.notification.icon != 0) {
long identity = Binder.clearCallingIdentity();
@@ -904,7 +919,7 @@
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
- int mustNotHaveFlags) {
+ int mustNotHaveFlags, boolean sendDelete) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags);
synchronized (mNotificationList) {
@@ -921,7 +936,7 @@
mNotificationList.remove(index);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, sendDelete);
updateLightsLocked();
}
}
@@ -954,7 +969,7 @@
return true;
}
mNotificationList.remove(i);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, false);
}
if (canceledSomething) {
updateLightsLocked();
@@ -973,7 +988,7 @@
// Don't allow client applications to cancel foreground service notis.
cancelNotification(pkg, tag, id, 0,
Binder.getCallingUid() == Process.SYSTEM_UID
- ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
}
public void cancelAllNotifications(String pkg) {
@@ -1009,17 +1024,8 @@
if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
- if (r.notification.deleteIntent != null) {
- try {
- r.notification.deleteIntent.send();
- } catch (PendingIntent.CanceledException ex) {
- // do nothing - there's no relevant way to recover, and
- // no reason to let this propagate
- Slog.w(TAG, "canceled PendingIntent for " + r.pkg, ex);
- }
- }
mNotificationList.remove(i);
- cancelNotificationLocked(r);
+ cancelNotificationLocked(r, true);
}
}
diff --git a/tests/StatusBar/AndroidManifest.xml b/tests/StatusBar/AndroidManifest.xml
index c1ca618..b1734bb 100644
--- a/tests/StatusBar/AndroidManifest.xml
+++ b/tests/StatusBar/AndroidManifest.xml
@@ -21,6 +21,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name="NotificationBuilderTest" android:label="_Notify Builder">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity android:name="ToastTest" android:label="_Toasts">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -35,6 +42,8 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name="ConfirmationActivity" android:theme="@android:style/Theme.Dialog">
+ </activity>
<activity android:name="TestAlertActivity" android:theme="@android:style/Theme.Dialog">
</activity>
</application>
diff --git a/tests/StatusBar/res/layout/confirmation_activity.xml b/tests/StatusBar/res/layout/confirmation_activity.xml
new file mode 100644
index 0000000..50d1a49
--- /dev/null
+++ b/tests/StatusBar/res/layout/confirmation_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ >
+
+ <TextView android:id="@+id/text"
+ style="?android:attr/textAppearanceLarge"
+ android:padding="5dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="3"
+ android:onClick="dismiss"
+ android:text="Ok" />
+
+</LinearLayout>
diff --git a/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java b/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java
new file mode 100644
index 0000000..5ce8f3f
--- /dev/null
+++ b/tests/StatusBar/src/com/android/statusbartest/ConfirmationActivity.java
@@ -0,0 +1,32 @@
+package com.android.statusbartest;
+
+import android.app.Activity;
+import android.view.View;
+import android.widget.TextView;
+
+public class ConfirmationActivity extends Activity {
+ public static final String EXTRA_TITLE = "title";
+ public static final String EXTRA_TEXT = "text";
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ setContentView(R.layout.confirmation_activity);
+ setTitle(getTextExtra(EXTRA_TITLE, "Title"));
+ ((TextView)findViewById(R.id.text)).setText(getTextExtra(EXTRA_TEXT, "text"));
+ findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ }
+
+ private String getTextExtra(String extra, String def) {
+ final String text = getIntent().getStringExtra(extra);
+ if (text == null) {
+ return def;
+ } else {
+ return text;
+ }
+ }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
new file mode 100644
index 0000000..b871197
--- /dev/null
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.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.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Vibrator;
+import android.os.Handler;
+import android.util.Log;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.widget.RemoteViews;
+import android.os.PowerManager;
+
+public class NotificationBuilderTest extends TestActivity
+{
+ private final static String TAG = "NotificationTestList";
+
+ NotificationManager mNM;
+
+ @Override
+ protected String tag() {
+ return TAG;
+ }
+
+ @Override
+ protected Test[] tests() {
+ mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+
+ return mTests;
+ }
+
+ private Test[] mTests = new Test[] {
+ new Test("Cancel (1)") {
+ public void run() {
+ mNM.cancel(1);
+ }
+ },
+
+ new Test("Basic Content (1)") {
+ public void run() {
+ int id = 1;
+
+ Notification.Builder b = new Notification.Builder(NotificationBuilderTest.this);
+
+ b.setWhen(System.currentTimeMillis());
+ b.setSmallIcon(R.drawable.ic_statusbar_chat);
+ b.setContentTitle("Title");
+ b.setContentText("text\nline2");
+ b.setContentIntent(makeContentIntent(id));
+ b.setDeleteIntent(makeDeleteIntent(id));
+
+ mNM.notify(id, b.getNotification());
+ }
+ },
+ };
+
+ private PendingIntent makeContentIntent(int id) {
+ Intent intent = new Intent(this, ConfirmationActivity.class);
+ intent.setData(Uri.fromParts("content", "//status_bar_test/content/" + id, null));
+ intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Content intent");
+ intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
+ return PendingIntent.getActivity(this, 0, intent, 0);
+ }
+
+ private PendingIntent makeDeleteIntent(int id) {
+ Intent intent = new Intent(this, ConfirmationActivity.class);
+ intent.setData(Uri.fromParts("content", "//status_bar_test/delete/" + id, null));
+ intent.putExtra(ConfirmationActivity.EXTRA_TITLE, "Delete intent");
+ intent.putExtra(ConfirmationActivity.EXTRA_TEXT, "id: " + id);
+ return PendingIntent.getActivity(this, 0, intent, 0);
+ }
+}
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 2df97dc..0f0637f 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -135,95 +135,6 @@
}
},
- 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",