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