Skip to content

Commit f3b7343

Browse files
committed
The beginning of expanded notifications.
There are now two "rebuilder" classes, each of which consumes a Notification.Builder and modifies its behavior. (Inheritance in Builder classes is...not advisable.) - BigPictureStyle: includes a large Bitmap above the usual notification strip. - BigTextStyle: shows the contentText in a large, wrapping TextView instead of truncating to one line. As for SystemUI, the notification panel now shows the expanded form if it is available, otherwise the usual contentView is shown. (Note that the structure of largeIcon notifications has changed a bit: The largeIcon is no longer handled by the status bar at all; it's entirely inside the template now. Not only does this make the code simpler, and make large notifications possible, but it fixes the longstanding irritation that tapping on a largeIcon doesn't highlight the whole notification row. Man, that feels good.) Change-Id: I2b9d8a6ea4385659d8cb1ed467c1caf5e12628dd
1 parent fb32ab9 commit f3b7343

File tree

8 files changed

+205
-71
lines changed

8 files changed

+205
-71
lines changed

api/current.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3724,6 +3724,7 @@ package android.app {
37243724
method public android.app.Notification.Builder setSmallIcon(int, int);
37253725
method public android.app.Notification.Builder setSound(android.net.Uri);
37263726
method public android.app.Notification.Builder setSound(android.net.Uri, int);
3727+
method public android.app.Notification.Builder setSubText(java.lang.CharSequence);
37273728
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
37283729
method public android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
37293730
method public android.app.Notification.Builder setUsesIntruderAlert(boolean);

core/java/android/app/Notification.java

Lines changed: 155 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,20 @@ public class Notification implements Parcelable
187187
*/
188188
public RemoteViews contentView;
189189

190-
191190
/**
192191
* The view that will represent this notification in the pop-up "intruder alert" dialog.
193192
* @hide
194193
*/
195194
public RemoteViews intruderView;
196195

196+
/**
197+
* A larger version of {@link #contentView}, giving the Notification an
198+
* opportunity to show more detail. The system UI may choose to show this
199+
* instead of the normal content view at its discretion.
200+
* @hide
201+
*/
202+
public RemoteViews bigContentView;
203+
197204
/**
198205
* The bitmap that may escape the bounds of the panel and bar.
199206
*/
@@ -584,6 +591,9 @@ public Notification(Parcel parcel)
584591
if (parcel.readInt() != 0) {
585592
intruderView = RemoteViews.CREATOR.createFromParcel(parcel);
586593
}
594+
if (parcel.readInt() != 0) {
595+
bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
596+
}
587597
}
588598

589599
@Override
@@ -650,6 +660,9 @@ public Notification clone() {
650660
if (this.intruderView != null) {
651661
that.intruderView = this.intruderView.clone();
652662
}
663+
if (this.bigContentView != null) {
664+
that.bigContentView = this.bigContentView.clone();
665+
}
653666

654667
return that;
655668
}
@@ -747,6 +760,13 @@ public void writeToParcel(Parcel parcel, int flags)
747760
} else {
748761
parcel.writeInt(0);
749762
}
763+
764+
if (bigContentView != null) {
765+
parcel.writeInt(1);
766+
bigContentView.writeToParcel(parcel, 0);
767+
} else {
768+
parcel.writeInt(0);
769+
}
750770
}
751771

