• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app;
18 
19 import com.android.internal.R;
20 
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.res.Resources;
24 import android.graphics.Bitmap;
25 import android.media.AudioManager;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.IBinder;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.os.SystemClock;
32 import android.text.TextUtils;
33 import android.util.IntProperty;
34 import android.util.Log;
35 import android.util.Slog;
36 import android.util.TypedValue;
37 import android.view.View;
38 import android.widget.ProgressBar;
39 import android.widget.RemoteViews;
40 
41 import java.text.NumberFormat;
42 import java.util.ArrayList;
43 
44 /**
45  * A class that represents how a persistent notification is to be presented to
46  * the user using the {@link android.app.NotificationManager}.
47  *
48  * <p>The {@link Notification.Builder Notification.Builder} has been added to make it
49  * easier to construct Notifications.</p>
50  *
51  * <div class="special reference">
52  * <h3>Developer Guides</h3>
53  * <p>For a guide to creating notifications, read the
54  * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
55  * developer guide.</p>
56  * </div>
57  */
58 public class Notification implements Parcelable
59 {
60     /**
61      * Use all default values (where applicable).
62      */
63     public static final int DEFAULT_ALL = ~0;
64 
65     /**
66      * Use the default notification sound. This will ignore any given
67      * {@link #sound}.
68      *
69 
70      * @see #defaults
71      */
72 
73     public static final int DEFAULT_SOUND = 1;
74 
75     /**
76      * Use the default notification vibrate. This will ignore any given
77      * {@link #vibrate}. Using phone vibration requires the
78      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
79      *
80      * @see #defaults
81      */
82 
83     public static final int DEFAULT_VIBRATE = 2;
84 
85     /**
86      * Use the default notification lights. This will ignore the
87      * {@link #FLAG_SHOW_LIGHTS} bit, and {@link #ledARGB}, {@link #ledOffMS}, or
88      * {@link #ledOnMS}.
89      *
90      * @see #defaults
91      */
92 
93     public static final int DEFAULT_LIGHTS = 4;
94 
95     /**
96      * A timestamp related to this notification, in milliseconds since the epoch.
97      *
98      * Default value: {@link System#currentTimeMillis() Now}.
99      *
100      * Choose a timestamp that will be most relevant to the user. For most finite events, this
101      * corresponds to the time the event happened (or will happen, in the case of events that have
102      * yet to occur but about which the user is being informed). Indefinite events should be
103      * timestamped according to when the activity began.
104      *
105      * Some examples:
106      *
107      * <ul>
108      *   <li>Notification of a new chat message should be stamped when the message was received.</li>
109      *   <li>Notification of an ongoing file download (with a progress bar, for example) should be stamped when the download started.</li>
110      *   <li>Notification of a completed file download should be stamped when the download finished.</li>
111      *   <li>Notification of an upcoming meeting should be stamped with the time the meeting will begin (that is, in the future).</li>
112      *   <li>Notification of an ongoing stopwatch (increasing timer) should be stamped with the watch's start time.
113      *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
114      * </ul>
115      *
116      */
117     public long when;
118 
119     /**
120      * The resource id of a drawable to use as the icon in the status bar.
121      * This is required; notifications with an invalid icon resource will not be shown.
122      */
123     public int icon;
124 
125     /**
126      * If the icon in the status bar is to have more than one level, you can set this.  Otherwise,
127      * leave it at its default value of 0.
128      *
129      * @see android.widget.ImageView#setImageLevel
130      * @see android.graphics.drawable#setLevel
131      */
132     public int iconLevel;
133 
134     /**
135      * The number of events that this notification represents. For example, in a new mail
136      * notification, this could be the number of unread messages.
137      *
138      * The system may or may not use this field to modify the appearance of the notification. For
139      * example, before {@link android.os.Build.VERSION_CODES#HONEYCOMB}, this number was
140      * superimposed over the icon in the status bar. Starting with
141      * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, the template used by
142      * {@link Notification.Builder} has displayed the number in the expanded notification view.
143      *
144      * If the number is 0 or negative, it is never shown.
145      */
146     public int number;
147 
148     /**
149      * The intent to execute when the expanded status entry is clicked.  If
150      * this is an activity, it must include the
151      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
152      * that you take care of task management as described in the
153      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
154      * Stack</a> document.  In particular, make sure to read the notification section
155      * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#HandlingNotifications">Handling
156      * Notifications</a> for the correct ways to launch an application from a
157      * notification.
158      */
159     public PendingIntent contentIntent;
160 
161     /**
162      * The intent to execute when the notification is explicitly dismissed by the user, either with
163      * the "Clear All" button or by swiping it away individually.
164      *
165      * This probably shouldn't be launching an activity since several of those will be sent
166      * at the same time.
167      */
168     public PendingIntent deleteIntent;
169 
170     /**
171      * An intent to launch instead of posting the notification to the status bar.
172      *
173      * @see Notification.Builder#setFullScreenIntent
174      */
175     public PendingIntent fullScreenIntent;
176 
177     /**
178      * Text to scroll across the screen when this item is added to
179      * the status bar on large and smaller devices.
180      *
181      * @see #tickerView
182      */
183     public CharSequence tickerText;
184 
185     /**
186      * The view to show as the ticker in the status bar when the notification
187      * is posted.
188      */
189     public RemoteViews tickerView;
190 
191     /**
192      * The view that will represent this notification in the expanded status bar.
193      */
194     public RemoteViews contentView;
195 
196     /**
197      * A large-format 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      */
201     public RemoteViews bigContentView;
202 
203     /**
204      * The bitmap that may escape the bounds of the panel and bar.
205      */
206     public Bitmap largeIcon;
207 
208     /**
209      * The sound to play.
210      *
211      * <p>
212      * To play the default notification sound, see {@link #defaults}.
213      * </p>
214      */
215     public Uri sound;
216 
217     /**
218      * Use this constant as the value for audioStreamType to request that
219      * the default stream type for notifications be used.  Currently the
220      * default stream type is {@link AudioManager#STREAM_NOTIFICATION}.
221      */
222     public static final int STREAM_DEFAULT = -1;
223 
224     /**
225      * The audio stream type to use when playing the sound.
226      * Should be one of the STREAM_ constants from
227      * {@link android.media.AudioManager}.
228      */
229     public int audioStreamType = STREAM_DEFAULT;
230 
231     /**
232      * The pattern with which to vibrate.
233      *
234      * <p>
235      * To vibrate the default pattern, see {@link #defaults}.
236      * </p>
237      *
238      * @see android.os.Vibrator#vibrate(long[],int)
239      */
240     public long[] vibrate;
241 
242     /**
243      * The color of the led.  The hardware will do its best approximation.
244      *
245      * @see #FLAG_SHOW_LIGHTS
246      * @see #flags
247      */
248     public int ledARGB;
249 
250     /**
251      * The number of milliseconds for the LED to be on while it's flashing.
252      * The hardware will do its best approximation.
253      *
254      * @see #FLAG_SHOW_LIGHTS
255      * @see #flags
256      */
257     public int ledOnMS;
258 
259     /**
260      * The number of milliseconds for the LED to be off while it's flashing.
261      * The hardware will do its best approximation.
262      *
263      * @see #FLAG_SHOW_LIGHTS
264      * @see #flags
265      */
266     public int ledOffMS;
267 
268     /**
269      * Specifies which values should be taken from the defaults.
270      * <p>
271      * To set, OR the desired from {@link #DEFAULT_SOUND},
272      * {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}. For all default
273      * values, use {@link #DEFAULT_ALL}.
274      * </p>
275      */
276     public int defaults;
277 
278     /**
279      * Bit to be bitwise-ored into the {@link #flags} field that should be
280      * set if you want the LED on for this notification.
281      * <ul>
282      * <li>To turn the LED off, pass 0 in the alpha channel for colorARGB
283      *      or 0 for both ledOnMS and ledOffMS.</li>
284      * <li>To turn the LED on, pass 1 for ledOnMS and 0 for ledOffMS.</li>
285      * <li>To flash the LED, pass the number of milliseconds that it should
286      *      be on and off to ledOnMS and ledOffMS.</li>
287      * </ul>
288      * <p>
289      * Since hardware varies, you are not guaranteed that any of the values
290      * you pass are honored exactly.  Use the system defaults (TODO) if possible
291      * because they will be set to values that work on any given hardware.
292      * <p>
293      * The alpha channel must be set for forward compatibility.
294      *
295      */
296     public static final int FLAG_SHOW_LIGHTS        = 0x00000001;
297 
298     /**
299      * Bit to be bitwise-ored into the {@link #flags} field that should be
300      * set if this notification is in reference to something that is ongoing,
301      * like a phone call.  It should not be set if this notification is in
302      * reference to something that happened at a particular point in time,
303      * like a missed phone call.
304      */
305     public static final int FLAG_ONGOING_EVENT      = 0x00000002;
306 
307     /**
308      * Bit to be bitwise-ored into the {@link #flags} field that if set,
309      * the audio will be repeated until the notification is
310      * cancelled or the notification window is opened.
311      */
312     public static final int FLAG_INSISTENT          = 0x00000004;
313 
314     /**
315      * Bit to be bitwise-ored into the {@link #flags} field that should be
316      * set if you want the sound and/or vibration play each time the
317      * notification is sent, even if it has not been canceled before that.
318      */
319     public static final int FLAG_ONLY_ALERT_ONCE    = 0x00000008;
320 
321     /**
322      * Bit to be bitwise-ored into the {@link #flags} field that should be
323      * set if the notification should be canceled when it is clicked by the
324      * user.  On tablets, the
325 
326      */
327     public static final int FLAG_AUTO_CANCEL        = 0x00000010;
328 
329     /**
330      * Bit to be bitwise-ored into the {@link #flags} field that should be
331      * set if the notification should not be canceled when the user clicks
332      * the Clear all button.
333      */
334     public static final int FLAG_NO_CLEAR           = 0x00000020;
335 
336     /**
337      * Bit to be bitwise-ored into the {@link #flags} field that should be
338      * set if this notification represents a currently running service.  This
339      * will normally be set for you by {@link Service#startForeground}.
340      */
341     public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
342 
343     /**
344      * Obsolete flag indicating high-priority notifications; use the priority field instead.
345      *
346      * @deprecated Use {@link #priority} with a positive value.
347      */
348     public static final int FLAG_HIGH_PRIORITY      = 0x00000080;
349 
350     public int flags;
351 
352     /**
353      * Default notification {@link #priority}. If your application does not prioritize its own
354      * notifications, use this value for all notifications.
355      */
356     public static final int PRIORITY_DEFAULT = 0;
357 
358     /**
359      * Lower {@link #priority}, for items that are less important. The UI may choose to show these
360      * items smaller, or at a different position in the list, compared with your app's
361      * {@link #PRIORITY_DEFAULT} items.
362      */
363     public static final int PRIORITY_LOW = -1;
364 
365     /**
366      * Lowest {@link #priority}; these items might not be shown to the user except under special
367      * circumstances, such as detailed notification logs.
368      */
369     public static final int PRIORITY_MIN = -2;
370 
371     /**
372      * Higher {@link #priority}, for more important notifications or alerts. The UI may choose to
373      * show these items larger, or at a different position in notification lists, compared with
374      * your app's {@link #PRIORITY_DEFAULT} items.
375      */
376     public static final int PRIORITY_HIGH = 1;
377 
378     /**
379      * Highest {@link #priority}, for your application's most important items that require the
380      * user's prompt attention or input.
381      */
382     public static final int PRIORITY_MAX = 2;
383 
384     /**
385      * Relative priority for this notification.
386      *
387      * Priority is an indication of how much of the user's valuable attention should be consumed by
388      * this notification. Low-priority notifications may be hidden from the user in certain
389      * situations, while the user might be interrupted for a higher-priority notification. The
390      * system will make a determination about how to interpret notification priority as described in
391      * MUMBLE MUMBLE.
392      */
393     public int priority;
394 
395     /**
396      * @hide
397      * Notification type: incoming call (voice or video) or similar synchronous communication request.
398      */
399     public static final String KIND_CALL = "android.call";
400 
401     /**
402      * @hide
403      * Notification type: incoming direct message (SMS, instant message, etc.).
404      */
405     public static final String KIND_MESSAGE = "android.message";
406 
407     /**
408      * @hide
409      * Notification type: asynchronous bulk message (email).
410      */
411     public static final String KIND_EMAIL = "android.email";
412 
413     /**
414      * @hide
415      * Notification type: calendar event.
416      */
417     public static final String KIND_EVENT = "android.event";
418 
419     /**
420      * @hide
421      * Notification type: promotion or advertisement.
422      */
423     public static final String KIND_PROMO = "android.promo";
424 
425     /**
426      * @hide
427      * If this notification matches of one or more special types (see the <code>KIND_*</code>
428      * constants), add them here, best match first.
429      */
430     public String[] kind;
431 
432     /**
433      * Extra key for people values (type TBD).
434      *
435      * @hide
436      */
437     public static final String EXTRA_PEOPLE = "android.people";
438 
439     private Bundle extras;
440 
441     /**
442      * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
443      * @hide
444      */
445     private static class Action implements Parcelable {
446         public int icon;
447         public CharSequence title;
448         public PendingIntent actionIntent;
449         @SuppressWarnings("unused")
Action()450         public Action() { }
Action(Parcel in)451         private Action(Parcel in) {
452             icon = in.readInt();
453             title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
454             if (in.readInt() == 1) {
455                 actionIntent = PendingIntent.CREATOR.createFromParcel(in);
456             }
457         }
Action(int icon_, CharSequence title_, PendingIntent intent_)458         public Action(int icon_, CharSequence title_, PendingIntent intent_) {
459             this.icon = icon_;
460             this.title = title_;
461             this.actionIntent = intent_;
462         }
463         @Override
clone()464         public Action clone() {
465             return new Action(
466                 this.icon,
467                 this.title.toString(),
468                 this.actionIntent // safe to alias
469             );
470         }
471         @Override
describeContents()472         public int describeContents() {
473             return 0;
474         }
475         @Override
writeToParcel(Parcel out, int flags)476         public void writeToParcel(Parcel out, int flags) {
477             out.writeInt(icon);
478             TextUtils.writeToParcel(title, out, flags);
479             if (actionIntent != null) {
480                 out.writeInt(1);
481                 actionIntent.writeToParcel(out, flags);
482             } else {
483                 out.writeInt(0);
484             }
485         }
486         public static final Parcelable.Creator<Action> CREATOR
487         = new Parcelable.Creator<Action>() {
488             public Action createFromParcel(Parcel in) {
489                 return new Action(in);
490             }
491             public Action[] newArray(int size) {
492                 return new Action[size];
493             }
494         };
495     }
496 
497     private Action[] actions;
498 
499     /**
500      * Constructs a Notification object with default values.
501      * You might want to consider using {@link Builder} instead.
502      */
Notification()503     public Notification()
504     {
505         this.when = System.currentTimeMillis();
506         this.priority = PRIORITY_DEFAULT;
507     }
508 
509     /**
510      * @hide
511      */
Notification(Context context, int icon, CharSequence tickerText, long when, CharSequence contentTitle, CharSequence contentText, Intent contentIntent)512     public Notification(Context context, int icon, CharSequence tickerText, long when,
513             CharSequence contentTitle, CharSequence contentText, Intent contentIntent)
514     {
515         this.when = when;
516         this.icon = icon;
517         this.tickerText = tickerText;
518         setLatestEventInfo(context, contentTitle, contentText,
519                 PendingIntent.getActivity(context, 0, contentIntent, 0));
520     }
521 
522     /**
523      * Constructs a Notification object with the information needed to
524      * have a status bar icon without the standard expanded view.
525      *
526      * @param icon          The resource id of the icon to put in the status bar.
527      * @param tickerText    The text that flows by in the status bar when the notification first
528      *                      activates.
529      * @param when          The time to show in the time field.  In the System.currentTimeMillis
530      *                      timebase.
531      *
532      * @deprecated Use {@link Builder} instead.
533      */
534     @Deprecated
Notification(int icon, CharSequence tickerText, long when)535     public Notification(int icon, CharSequence tickerText, long when)
536     {
537         this.icon = icon;
538         this.tickerText = tickerText;
539         this.when = when;
540     }
541 
542     /**
543      * Unflatten the notification from a parcel.
544      */
Notification(Parcel parcel)545     public Notification(Parcel parcel)
546     {
547         int version = parcel.readInt();
548 
549         when = parcel.readLong();
550         icon = parcel.readInt();
551         number = parcel.readInt();
552         if (parcel.readInt() != 0) {
553             contentIntent = PendingIntent.CREATOR.createFromParcel(parcel);
554         }
555         if (parcel.readInt() != 0) {
556             deleteIntent = PendingIntent.CREATOR.createFromParcel(parcel);
557         }
558         if (parcel.readInt() != 0) {
559             tickerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
560         }
561         if (parcel.readInt() != 0) {
562             tickerView = RemoteViews.CREATOR.createFromParcel(parcel);
563         }
564         if (parcel.readInt() != 0) {
565             contentView = RemoteViews.CREATOR.createFromParcel(parcel);
566         }
567         if (parcel.readInt() != 0) {
568             largeIcon = Bitmap.CREATOR.createFromParcel(parcel);
569         }
570         defaults = parcel.readInt();
571         flags = parcel.readInt();
572         if (parcel.readInt() != 0) {
573             sound = Uri.CREATOR.createFromParcel(parcel);
574         }
575 
576         audioStreamType = parcel.readInt();
577         vibrate = parcel.createLongArray();
578         ledARGB = parcel.readInt();
579         ledOnMS = parcel.readInt();
580         ledOffMS = parcel.readInt();
581         iconLevel = parcel.readInt();
582 
583         if (parcel.readInt() != 0) {
584             fullScreenIntent = PendingIntent.CREATOR.createFromParcel(parcel);
585         }
586 
587         priority = parcel.readInt();
588 
589         kind = parcel.createStringArray(); // may set kind to null
590 
591         if (parcel.readInt() != 0) {
592             extras = parcel.readBundle();
593         }
594 
595         actions = parcel.createTypedArray(Action.CREATOR);
596         if (parcel.readInt() != 0) {
597             bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
598         }
599     }
600 
601     @Override
clone()602     public Notification clone() {
603         Notification that = new Notification();
604 
605         that.when = this.when;
606         that.icon = this.icon;
607         that.number = this.number;
608 
609         // PendingIntents are global, so there's no reason (or way) to clone them.
610         that.contentIntent = this.contentIntent;
611         that.deleteIntent = this.deleteIntent;
612         that.fullScreenIntent = this.fullScreenIntent;
613 
614         if (this.tickerText != null) {
615             that.tickerText = this.tickerText.toString();
616         }
617         if (this.tickerView != null) {
618             that.tickerView = this.tickerView.clone();
619         }
620         if (this.contentView != null) {
621             that.contentView = this.contentView.clone();
622         }
623         if (this.largeIcon != null) {
624             that.largeIcon = Bitmap.createBitmap(this.largeIcon);
625         }
626         that.iconLevel = this.iconLevel;
627         that.sound = this.sound; // android.net.Uri is immutable
628         that.audioStreamType = this.audioStreamType;
629 
630         final long[] vibrate = this.vibrate;
631         if (vibrate != null) {
632             final int N = vibrate.length;
633             final long[] vib = that.vibrate = new long[N];
634             System.arraycopy(vibrate, 0, vib, 0, N);
635         }
636 
637         that.ledARGB = this.ledARGB;
638         that.ledOnMS = this.ledOnMS;
639         that.ledOffMS = this.ledOffMS;
640         that.defaults = this.defaults;
641 
642         that.flags = this.flags;
643 
644         that.priority = this.priority;
645 
646         final String[] thiskind = this.kind;
647         if (thiskind != null) {
648             final int N = thiskind.length;
649             final String[] thatkind = that.kind = new String[N];
650             System.arraycopy(thiskind, 0, thatkind, 0, N);
651         }
652 
653         if (this.extras != null) {
654             that.extras = new Bundle(this.extras);
655 
656         }
657 
658         that.actions = new Action[this.actions.length];
659         for(int i=0; i<this.actions.length; i++) {
660             that.actions[i] = this.actions[i].clone();
661         }
662         if (this.bigContentView != null) {
663             that.bigContentView = this.bigContentView.clone();
664         }
665 
666         return that;
667     }
668 
describeContents()669     public int describeContents() {
670         return 0;
671     }
672 
673     /**
674      * Flatten this notification from a parcel.
675      */
writeToParcel(Parcel parcel, int flags)676     public void writeToParcel(Parcel parcel, int flags)
677     {
678         parcel.writeInt(1);
679 
680         parcel.writeLong(when);
681         parcel.writeInt(icon);
682         parcel.writeInt(number);
683         if (contentIntent != null) {
684             parcel.writeInt(1);
685             contentIntent.writeToParcel(parcel, 0);
686         } else {
687             parcel.writeInt(0);
688         }
689         if (deleteIntent != null) {
690             parcel.writeInt(1);
691             deleteIntent.writeToParcel(parcel, 0);
692         } else {
693             parcel.writeInt(0);
694         }
695         if (tickerText != null) {
696             parcel.writeInt(1);
697             TextUtils.writeToParcel(tickerText, parcel, flags);
698         } else {
699             parcel.writeInt(0);
700         }
701         if (tickerView != null) {
702             parcel.writeInt(1);
703             tickerView.writeToParcel(parcel, 0);
704         } else {
705             parcel.writeInt(0);
706         }
707         if (contentView != null) {
708             parcel.writeInt(1);
709             contentView.writeToParcel(parcel, 0);
710         } else {
711             parcel.writeInt(0);
712         }
713         if (largeIcon != null) {
714             parcel.writeInt(1);
715             largeIcon.writeToParcel(parcel, 0);
716         } else {
717             parcel.writeInt(0);
718         }
719 
720         parcel.writeInt(defaults);
721         parcel.writeInt(this.flags);
722 
723         if (sound != null) {
724             parcel.writeInt(1);
725             sound.writeToParcel(parcel, 0);
726         } else {
727             parcel.writeInt(0);
728         }
729         parcel.writeInt(audioStreamType);
730         parcel.writeLongArray(vibrate);
731         parcel.writeInt(ledARGB);
732         parcel.writeInt(ledOnMS);
733         parcel.writeInt(ledOffMS);
734         parcel.writeInt(iconLevel);
735 
736         if (fullScreenIntent != null) {
737             parcel.writeInt(1);
738             fullScreenIntent.writeToParcel(parcel, 0);
739         } else {
740             parcel.writeInt(0);
741         }
742 
743         parcel.writeInt(priority);
744 
745         parcel.writeStringArray(kind); // ok for null
746 
747         if (extras != null) {
748             parcel.writeInt(1);
749             extras.writeToParcel(parcel, 0);
750         } else {
751             parcel.writeInt(0);
752         }
753 
754         parcel.writeTypedArray(actions, 0);
755 
756         if (bigContentView != null) {
757             parcel.writeInt(1);
758             bigContentView.writeToParcel(parcel, 0);
759         } else {
760             parcel.writeInt(0);
761         }
762     }
763 
764     /**
765      * Parcelable.Creator that instantiates Notification objects
766      */
767     public static final Parcelable.Creator<Notification> CREATOR
768             = new Parcelable.Creator<Notification>()
769     {
770         public Notification createFromParcel(Parcel parcel)
771         {
772             return new Notification(parcel);
773         }
774 
775         public Notification[] newArray(int size)
776         {
777             return new Notification[size];
778         }
779     };
780 
781     /**
782      * Sets the {@link #contentView} field to be a view with the standard "Latest Event"
783      * layout.
784      *
785      * <p>Uses the {@link #icon} and {@link #when} fields to set the icon and time fields
786      * in the view.</p>
787      * @param context       The context for your application / activity.
788      * @param contentTitle The title that goes in the expanded entry.
789      * @param contentText  The text that goes in the expanded entry.
790      * @param contentIntent The intent to launch when the user clicks the expanded notification.
791      * If this is an activity, it must include the
792      * {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag, which requires
793      * that you take care of task management as described in the
794      * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back
795      * Stack</a> document.
796      *
797      * @deprecated Use {@link Builder} instead.
798      */
799     @Deprecated
setLatestEventInfo(Context context, CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent)800     public void setLatestEventInfo(Context context,
801             CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
802         // TODO: rewrite this to use Builder
803         RemoteViews contentView = new RemoteViews(context.getPackageName(),
804                 R.layout.notification_template_base);
805         if (this.icon != 0) {
806             contentView.setImageViewResource(R.id.icon, this.icon);
807         }
808         if (priority < PRIORITY_LOW) {
809             contentView.setInt(R.id.icon,
810                     "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
811             contentView.setInt(R.id.status_bar_latest_event_content,
812                     "setBackgroundResource", R.drawable.notification_bg_low);
813         }
814         if (contentTitle != null) {
815             contentView.setTextViewText(R.id.title, contentTitle);
816         }
817         if (contentText != null) {
818             contentView.setTextViewText(R.id.text, contentText);
819         }
820         if (this.when != 0) {
821             contentView.setViewVisibility(R.id.time, View.VISIBLE);
822             contentView.setLong(R.id.time, "setTime", when);
823         }
824         if (this.number != 0) {
825             NumberFormat f = NumberFormat.getIntegerInstance();
826             contentView.setTextViewText(R.id.info, f.format(this.number));
827         }
828 
829         this.contentView = contentView;
830         this.contentIntent = contentIntent;
831     }
832 
833     @Override
toString()834     public String toString() {
835         StringBuilder sb = new StringBuilder();
836         sb.append("Notification(pri=");
837         sb.append(priority);
838         sb.append(" contentView=");
839         if (contentView != null) {
840             sb.append(contentView.getPackage());
841             sb.append("/0x");
842             sb.append(Integer.toHexString(contentView.getLayoutId()));
843         } else {
844             sb.append("null");
845         }
846         // TODO(dsandler): defaults take precedence over local values, so reorder the branches below
847         sb.append(" vibrate=");
848         if (this.vibrate != null) {
849             int N = this.vibrate.length-1;
850             sb.append("[");
851             for (int i=0; i<N; i++) {
852                 sb.append(this.vibrate[i]);
853                 sb.append(',');
854             }
855             if (N != -1) {
856                 sb.append(this.vibrate[N]);
857             }
858             sb.append("]");
859         } else if ((this.defaults & DEFAULT_VIBRATE) != 0) {
860             sb.append("default");
861         } else {
862             sb.append("null");
863         }
864         sb.append(" sound=");
865         if (this.sound != null) {
866             sb.append(this.sound.toString());
867         } else if ((this.defaults & DEFAULT_SOUND) != 0) {
868             sb.append("default");
869         } else {
870             sb.append("null");
871         }
872         sb.append(" defaults=0x");
873         sb.append(Integer.toHexString(this.defaults));
874         sb.append(" flags=0x");
875         sb.append(Integer.toHexString(this.flags));
876         sb.append(" kind=[");
877         if (this.kind == null) {
878             sb.append("null");
879         } else {
880             for (int i=0; i<this.kind.length; i++) {
881                 if (i>0) sb.append(",");
882                 sb.append(this.kind[i]);
883             }
884         }
885         sb.append("]");
886         if (actions != null) {
887             sb.append(" ");
888             sb.append(actions.length);
889             sb.append(" action");
890             if (actions.length > 1) sb.append("s");
891         }
892         sb.append(")");
893         return sb.toString();
894     }
895 
896     /**
897      * Builder class for {@link Notification} objects.
898      *
899      * Provides a convenient way to set the various fields of a {@link Notification} and generate
900      * content views using the platform's notification layout template.
901      *
902      * Example:
903      *
904      * <pre class="prettyprint">
905      * Notification noti = new Notification.Builder()
906      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
907      *         .setContentText(subject)
908      *         .setSmallIcon(R.drawable.new_mail)
909      *         .setLargeIcon(aBitmap)
910      *         .build();
911      * </pre>
912      */
913     public static class Builder {
914         private static final int MAX_ACTION_BUTTONS = 3;
915 
916         private Context mContext;
917 
918         private long mWhen;
919         private int mSmallIcon;
920         private int mSmallIconLevel;
921         private int mNumber;
922         private CharSequence mContentTitle;
923         private CharSequence mContentText;
924         private CharSequence mContentInfo;
925         private CharSequence mSubText;
926         private PendingIntent mContentIntent;
927         private RemoteViews mContentView;
928         private PendingIntent mDeleteIntent;
929         private PendingIntent mFullScreenIntent;
930         private CharSequence mTickerText;
931         private RemoteViews mTickerView;
932         private Bitmap mLargeIcon;
933         private Uri mSound;
934         private int mAudioStreamType;
935         private long[] mVibrate;
936         private int mLedArgb;
937         private int mLedOnMs;
938         private int mLedOffMs;
939         private int mDefaults;
940         private int mFlags;
941         private int mProgressMax;
942         private int mProgress;
943         private boolean mProgressIndeterminate;
944         private ArrayList<String> mKindList = new ArrayList<String>(1);
945         private Bundle mExtras;
946         private int mPriority;
947         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
948         private boolean mUseChronometer;
949         private Style mStyle;
950 
951         /**
952          * Constructs a new Builder with the defaults:
953          *
954 
955          * <table>
956          * <tr><th align=right>priority</th>
957          *     <td>{@link #PRIORITY_DEFAULT}</td></tr>
958          * <tr><th align=right>when</th>
959          *     <td>now ({@link System#currentTimeMillis()})</td></tr>
960          * <tr><th align=right>audio stream</th>
961          *     <td>{@link #STREAM_DEFAULT}</td></tr>
962          * </table>
963          *
964 
965          * @param context
966          *            A {@link Context} that will be used by the Builder to construct the
967          *            RemoteViews. The Context will not be held past the lifetime of this Builder
968          *            object.
969          */
Builder(Context context)970         public Builder(Context context) {
971             mContext = context;
972 
973             // Set defaults to match the defaults of a Notification
974             mWhen = System.currentTimeMillis();
975             mAudioStreamType = STREAM_DEFAULT;
976             mPriority = PRIORITY_DEFAULT;
977         }
978 
979         /**
980          * Add a timestamp pertaining to the notification (usually the time the event occurred).
981          *
982 
983          * @see Notification#when
984          */
setWhen(long when)985         public Builder setWhen(long when) {
986             mWhen = when;
987             return this;
988         }
989 
990         /**
991          * Show the {@link Notification#when} field as a stopwatch.
992          *
993          * Instead of presenting <code>when</code> as a timestamp, the notification will show an
994          * automatically updating display of the minutes and seconds since <code>when</code>.
995          *
996          * Useful when showing an elapsed time (like an ongoing phone call).
997          *
998          * @see android.widget.Chronometer
999          * @see Notification#when
1000          */
setUsesChronometer(boolean b)1001         public Builder setUsesChronometer(boolean b) {
1002             mUseChronometer = b;
1003             return this;
1004         }
1005 
1006         /**
1007          * Set the small icon resource, which will be used to represent the notification in the
1008          * status bar.
1009          *
1010 
1011          * The platform template for the expanded view will draw this icon in the left, unless a
1012          * {@link #setLargeIcon(Bitmap) large icon} has also been specified, in which case the small
1013          * icon will be moved to the right-hand side.
1014          *
1015 
1016          * @param icon
1017          *            A resource ID in the application's package of the drawable to use.
1018          * @see Notification#icon
1019          */
setSmallIcon(int icon)1020         public Builder setSmallIcon(int icon) {
1021             mSmallIcon = icon;
1022             return this;
1023         }
1024 
1025         /**
1026          * A variant of {@link #setSmallIcon(int) setSmallIcon(int)} that takes an additional
1027          * level parameter for when the icon is a {@link android.graphics.drawable.LevelListDrawable
1028          * LevelListDrawable}.
1029          *
1030          * @param icon A resource ID in the application's package of the drawable to use.
1031          * @param level The level to use for the icon.
1032          *
1033          * @see Notification#icon
1034          * @see Notification#iconLevel
1035          */
setSmallIcon(int icon, int level)1036         public Builder setSmallIcon(int icon, int level) {
1037             mSmallIcon = icon;
1038             mSmallIconLevel = level;
1039             return this;
1040         }
1041 
1042         /**
1043          * Set the first line of text in the platform notification template.
1044          */
setContentTitle(CharSequence title)1045         public Builder setContentTitle(CharSequence title) {
1046             mContentTitle = title;
1047             return this;
1048         }
1049 
1050         /**
1051          * Set the second line of text in the platform notification template.
1052          */
setContentText(CharSequence text)1053         public Builder setContentText(CharSequence text) {
1054             mContentText = text;
1055             return this;
1056         }
1057 
1058         /**
1059          * Set the third line of text in the platform notification template.
1060          * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the same location in the standard template.
1061          */
setSubText(CharSequence text)1062         public Builder setSubText(CharSequence text) {
1063             mSubText = text;
1064             return this;
1065         }
1066 
1067         /**
1068          * Set the large number at the right-hand side of the notification.  This is
1069          * equivalent to setContentInfo, although it might show the number in a different
1070          * font size for readability.
1071          */
setNumber(int number)1072         public Builder setNumber(int number) {
1073             mNumber = number;
1074             return this;
1075         }
1076 
1077         /**
1078          * A small piece of additional information pertaining to this notification.
1079          *
1080          * The platform template will draw this on the last line of the notification, at the far
1081          * right (to the right of a smallIcon if it has been placed there).
1082          */
setContentInfo(CharSequence info)1083         public Builder setContentInfo(CharSequence info) {
1084             mContentInfo = info;
1085             return this;
1086         }
1087 
1088         /**
1089          * Set the progress this notification represents.
1090          *
1091          * The platform template will represent this using a {@link ProgressBar}.
1092          */
setProgress(int max, int progress, boolean indeterminate)1093         public Builder setProgress(int max, int progress, boolean indeterminate) {
1094             mProgressMax = max;
1095             mProgress = progress;
1096             mProgressIndeterminate = indeterminate;
1097             return this;
1098         }
1099 
1100         /**
1101          * Supply a custom RemoteViews to use instead of the platform template.
1102          *
1103          * @see Notification#contentView
1104          */
setContent(RemoteViews views)1105         public Builder setContent(RemoteViews views) {
1106             mContentView = views;
1107             return this;
1108         }
1109 
1110         /**
1111          * Supply a {@link PendingIntent} to be sent when the notification is clicked.
1112          *
1113          * As of {@link android.os.Build.VERSION_CODES#HONEYCOMB}, if this field is unset and you
1114          * have specified a custom RemoteViews with {@link #setContent(RemoteViews)}, you can use
1115          * {@link RemoteViews#setOnClickPendingIntent RemoteViews.setOnClickPendingIntent(int,PendingIntent)}
1116          * to assign PendingIntents to individual views in that custom layout (i.e., to create
1117          * clickable buttons inside the notification view).
1118          *
1119          * @see Notification#contentIntent Notification.contentIntent
1120          */
setContentIntent(PendingIntent intent)1121         public Builder setContentIntent(PendingIntent intent) {
1122             mContentIntent = intent;
1123             return this;
1124         }
1125 
1126         /**
1127          * Supply a {@link PendingIntent} to send when the notification is cleared explicitly by the user.
1128          *
1129          * @see Notification#deleteIntent
1130          */
setDeleteIntent(PendingIntent intent)1131         public Builder setDeleteIntent(PendingIntent intent) {
1132             mDeleteIntent = intent;
1133             return this;
1134         }
1135 
1136         /**
1137          * An intent to launch instead of posting the notification to the status bar.
1138          * Only for use with extremely high-priority notifications demanding the user's
1139          * <strong>immediate</strong> attention, such as an incoming phone call or
1140          * alarm clock that the user has explicitly set to a particular time.
1141          * If this facility is used for something else, please give the user an option
1142          * to turn it off and use a normal notification, as this can be extremely
1143          * disruptive.
1144          *
1145          * @param intent The pending intent to launch.
1146          * @param highPriority Passing true will cause this notification to be sent
1147          *          even if other notifications are suppressed.
1148          *
1149          * @see Notification#fullScreenIntent
1150          */
setFullScreenIntent(PendingIntent intent, boolean highPriority)1151         public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
1152             mFullScreenIntent = intent;
1153             setFlag(FLAG_HIGH_PRIORITY, highPriority);
1154             return this;
1155         }
1156 
1157         /**
1158          * Set the "ticker" text which is displayed in the status bar when the notification first
1159          * arrives.
1160          *
1161          * @see Notification#tickerText
1162          */
setTicker(CharSequence tickerText)1163         public Builder setTicker(CharSequence tickerText) {
1164             mTickerText = tickerText;
1165             return this;
1166         }
1167 
1168         /**
1169          * Set the text that is displayed in the status bar when the notification first
1170          * arrives, and also a RemoteViews object that may be displayed instead on some
1171          * devices.
1172          *
1173          * @see Notification#tickerText
1174          * @see Notification#tickerView
1175          */
setTicker(CharSequence tickerText, RemoteViews views)1176         public Builder setTicker(CharSequence tickerText, RemoteViews views) {
1177             mTickerText = tickerText;
1178             mTickerView = views;
1179             return this;
1180         }
1181 
1182         /**
1183          * Add a large icon to the notification (and the ticker on some devices).
1184          *
1185          * In the platform template, this image will be shown on the left of the notification view
1186          * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side).
1187          *
1188          * @see Notification#largeIcon
1189          */
setLargeIcon(Bitmap icon)1190         public Builder setLargeIcon(Bitmap icon) {
1191             mLargeIcon = icon;
1192             return this;
1193         }
1194 
1195         /**
1196          * Set the sound to play.
1197          *
1198          * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications.
1199          *
1200          * @see Notification#sound
1201          */
setSound(Uri sound)1202         public Builder setSound(Uri sound) {
1203             mSound = sound;
1204             mAudioStreamType = STREAM_DEFAULT;
1205             return this;
1206         }
1207 
1208         /**
1209          * Set the sound to play, along with a specific stream on which to play it.
1210          *
1211          * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
1212          *
1213          * @see Notification#sound
1214          */
setSound(Uri sound, int streamType)1215         public Builder setSound(Uri sound, int streamType) {
1216             mSound = sound;
1217             mAudioStreamType = streamType;
1218             return this;
1219         }
1220 
1221         /**
1222          * Set the vibration pattern to use.
1223          *
1224 
1225          * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
1226          * <code>pattern</code> parameter.
1227          *
1228 
1229          * @see Notification#vibrate
1230          */
setVibrate(long[] pattern)1231         public Builder setVibrate(long[] pattern) {
1232             mVibrate = pattern;
1233             return this;
1234         }
1235 
1236         /**
1237          * Set the desired color for the indicator LED on the device, as well as the
1238          * blink duty cycle (specified in milliseconds).
1239          *
1240 
1241          * Not all devices will honor all (or even any) of these values.
1242          *
1243 
1244          * @see Notification#ledARGB
1245          * @see Notification#ledOnMS
1246          * @see Notification#ledOffMS
1247          */
setLights(int argb, int onMs, int offMs)1248         public Builder setLights(int argb, int onMs, int offMs) {
1249             mLedArgb = argb;
1250             mLedOnMs = onMs;
1251             mLedOffMs = offMs;
1252             return this;
1253         }
1254 
1255         /**
1256          * Set whether this is an "ongoing" notification.
1257          *
1258 
1259          * Ongoing notifications cannot be dismissed by the user, so your application or service
1260          * must take care of canceling them.
1261          *
1262 
1263          * They are typically used to indicate a background task that the user is actively engaged
1264          * with (e.g., playing music) or is pending in some way and therefore occupying the device
1265          * (e.g., a file download, sync operation, active network connection).
1266          *
1267 
1268          * @see Notification#FLAG_ONGOING_EVENT
1269          * @see Service#setForeground(boolean)
1270          */
setOngoing(boolean ongoing)1271         public Builder setOngoing(boolean ongoing) {
1272             setFlag(FLAG_ONGOING_EVENT, ongoing);
1273             return this;
1274         }
1275 
1276         /**
1277          * Set this flag if you would only like the sound, vibrate
1278          * and ticker to be played if the notification is not already showing.
1279          *
1280          * @see Notification#FLAG_ONLY_ALERT_ONCE
1281          */
setOnlyAlertOnce(boolean onlyAlertOnce)1282         public Builder setOnlyAlertOnce(boolean onlyAlertOnce) {
1283             setFlag(FLAG_ONLY_ALERT_ONCE, onlyAlertOnce);
1284             return this;
1285         }
1286 
1287         /**
1288          * Make this notification automatically dismissed when the user touches it. The
1289          * PendingIntent set with {@link #setDeleteIntent} will be sent when this happens.
1290          *
1291          * @see Notification#FLAG_AUTO_CANCEL
1292          */
setAutoCancel(boolean autoCancel)1293         public Builder setAutoCancel(boolean autoCancel) {
1294             setFlag(FLAG_AUTO_CANCEL, autoCancel);
1295             return this;
1296         }
1297 
1298         /**
1299          * Set which notification properties will be inherited from system defaults.
1300          * <p>
1301          * The value should be one or more of the following fields combined with
1302          * bitwise-or:
1303          * {@link #DEFAULT_SOUND}, {@link #DEFAULT_VIBRATE}, {@link #DEFAULT_LIGHTS}.
1304          * <p>
1305          * For all default values, use {@link #DEFAULT_ALL}.
1306          */
setDefaults(int defaults)1307         public Builder setDefaults(int defaults) {
1308             mDefaults = defaults;
1309             return this;
1310         }
1311 
1312         /**
1313          * Set the priority of this notification.
1314          *
1315          * @see Notification#priority
1316          */
setPriority(int pri)1317         public Builder setPriority(int pri) {
1318             mPriority = pri;
1319             return this;
1320         }
1321 
1322         /**
1323          * @hide
1324          *
1325          * Add a kind (category) to this notification. Optional.
1326          *
1327          * @see Notification#kind
1328          */
addKind(String k)1329         public Builder addKind(String k) {
1330             mKindList.add(k);
1331             return this;
1332         }
1333 
1334         /**
1335          * Add metadata to this notification.
1336          *
1337          * A reference to the Bundle is held for the lifetime of this Builder, and the Bundle's
1338          * current contents are copied into the Notification each time {@link #build()} is
1339          * called.
1340          *
1341          * @see Notification#extras
1342          * @hide
1343          */
setExtras(Bundle bag)1344         public Builder setExtras(Bundle bag) {
1345             mExtras = bag;
1346             return this;
1347         }
1348 
1349         /**
1350          * Add an action to this notification. Actions are typically displayed by
1351          * the system as a button adjacent to the notification content.
1352          *
1353          * @param icon Resource ID of a drawable that represents the action.
1354          * @param title Text describing the action.
1355          * @param intent PendingIntent to be fired when the action is invoked.
1356          */
addAction(int icon, CharSequence title, PendingIntent intent)1357         public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
1358             mActions.add(new Action(icon, title, intent));
1359             return this;
1360         }
1361 
1362         /**
1363          * Add a rich notification style to be applied at build time.
1364          *
1365          * @param style Object responsible for modifying the notification style.
1366          */
setStyle(Style style)1367         public Builder setStyle(Style style) {
1368             if (mStyle != style) {
1369                 mStyle = style;
1370                 if (mStyle != null) {
1371                     mStyle.setBuilder(this);
1372                 }
1373             }
1374             return this;
1375         }
1376 
setFlag(int mask, boolean value)1377         private void setFlag(int mask, boolean value) {
1378             if (value) {
1379                 mFlags |= mask;
1380             } else {
1381                 mFlags &= ~mask;
1382             }
1383         }
1384 
applyStandardTemplate(int resId, boolean fitIn1U)1385         private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
1386             RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
1387             boolean showLine3 = false;
1388             boolean showLine2 = false;
1389             int smallIconImageViewId = R.id.icon;
1390             if (mLargeIcon != null) {
1391                 contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
1392                 smallIconImageViewId = R.id.right_icon;
1393             }
1394             if (mPriority < PRIORITY_LOW) {
1395                 contentView.setInt(R.id.icon,
1396                         "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
1397                 contentView.setInt(R.id.status_bar_latest_event_content,
1398                         "setBackgroundResource", R.drawable.notification_bg_low);
1399             }
1400             if (mSmallIcon != 0) {
1401                 contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
1402                 contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
1403             } else {
1404                 contentView.setViewVisibility(smallIconImageViewId, View.GONE);
1405             }
1406             if (mContentTitle != null) {
1407                 contentView.setTextViewText(R.id.title, mContentTitle);
1408             }
1409             if (mContentText != null) {
1410                 contentView.setTextViewText(R.id.text, mContentText);
1411                 showLine3 = true;
1412             }
1413             if (mContentInfo != null) {
1414                 contentView.setTextViewText(R.id.info, mContentInfo);
1415                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
1416                 showLine3 = true;
1417             } else if (mNumber > 0) {
1418                 final int tooBig = mContext.getResources().getInteger(
1419                         R.integer.status_bar_notification_info_maxnum);
1420                 if (mNumber > tooBig) {
1421                     contentView.setTextViewText(R.id.info, mContext.getResources().getString(
1422                                 R.string.status_bar_notification_info_overflow));
1423                 } else {
1424                     NumberFormat f = NumberFormat.getIntegerInstance();
1425                     contentView.setTextViewText(R.id.info, f.format(mNumber));
1426                 }
1427                 contentView.setViewVisibility(R.id.info, View.VISIBLE);
1428                 showLine3 = true;
1429             } else {
1430                 contentView.setViewVisibility(R.id.info, View.GONE);
1431             }
1432 
1433             // Need to show three lines?
1434             if (mSubText != null) {
1435                 contentView.setTextViewText(R.id.text, mSubText);
1436                 if (mContentText != null) {
1437                     contentView.setTextViewText(R.id.text2, mContentText);
1438                     contentView.setViewVisibility(R.id.text2, View.VISIBLE);
1439                     showLine2 = true;
1440                 } else {
1441                     contentView.setViewVisibility(R.id.text2, View.GONE);
1442                 }
1443             } else {
1444                 contentView.setViewVisibility(R.id.text2, View.GONE);
1445                 if (mProgressMax != 0 || mProgressIndeterminate) {
1446                     contentView.setProgressBar(
1447                             R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
1448                     contentView.setViewVisibility(R.id.progress, View.VISIBLE);
1449                     showLine2 = true;
1450                 } else {
1451                     contentView.setViewVisibility(R.id.progress, View.GONE);
1452                 }
1453             }
1454             if (showLine2) {
1455                 if (fitIn1U) {
1456                     // need to shrink all the type to make sure everything fits
1457                     final Resources res = mContext.getResources();
1458                     final float subTextSize = res.getDimensionPixelSize(
1459                             R.dimen.notification_subtext_size);
1460                     contentView.setTextViewTextSize(R.id.text, TypedValue.COMPLEX_UNIT_PX, subTextSize);
1461                 }
1462                 // vertical centering
1463                 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
1464             }
1465 
1466             if (mWhen != 0) {
1467                 if (mUseChronometer) {
1468                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
1469                     contentView.setLong(R.id.chronometer, "setBase",
1470                             mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
1471                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
1472                 } else {
1473                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
1474                     contentView.setLong(R.id.time, "setTime", mWhen);
1475                 }
1476             }
1477             contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
1478             contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
1479             return contentView;
1480         }
1481 
applyStandardTemplateWithActions(int layoutId)1482         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
1483             RemoteViews big = applyStandardTemplate(layoutId, false);
1484 
1485             int N = mActions.size();
1486             if (N > 0) {
1487                 // Log.d("Notification", "has actions: " + mContentText);
1488                 big.setViewVisibility(R.id.actions, View.VISIBLE);
1489                 big.setViewVisibility(R.id.action_divider, View.VISIBLE);
1490                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
1491                 big.removeAllViews(R.id.actions);
1492                 for (int i=0; i<N; i++) {
1493                     final RemoteViews button = generateActionButton(mActions.get(i));
1494                     //Log.d("Notification", "adding action " + i + ": " + mActions.get(i).title);
1495                     big.addView(R.id.actions, button);
1496                 }
1497             }
1498             return big;
1499         }
1500 
makeContentView()1501         private RemoteViews makeContentView() {
1502             if (mContentView != null) {
1503                 return mContentView;
1504             } else {
1505                 return applyStandardTemplate(R.layout.notification_template_base, true); // no more special large_icon flavor
1506             }
1507         }
1508 
makeTickerView()1509         private RemoteViews makeTickerView() {
1510             if (mTickerView != null) {
1511                 return mTickerView;
1512             } else {
1513                 if (mContentView == null) {
1514                     return applyStandardTemplate(mLargeIcon == null
1515                             ? R.layout.status_bar_latest_event_ticker
1516                             : R.layout.status_bar_latest_event_ticker_large_icon, true);
1517                 } else {
1518                     return null;
1519                 }
1520             }
1521         }
1522 
makeBigContentView()1523         private RemoteViews makeBigContentView() {
1524             if (mActions.size() == 0) return null;
1525 
1526             return applyStandardTemplateWithActions(R.layout.notification_template_big_base);
1527         }
1528 
generateActionButton(Action action)1529         private RemoteViews generateActionButton(Action action) {
1530             final boolean tombstone = (action.actionIntent == null);
1531             RemoteViews button = new RemoteViews(mContext.getPackageName(),
1532                     tombstone ? R.layout.notification_action_tombstone
1533                               : R.layout.notification_action);
1534             button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0);
1535             button.setTextViewText(R.id.action0, action.title);
1536             if (!tombstone) {
1537                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
1538             }
1539             button.setContentDescription(R.id.action0, action.title);
1540             return button;
1541         }
1542 
1543         /**
1544          * Apply the unstyled operations and return a new {@link Notification} object.
1545          */
buildUnstyled()1546         private Notification buildUnstyled() {
1547             Notification n = new Notification();
1548             n.when = mWhen;
1549             n.icon = mSmallIcon;
1550             n.iconLevel = mSmallIconLevel;
1551             n.number = mNumber;
1552             n.contentView = makeContentView();
1553             n.contentIntent = mContentIntent;
1554             n.deleteIntent = mDeleteIntent;
1555             n.fullScreenIntent = mFullScreenIntent;
1556             n.tickerText = mTickerText;
1557             n.tickerView = makeTickerView();
1558             n.largeIcon = mLargeIcon;
1559             n.sound = mSound;
1560             n.audioStreamType = mAudioStreamType;
1561             n.vibrate = mVibrate;
1562             n.ledARGB = mLedArgb;
1563             n.ledOnMS = mLedOnMs;
1564             n.ledOffMS = mLedOffMs;
1565             n.defaults = mDefaults;
1566             n.flags = mFlags;
1567             n.bigContentView = makeBigContentView();
1568             if (mLedOnMs != 0 && mLedOffMs != 0) {
1569                 n.flags |= FLAG_SHOW_LIGHTS;
1570             }
1571             if ((mDefaults & DEFAULT_LIGHTS) != 0) {
1572                 n.flags |= FLAG_SHOW_LIGHTS;
1573             }
1574             if (mKindList.size() > 0) {
1575                 n.kind = new String[mKindList.size()];
1576                 mKindList.toArray(n.kind);
1577             } else {
1578                 n.kind = null;
1579             }
1580             n.priority = mPriority;
1581             n.extras = mExtras != null ? new Bundle(mExtras) : null;
1582             if (mActions.size() > 0) {
1583                 n.actions = new Action[mActions.size()];
1584                 mActions.toArray(n.actions);
1585             }
1586             return n;
1587         }
1588 
1589         /**
1590          * @deprecated Use {@link #build()} instead.
1591          */
1592         @Deprecated
getNotification()1593         public Notification getNotification() {
1594             return build();
1595         }
1596 
1597         /**
1598          * Combine all of the options that have been set and return a new {@link Notification}
1599          * object.
1600          */
build()1601         public Notification build() {
1602             if (mStyle != null) {
1603                 return mStyle.build();
1604             } else {
1605                 return buildUnstyled();
1606             }
1607         }
1608     }
1609 
1610 
1611     /**
1612      * An object that can apply a rich notification style to a {@link Notification.Builder}
1613      * object.
1614      */
1615     public static abstract class Style
1616     {
1617         private CharSequence mBigContentTitle;
1618         private CharSequence mSummaryText = null;
1619         private boolean mSummaryTextSet = false;
1620 
1621         protected Builder mBuilder;
1622 
1623         /**
1624          * Overrides ContentTitle in the big form of the template.
1625          * This defaults to the value passed to setContentTitle().
1626          */
internalSetBigContentTitle(CharSequence title)1627         protected void internalSetBigContentTitle(CharSequence title) {
1628             mBigContentTitle = title;
1629         }
1630 
1631         /**
1632          * Set the first line of text after the detail section in the big form of the template.
1633          */
internalSetSummaryText(CharSequence cs)1634         protected void internalSetSummaryText(CharSequence cs) {
1635             mSummaryText = cs;
1636             mSummaryTextSet = true;
1637         }
1638 
setBuilder(Builder builder)1639         public void setBuilder(Builder builder) {
1640             if (mBuilder != builder) {
1641                 mBuilder = builder;
1642                 if (mBuilder != null) {
1643                     mBuilder.setStyle(this);
1644                 }
1645             }
1646         }
1647 
checkBuilder()1648         protected void checkBuilder() {
1649             if (mBuilder == null) {
1650                 throw new IllegalArgumentException("Style requires a valid Builder object");
1651             }
1652         }
1653 
getStandardView(int layoutId)1654         protected RemoteViews getStandardView(int layoutId) {
1655             checkBuilder();
1656 
1657             if (mBigContentTitle != null) {
1658                 mBuilder.setContentTitle(mBigContentTitle);
1659             }
1660 
1661             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
1662 
1663             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
1664                 contentView.setViewVisibility(R.id.line1, View.GONE);
1665             } else {
1666                 contentView.setViewVisibility(R.id.line1, View.VISIBLE);
1667             }
1668 
1669             // The last line defaults to the subtext, but can be replaced by mSummaryText
1670             final CharSequence overflowText =
1671                     mSummaryTextSet ? mSummaryText
1672                                     : mBuilder.mSubText;
1673             if (overflowText != null) {
1674                 contentView.setTextViewText(R.id.text, overflowText);
1675                 contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
1676                 contentView.setViewVisibility(R.id.line3, View.VISIBLE);
1677             } else {
1678                 contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
1679                 contentView.setViewVisibility(R.id.line3, View.GONE);
1680             }
1681 
1682             return contentView;
1683         }
1684 
build()1685         public abstract Notification build();
1686     }
1687 
1688     /**
1689      * Helper class for generating large-format notifications that include a large image attachment.
1690      *
1691      * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
1692      * <pre class="prettyprint">
1693      * Notification noti = new Notification.BigPictureStyle(
1694      *      new Notification.Builder()
1695      *         .setContentTitle(&quot;New photo from &quot; + sender.toString())
1696      *         .setContentText(subject)
1697      *         .setSmallIcon(R.drawable.new_post)
1698      *         .setLargeIcon(aBitmap))
1699      *      .bigPicture(aBigBitmap)
1700      *      .build();
1701      * </pre>
1702      *
1703      * @see Notification#bigContentView
1704      */
1705     public static class BigPictureStyle extends Style {
1706         private Bitmap mPicture;
1707         private Bitmap mBigLargeIcon;
1708         private boolean mBigLargeIconSet = false;
1709 
BigPictureStyle()1710         public BigPictureStyle() {
1711         }
1712 
BigPictureStyle(Builder builder)1713         public BigPictureStyle(Builder builder) {
1714             setBuilder(builder);
1715         }
1716 
1717         /**
1718          * Overrides ContentTitle in the big form of the template.
1719          * This defaults to the value passed to setContentTitle().
1720          */
setBigContentTitle(CharSequence title)1721         public BigPictureStyle setBigContentTitle(CharSequence title) {
1722             internalSetBigContentTitle(title);
1723             return this;
1724         }
1725 
1726         /**
1727          * Set the first line of text after the detail section in the big form of the template.
1728          */
setSummaryText(CharSequence cs)1729         public BigPictureStyle setSummaryText(CharSequence cs) {
1730             internalSetSummaryText(cs);
1731             return this;
1732         }
1733 
bigPicture(Bitmap b)1734         public BigPictureStyle bigPicture(Bitmap b) {
1735             mPicture = b;
1736             return this;
1737         }
1738 
1739         /**
1740          * Override the large icon when the big notification is shown.
1741          */
bigLargeIcon(Bitmap b)1742         public BigPictureStyle bigLargeIcon(Bitmap b) {
1743             mBigLargeIconSet = true;
1744             mBigLargeIcon = b;
1745             return this;
1746         }
1747 
makeBigContentView()1748         private RemoteViews makeBigContentView() {
1749             RemoteViews contentView = getStandardView(R.layout.notification_template_big_picture);
1750 
1751             contentView.setImageViewBitmap(R.id.big_picture, mPicture);
1752 
1753             return contentView;
1754         }
1755 
1756         @Override
build()1757         public Notification build() {
1758             checkBuilder();
1759             Notification wip = mBuilder.buildUnstyled();
1760             if (mBigLargeIconSet ) {
1761                 mBuilder.mLargeIcon = mBigLargeIcon;
1762             }
1763             wip.bigContentView = makeBigContentView();
1764             return wip;
1765         }
1766     }
1767 
1768     /**
1769      * Helper class for generating large-format notifications that include a lot of text.
1770      *
1771      * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
1772      * <pre class="prettyprint">
1773      * Notification noti = new Notification.BigPictureStyle(
1774      *      new Notification.Builder()
1775      *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
1776      *         .setContentText(subject)
1777      *         .setSmallIcon(R.drawable.new_mail)
1778      *         .setLargeIcon(aBitmap))
1779      *      .bigText(aVeryLongString)
1780      *      .build();
1781      * </pre>
1782      *
1783      * @see Notification#bigContentView
1784      */
1785     public static class BigTextStyle extends Style {
1786         private CharSequence mBigText;
1787 
BigTextStyle()1788         public BigTextStyle() {
1789         }
1790 
BigTextStyle(Builder builder)1791         public BigTextStyle(Builder builder) {
1792             setBuilder(builder);
1793         }
1794 
1795         /**
1796          * Overrides ContentTitle in the big form of the template.
1797          * This defaults to the value passed to setContentTitle().
1798          */
setBigContentTitle(CharSequence title)1799         public BigTextStyle setBigContentTitle(CharSequence title) {
1800             internalSetBigContentTitle(title);
1801             return this;
1802         }
1803 
1804         /**
1805          * Set the first line of text after the detail section in the big form of the template.
1806          */
setSummaryText(CharSequence cs)1807         public BigTextStyle setSummaryText(CharSequence cs) {
1808             internalSetSummaryText(cs);
1809             return this;
1810         }
1811 
bigText(CharSequence cs)1812         public BigTextStyle bigText(CharSequence cs) {
1813             mBigText = cs;
1814             return this;
1815         }
1816 
makeBigContentView()1817         private RemoteViews makeBigContentView() {
1818             // Remove the content text so line3 only shows if you have a summary
1819             final boolean hadThreeLines = (mBuilder.mContentText != null && mBuilder.mSubText != null);
1820             mBuilder.mContentText = null;
1821 
1822             RemoteViews contentView = getStandardView(R.layout.notification_template_big_text);
1823 
1824             if (hadThreeLines) {
1825                 // vertical centering
1826                 contentView.setViewPadding(R.id.line1, 0, 0, 0, 0);
1827             }
1828 
1829             contentView.setTextViewText(R.id.big_text, mBigText);
1830             contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
1831             contentView.setViewVisibility(R.id.text2, View.GONE);
1832 
1833             return contentView;
1834         }
1835 
1836         @Override
build()1837         public Notification build() {
1838             checkBuilder();
1839             Notification wip = mBuilder.buildUnstyled();
1840             wip.bigContentView = makeBigContentView();
1841             return wip;
1842         }
1843     }
1844 
1845     /**
1846      * Helper class for generating large-format notifications that include a list of (up to 5) strings.
1847      *
1848      * This class is a "rebuilder": It consumes a Builder object and modifies its behavior, like so:
1849      * <pre class="prettyprint">
1850      * Notification noti = new Notification.InboxStyle(
1851      *      new Notification.Builder()
1852      *         .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
1853      *         .setContentText(subject)
1854      *         .setSmallIcon(R.drawable.new_mail)
1855      *         .setLargeIcon(aBitmap))
1856      *      .addLine(str1)
1857      *      .addLine(str2)
1858      *      .setContentTitle("")
1859      *      .setSummaryText(&quot;+3 more&quot;)
1860      *      .build();
1861      * </pre>
1862      *
1863      * @see Notification#bigContentView
1864      */
1865     public static class InboxStyle extends Style {
1866         private ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(5);
1867 
InboxStyle()1868         public InboxStyle() {
1869         }
1870 
InboxStyle(Builder builder)1871         public InboxStyle(Builder builder) {
1872             setBuilder(builder);
1873         }
1874 
1875         /**
1876          * Overrides ContentTitle in the big form of the template.
1877          * This defaults to the value passed to setContentTitle().
1878          */
setBigContentTitle(CharSequence title)1879         public InboxStyle setBigContentTitle(CharSequence title) {
1880             internalSetBigContentTitle(title);
1881             return this;
1882         }
1883 
1884         /**
1885          * Set the first line of text after the detail section in the big form of the template.
1886          */
setSummaryText(CharSequence cs)1887         public InboxStyle setSummaryText(CharSequence cs) {
1888             internalSetSummaryText(cs);
1889             return this;
1890         }
1891 
addLine(CharSequence cs)1892         public InboxStyle addLine(CharSequence cs) {
1893             mTexts.add(cs);
1894             return this;
1895         }
1896 
makeBigContentView()1897         private RemoteViews makeBigContentView() {
1898             // Remove the content text so line3 disappears unless you have a summary
1899             mBuilder.mContentText = null;
1900             RemoteViews contentView = getStandardView(R.layout.notification_template_inbox);
1901 
1902             contentView.setViewVisibility(R.id.text2, View.GONE);
1903 
1904             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
1905                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
1906 
1907             // Make sure all rows are gone in case we reuse a view.
1908             for (int rowId : rowIds) {
1909                 contentView.setViewVisibility(rowId, View.GONE);
1910             }
1911 
1912             int i=0;
1913             while (i < mTexts.size() && i < rowIds.length) {
1914                 CharSequence str = mTexts.get(i);
1915                 if (str != null && !str.equals("")) {
1916                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
1917                     contentView.setTextViewText(rowIds[i], str);
1918                 }
1919                 i++;
1920             }
1921 
1922             if  (mTexts.size() > rowIds.length) {
1923                 contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE);
1924             } else {
1925                 contentView.setViewVisibility(R.id.inbox_more, View.GONE);
1926             }
1927 
1928             return contentView;
1929         }
1930 
1931         @Override
build()1932         public Notification build() {
1933             checkBuilder();
1934             Notification wip = mBuilder.buildUnstyled();
1935             wip.bigContentView = makeBigContentView();
1936             return wip;
1937         }
1938     }
1939 }
1940