752772
/**
@@ -896,6 +916,7 @@ public static class Builder {
896916
private CharSequence mContentTitle;
897917
private CharSequence mContentText;
898918
private CharSequence mContentInfo;
919+
private CharSequence mSubText;
899920
private PendingIntent mContentIntent;
900921
private RemoteViews mContentView;
901922
private PendingIntent mDeleteIntent;
@@ -1012,6 +1033,15 @@ public Builder setContentText(CharSequence text) {
10121033
return this;
10131034
}
10141035

1036+
/**
1037+
* Set the third line of text in the platform notification template.
1038+
* Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the same location in the standard template.
1039+
*/
1040+
public Builder setSubText(CharSequence text) {
1041+
mSubText = text;
1042+
return this;
1043+
}
1044+
10151045
/**
10161046
* Set the large number at the right-hand side of the notification. This is
10171047
* equivalent to setContentInfo, although it might show the number in a different
@@ -1025,7 +1055,6 @@ public Builder setNumber(int number) {
10251055
/**
10261056
* A small piece of additional information pertaining to this notification.
10271057
*
1028-
10291058
* The platform template will draw this on the last line of the notification, at the far
10301059
* right (to the right of a smallIcon if it has been placed there).
10311060
*/
@@ -1037,7 +1066,6 @@ public Builder setContentInfo(CharSequence info) {
10371066
/**
10381067
* Set the progress this notification represents.
10391068
*
1040-
10411069
* The platform template will represent this using a {@link ProgressBar}.
10421070
*/
10431071
public Builder setProgress(int max, int progress, boolean indeterminate) {
@@ -1050,7 +1078,6 @@ public Builder setProgress(int max, int progress, boolean indeterminate) {
10501078
/**
10511079
* Supply a custom RemoteViews to use instead of the platform template.
10521080
*
1053-
10541081
* @see Notification#contentView
10551082
*/
10561083
public Builder setContent(RemoteViews views) {
@@ -1061,17 +1088,12 @@ public Builder setContent(RemoteViews views) {
10611088
/**
10621089
* Supply a {@link PendingIntent} to be sent when the notification is clicked.
10631090
*
1064-
10651091
* As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
10661092
* have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
10671093
* {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
1068-
10691094
* to assign PendingIntents to individual views in that custom layout (i.e., to create
1070-
1071-
* clickable buttons inside the
1072-
* notification view).
1095+
* clickable buttons inside the notification view).
10731096
*
1074-
10751097
* @see Notification#contentIntent Notification.contentIntent
10761098
*/
10771099
public Builder setContentIntent(PendingIntent intent) {
@@ -1082,7 +1104,6 @@ public Builder setContentIntent(PendingIntent intent) {
10821104
/**
10831105
* Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
10841106
*
1085-
10861107
* @see Notification#deleteIntent
10871108
*/
10881109
public Builder setDeleteIntent(PendingIntent intent) {
@@ -1115,7 +1136,6 @@ public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
11151136
* Set the "ticker" text which is displayed in the status bar when the notification first
11161137
* arrives.
11171138
*
1118-
11191139
* @see Notification#tickerText
11201140
*/
11211141
public Builder setTicker(CharSequence tickerText) {
@@ -1355,20 +1375,28 @@ private void setFlag(int mask, boolean value) {
13551375
}
13561376
}
13571377

1358-
private RemoteViews makeRemoteViews(int resId) {
1378+
private RemoteViews applyStandardTemplate(int resId) {
13591379
RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
13601380
boolean hasLine3 = false;
1381+
boolean hasLine2 = false;
1382+
int smallIconImageViewId = R.id.icon;
1383+
if (mLargeIcon != null) {
1384+
contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
1385+
smallIconImageViewId = R.id.right_icon;
1386+
}
13611387
if (mSmallIcon != 0) {
1362-
contentView.setImageViewResource(R.id.icon, mSmallIcon);
1363-
contentView.setViewVisibility(R.id.icon, View.VISIBLE);
1388+
contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
1389+
contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
13641390
} else {
1365-
contentView.setViewVisibility(R.id.icon, View.GONE);
1391+
contentView.setViewVisibility(smallIconImageViewId, View.GONE);
13661392
}
13671393
if (mContentTitle != null) {
13681394
contentView.setTextViewText(R.id.title, mContentTitle);
13691395
}
13701396
if (mContentText != null) {
1371-
contentView.setTextViewText(R.id.text, mContentText);
1397+
contentView.setTextViewText(
1398+
(mSubText != null) ? R.id.text2 : R.id.text,
1399+
mContentText);
13721400
hasLine3 = true;
13731401
}
13741402
if (mContentInfo != null) {
@@ -1390,12 +1418,19 @@ private RemoteViews makeRemoteViews(int resId) {
13901418
} else {
13911419
contentView.setViewVisibility(R.id.info, View.GONE);
13921420
}
1393-
if (mProgressMax != 0 || mProgressIndeterminate) {
1394-
contentView.setProgressBar(
1395-
R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
1396-
contentView.setViewVisibility(R.id.progress, View.VISIBLE);
1421+
1422+
if (mSubText != null) {
1423+
contentView.setTextViewText(R.id.text, mSubText);
1424+
contentView.setViewVisibility(R.id.text2, View.VISIBLE);
13971425
} else {
1398-
contentView.setViewVisibility(R.id.progress, View.GONE);
1426+
contentView.setViewVisibility(R.id.text2, View.GONE);
1427+
if (mProgressMax != 0 || mProgressIndeterminate) {
1428+
contentView.setProgressBar(
1429+
R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
1430+
contentView.setViewVisibility(R.id.progress, View.VISIBLE);
1431+
} else {
1432+
contentView.setViewVisibility(R.id.progress, View.GONE);
1433+
}
13991434
}
14001435
if (mWhen != 0) {
14011436
contentView.setLong(R.id.time, "setTime", mWhen);
@@ -1408,9 +1443,7 @@ private RemoteViews makeContentView() {
14081443
if (mContentView != null) {
14091444
return mContentView;
14101445
} else {
1411-
return makeRemoteViews(mLargeIcon == null
1412-
? R.layout.status_bar_latest_event_content
1413-
: R.layout.status_bar_latest_event_content_large_icon);
1446+
return applyStandardTemplate(R.layout.status_bar_latest_event_content); // no more special large_icon flavor
14141447
}
14151448
}
14161449

@@ -1419,7 +1452,7 @@ private RemoteViews makeTickerView() {
14191452
return mTickerView;
14201453
} else {
14211454
if (mContentView == null) {
1422-
return makeRemoteViews(mLargeIcon == null
1455+
return applyStandardTemplate(mLargeIcon == null
14231456
? R.layout.status_bar_latest_event_ticker
14241457
: R.layout.status_bar_latest_event_ticker_large_icon);
14251458
} else {
@@ -1516,4 +1549,100 @@ public Notification getNotification() {
15161549
return n;
15171550
}
15181551
}
1552+
1553+
/**
1554+
* @hide because this API is still very rough
1555+
*
1556+
* This is a "rebuilder": It consumes a Builder object and modifies its output.
1557+
*
1558+
* This represents the "big picture" style notification, with a large Bitmap atop the usual notification.
1559+
*
1560+
* Usage:
1561+
* <pre class="prettyprint">
1562+
* Notification noti = new Notification.BigPictureStyle(
1563+
* new Notification.Builder()
1564+
* .setContentTitle(&quot;New mail from &quot; + sender.toString())
1565+
* .setContentText(subject)
1566+
* .setSmallIcon(R.drawable.new_mail)
1567+
* .setLargeIcon(aBitmap))
1568+
* .bigPicture(aBigBitmap)
1569+
* .build();
1570+
* </pre>
1571+
*/
1572+
public static class BigPictureStyle {
1573+
private Builder mBuilder;
1574+
private Bitmap mPicture;
1575+
1576+
public BigPictureStyle(Builder builder) {
1577+
mBuilder = builder;
1578+
}
1579+
1580+
public BigPictureStyle bigPicture(Bitmap b) {
1581+
mPicture = b;
1582+
return this;
1583+
}
1584+
1585+
private RemoteViews makeBigContentView() {
1586+
RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.notification_template_big_picture);
1587+
1588+
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
1589+
1590+
return contentView;
1591+
}
1592+
1593+
public Notification build() {
1594+
Notification wip = mBuilder.getNotification();
1595+
wip.bigContentView = makeBigContentView();
1596+
return wip;
1597+
}
1598+
}
1599+
1600+
/**
1601+
* @hide because this API is still very rough
1602+
*
1603+
* This is a "rebuilder": It consumes a Builder object and modifies its output.
1604+
*
1605+
* This represents the "big text" style notification, with more area for the main content text to be read in its entirety.
1606+
*
1607+
* Usage:
1608+
* <pre class="prettyprint">
1609+
* Notification noti = new Notification.BigPictureStyle(
1610+
* new Notification.Builder()
1611+
* .setContentTitle(&quot;New mail from &quot; + sender.toString())
1612+
* .setContentText(subject)
1613+
* .setSmallIcon(R.drawable.new_mail)
1614+
* .setLargeIcon(aBitmap))
1615+
* .bigText(aVeryLongString)
1616+
* .build();
1617+
* </pre>
1618+
*/
1619+
public static class BigTextStyle {
1620+
private Builder mBuilder;
1621+
private CharSequence mBigText;
1622+
1623+
public BigTextStyle(Builder builder) {
1624+
mBuilder = builder;
1625+
}
1626+
1627+
public BigTextStyle bigText(CharSequence cs) {
1628+
mBigText = cs;
1629+
return this;
1630+
}
1631+
1632+
private RemoteViews makeBigContentView() {
1633+
RemoteViews contentView = mBuilder.applyStandardTemplate(R.layout.status_bar_latest_event_content);
1634+
1635+
contentView.setTextViewText(R.id.big_text, mBigText);
1636+
contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
1637+
contentView.setTextViewText(R.id.text, ""); // XXX: what do do with this spot?
1638+
1639+
return contentView;
1640+
}
1641+
1642+
public Notification build() {
1643+
Notification wip = mBuilder.getNotification();
1644+
wip.bigContentView = makeBigContentView();
1645+
return wip;
1646+
}
1647+
}
15191648
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:id="@+id/status_bar_latest_event_content"
3+
android:layout_width="match_parent"
4+
android:layout_height="match_parent"
5+
>
6+
<ImageView
7+
android:id="@+id/big_picture"
8+
android:layout_width="match_parent"
9+
android:layout_height="192dp"
10+
android:scaleType="centerCrop"
11+
/>
12+
<include layout="@layout/status_bar_latest_event_content"
13+
android:layout_width="match_parent"
14+
android:layout_height="@dimen/notification_large_icon_height"
15+
android:layout_marginTop="192dp"
16+
/>
17+
</FrameLayout>

core/res/res/layout/status_bar_latest_event_content.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
22
android:id="@+id/status_bar_latest_event_content"
33
android:layout_width="match_parent"
4-
android:layout_height="match_parent"
4+
android:layout_height="wrap_content"
55
>
66
<ImageView android:id="@+id/icon"
77
android:layout_width="@dimen/notification_large_icon_width"

core/res/res/layout/status_bar_latest_event_content_large_icon.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
android:orientation="vertical"
77
android:paddingLeft="12dp"
88
android:paddingRight="12dp"
9+
android:paddingTop="4dp"
10+
android:paddingBottom="4dp"
911
>
1012
<LinearLayout
1113
android:id="@+id/line1"
@@ -44,6 +46,13 @@
4446
android:ellipsize="marquee"
4547
android:visibility="gone"
4648
/>
49+
<TextView android:id="@+id/big_text"
50+
android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
51+
android:layout_width="match_parent"
52+
android:layout_height="wrap_content"
53+
android:singleLine="false"
54+
android:visibility="gone"
55+
/>
4756
<LinearLayout
4857
android:id="@+id/line3"
4958
android:layout_width="match_parent"
@@ -70,7 +79,7 @@
7079
android:gravity="center"
7180
android:paddingLeft="8dp"
7281
/>
73-
<ImageView android:id="@+id/icon"
82+
<ImageView android:id="@+id/right_icon"
7483
android:layout_width="wrap_content"
7584
android:layout_height="wrap_content"
7685
android:layout_gravity="center"

0 commit comments

Comments
 (0)