Add v16 Notification APIs to NotificationCompat.

As before, we attempt to expose the superset of all
Notification.Builder APIs; where features from the newer
APIs apply to the API level available, we support that
behavior, otherwise we drop it.

This change also has some minor documentation updates that
should be mirrored in the framework.

Bug: 6316809
Change-Id: Ie57625e8bbecae87139f06007b6edd36deee95b1
diff --git a/v4/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java b/v4/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
new file mode 100644
index 0000000..3c74842
--- /dev/null
+++ b/v4/ics/android/support/v4/app/NotificationCompatIceCreamSandwich.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.widget.RemoteViews;
+
+class NotificationCompatIceCreamSandwich {
+    static Notification add(Context context, Notification n,
+            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
+            RemoteViews tickerView, int number,
+            PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
+            int mProgressMax, int mProgress, boolean mProgressIndeterminate) {
+        Notification.Builder b = new Notification.Builder(context)
+                .setWhen(n.when)
+                .setSmallIcon(n.icon, n.iconLevel)
+                .setContent(n.contentView)
+                .setTicker(n.tickerText, tickerView)
+                .setSound(n.sound, n.audioStreamType)
+                .setVibrate(n.vibrate)
+                .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+                .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+                .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+                .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+                .setDefaults(n.defaults)
+                .setContentTitle(contentTitle)
+                .setContentText(contentText)
+                .setContentInfo(contentInfo)
+                .setContentIntent(contentIntent)
+                .setDeleteIntent(n.deleteIntent)
+                .setFullScreenIntent(fullScreenIntent,
+                        (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+                .setLargeIcon(largeIcon)
+                .setNumber(number)
+                .setProgress(mProgressMax, mProgress, mProgressIndeterminate);
+
+        return b.getNotification();
+    }
+}
diff --git a/v4/java/android/support/v4/app/NotificationCompat.java b/v4/java/android/support/v4/app/NotificationCompat.java
index 71ec14e..482d3d8 100644
--- a/v4/java/android/support/v4/app/NotificationCompat.java
+++ b/v4/java/android/support/v4/app/NotificationCompat.java
@@ -25,43 +25,130 @@
 import android.net.Uri;
 import android.os.Build;
 import android.widget.RemoteViews;
+import java.util.ArrayList;
 
 public class NotificationCompat {
     /**
-     * Bit to be bitwise-ored into the {@link Notification#flags} field that should be set if
-     * this notification represents a high-priority event that may be shown to the user even
-     * if notifications are otherwise unavailable (that is, when the status bar is hidden).
-     * This flag is ideally used in conjunction with fullScreenIntent.
-     *
-     * <p>This will only be respected on API level 9 and above.</p>
+     * Obsolete flag indicating high-priority notifications; use the priority field instead.
+     * 
+     * @deprecated Use {@link NotificationCompat.Builder#setPriority(int)} with a positive value.
      */
     public static final int FLAG_HIGH_PRIORITY = 0x00000080;
 
+    /**
+     * Default notification priority for {@link NotificationCompat.Builder#setPriority(int)}.
+     * If your application does not prioritize its own notifications,
+     * use this value for all notifications.
+     */
+    public static final int PRIORITY_DEFAULT = 0;
+
+    /**
+     * Lower notification priority for {@link NotificationCompat.Builder#setPriority(int)},
+     * for items that are less important. The UI may choose to show
+     * these items smaller, or at a different position in the list,
+     * compared with your app's {@link #PRIORITY_DEFAULT} items.
+     */
+    public static final int PRIORITY_LOW = -1;
+
+    /**
+     * Lowest notification priority for {@link NotificationCompat.Builder#setPriority(int)};
+     * these items might not be shown to the user except under
+     * special circumstances, such as detailed notification logs.
+     */
+    public static final int PRIORITY_MIN = -2;
+
+    /**
+     * Higher notification priority for {@link NotificationCompat.Builder#setPriority(int)},
+     * for more important notifications or alerts. The UI may choose
+     * to show these items larger, or at a different position in
+     * notification lists, compared with your app's {@link #PRIORITY_DEFAULT} items.
+     */
+    public static final int PRIORITY_HIGH = 1;
+
+    /**
+     * Highest notification priority for {@link NotificationCompat.Builder#setPriority(int)},
+     * for your application's most important items that require the user's
+     * prompt attention or input.
+     */
+    public static final int PRIORITY_MAX = 2;
+
     private static final NotificationCompatImpl IMPL;
 
     interface NotificationCompatImpl {
-        public Notification getNotification(Builder b);
+        public Notification build(Builder b);
     }
 
     static class NotificationCompatImplBase implements NotificationCompatImpl {
-        public Notification getNotification(Builder b) {
+        public Notification build(Builder b) {
             Notification result = (Notification) b.mNotification;
             result.setLatestEventInfo(b.mContext, b.mContentTitle,
                     b.mContentText, b.mContentIntent);
+            // translate high priority requests into legacy flag
+            if (b.mPriority > PRIORITY_DEFAULT) {
+                result.flags |= FLAG_HIGH_PRIORITY; 
+            }
             return result;
         }
     }
 
     static class NotificationCompatImplHoneycomb implements NotificationCompatImpl {
-        public Notification getNotification(Builder b) {
+        public Notification build(Builder b) {
             return NotificationCompatHoneycomb.add(b.mContext, b.mNotification,
                     b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
                     b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon);
         }
     }
 
+    static class NotificationCompatImplIceCreamSandwich implements NotificationCompatImpl {
+        public Notification build(Builder b) {
+            return NotificationCompatIceCreamSandwich.add(b.mContext, b.mNotification,
+                    b.mContentTitle, b.mContentText, b.mContentInfo, b.mTickerView,
+                    b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
+                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate);
+        }
+    }
+
+    static class NotificationCompatImplJellybean implements NotificationCompatImpl {
+        public Notification build(Builder b) {
+            NotificationCompatJellybean jbBuilder = new NotificationCompatJellybean(
+                    b.mContext, b.mNotification, b.mContentTitle, b.mContentText, b.mContentInfo,
+                    b.mTickerView, b.mNumber, b.mContentIntent, b.mFullScreenIntent, b.mLargeIcon,
+                    b.mProgressMax, b.mProgress, b.mProgressIndeterminate,
+                    b.mUseChronometer, b.mPriority, b.mSubText);
+            for (Action action: b.mActions) {
+                jbBuilder.addAction(action.icon, action.title, action.actionIntent);
+            }
+            if (b.mStyle != null) {
+                if (b.mStyle instanceof BigTextStyle) {
+                    BigTextStyle style = (BigTextStyle) b.mStyle;
+                    jbBuilder.addBigTextStyle(style.mBigContentTitle,
+                            style.mSummaryTextSet, 
+                            style.mSummaryText,
+                            style.mBigText);
+                } else if (b.mStyle instanceof InboxStyle) {
+                    InboxStyle style = (InboxStyle) b.mStyle;
+                    jbBuilder.addInboxStyle(style.mBigContentTitle,
+                            style.mSummaryTextSet, 
+                            style.mSummaryText,
+                            style.mTexts);
+                } else if (b.mStyle instanceof BigPictureStyle) {
+                    BigPictureStyle style = (BigPictureStyle) b.mStyle;
+                    jbBuilder.addBigPictureStyle(style.mBigContentTitle,
+                            style.mSummaryTextSet, 
+                            style.mSummaryText,
+                            style.mPicture);
+                }
+            }
+            return(jbBuilder.build());
+        }
+    }
+
     static {
-        if (Build.VERSION.SDK_INT >= 11) {
+        if (Build.VERSION.SDK_INT >= 16) {
+            IMPL = new NotificationCompatImplJellybean();
+        } else if (Build.VERSION.SDK_INT >= 13) {
+            IMPL = new NotificationCompatImplIceCreamSandwich();
+        } else if (Build.VERSION.SDK_INT >= 11) {
             IMPL = new NotificationCompatImplHoneycomb();
         } else {
             IMPL = new NotificationCompatImplBase();
@@ -83,6 +170,14 @@
         Bitmap mLargeIcon;
         CharSequence mContentInfo;
         int mNumber;
+        int mPriority;
+        boolean mUseChronometer;
+        Style mStyle;
+        CharSequence mSubText;
+        int mProgressMax;
+        int mProgress;
+        boolean mProgressIndeterminate;
+        ArrayList<Action> mActions = new ArrayList<Action>();
 
         Notification mNotification = new Notification();
 
@@ -103,6 +198,7 @@
             // Set defaults to match the defaults of a Notification
             mNotification.when = System.currentTimeMillis();
             mNotification.audioStreamType = Notification.STREAM_DEFAULT;
+            mPriority = PRIORITY_DEFAULT;
         }
 
         /**
@@ -115,6 +211,22 @@
         }
 
         /**
+         * Show the {@link Notification#when} field as a stopwatch.
+         * 
+         * Instead of presenting <code>when</code> as a timestamp, the notification will show an 
+         * automatically updating display of the minutes and seconds since <code>when</code>.
+         *
+         * Useful when showing an elapsed time (like an ongoing phone call).
+         *
+         * @see android.widget.Chronometer
+         * @see Notification#when
+         */
+        public Builder setUsesChronometer(boolean b) {
+            mUseChronometer = b;
+            return this;
+        }
+
+        /**
          * Set the small icon to use in the notification layouts.  Different classes of devices
          * may return different sizes.  See the UX guidelines for more information on how to
          * design these icons.
@@ -159,6 +271,16 @@
         }
 
         /**
+         * Set the third line of text in the platform notification template. 
+         * Don't use if you're also using {@link #setProgress(int, int, boolean)};
+         * they occupy the same location in the standard template.
+         */
+        public Builder setSubText(CharSequence text) {
+            mSubText = text;
+            return this;
+        }
+
+        /**
          * Set the large number at the right-hand side of the notification.  This is
          * equivalent to setContentInfo, although it might show the number in a different
          * font size for readability.
@@ -178,15 +300,14 @@
 
         /**
          * Set the progress this notification represents, which may be
-         * represented as a {@link ProgressBar}.
+         * represented as a {@link android.widget.ProgressBar}.
          */
-        /* TODO
         public Builder setProgress(int max, int progress, boolean indeterminate) {
             mProgressMax = max;
             mProgress = progress;
             mProgressIndeterminate = indeterminate;
             return this;
-        }*/
+        }
 
         /**
          * Supply a custom RemoteViews to use instead of the standard one.
@@ -378,11 +499,260 @@
         }
 
         /**
+         * Set the relative priority for this notification.
+         * 
+         * Priority is an indication of how much of the user's
+         * valuable attention should be consumed by this
+         * notification. Low-priority notifications may be hidden from
+         * the user in certain situations, while the user might be
+         * interrupted for a higher-priority notification. The system
+         * will make a determination about how to interpret
+         * notification priority as described in MUMBLE MUMBLE.
+         */
+        public Builder setPriority(int pri) {
+            mPriority = pri;
+            return this;
+        }
+
+        /**
+         * Add an action to this notification. Actions are typically displayed by
+         * the system as a button adjacent to the notification content.
+         *
+         * @param icon Resource ID of a drawable that represents the action.
+         * @param title Text describing the action.
+         * @param intent PendingIntent to be fired when the action is invoked.
+         */
+        public Builder addAction(int icon, CharSequence title, PendingIntent intent) {
+            mActions.add(new Action(icon, title, intent));
+            return this;
+        }
+
+        /**
+         * Add a rich notification style to be applied at build time.
+         *
+         * @param style Object responsible for modifying the notification style.
+         */
+        public Builder setStyle(Style style) {
+            if (mStyle != style) {
+                mStyle = style;
+                if (mStyle != null) {
+                    mStyle.setBuilder(this);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * @deprecated Use {@link #build()} instead.
+         */
+        @Deprecated
+        public Notification getNotification() {
+            return (Notification) IMPL.build(this);
+        }
+
+        /**
          * Combine all of the options that have been set and return a new {@link Notification}
          * object.
          */
-        public Notification getNotification() {
-            return (Notification) IMPL.getNotification(this);
+        public Notification build() {
+            return (Notification) IMPL.build(this);
+        }
+    }
+
+    /**
+     * An object that can apply a rich notification style to a {@link Notification.Builder}
+     * object.
+     */
+    public static abstract class Style
+    {
+        Builder mBuilder;
+        CharSequence mBigContentTitle;
+        CharSequence mSummaryText;
+        boolean mSummaryTextSet = false;
+
+        public void setBuilder(Builder builder) {
+            if (mBuilder != builder) {
+                mBuilder = builder;
+                if (mBuilder != null) {
+                    mBuilder.setStyle(this);
+                }
+            }
+        }
+
+        public Notification build() {
+            Notification notification = null;
+            if (mBuilder != null) {
+                notification = mBuilder.build();
+            } 
+            return notification;
+        }
+    }
+
+    /**
+     * Helper class for generating large-format notifications that include a large image attachment.
+     * 
+     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.Builder()
+     *     .setContentTitle(&quot;New photo from &quot; + sender.toString())
+     *     .setContentText(subject)
+     *     .setSmallIcon(R.drawable.new_post)
+     *     .setLargeIcon(aBitmap)
+     *     .setStyle(new Notification.BigPictureStyle()
+     *         .bigPicture(aBigBitmap))
+     *     .build();
+     * </pre>
+     * 
+     * @see Notification#bigContentView
+     */
+    public static class BigPictureStyle extends Style {
+        Bitmap mPicture;
+
+        public BigPictureStyle() {
+        }
+
+        public BigPictureStyle(Builder builder) {
+            setBuilder(builder);
+        }
+
+        /**
+         * Overrides ContentTitle in the big form of the template.
+         * This defaults to the value passed to setContentTitle().
+         */
+        public BigPictureStyle setBigContentTitle(CharSequence title) {
+            mBigContentTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the first line of text after the detail section in the big form of the template.
+         */
+        public BigPictureStyle setSummaryText(CharSequence cs) {
+            mSummaryText = cs;
+            mSummaryTextSet = true;
+            return this;
+        }
+
+        public BigPictureStyle bigPicture(Bitmap b) {
+            mPicture = b;
+            return this;
+        }
+    }
+
+    /**
+     * Helper class for generating large-format notifications that include a lot of text.
+     * 
+     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.Builder()
+     *     .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *     .setContentText(subject)
+     *     .setSmallIcon(R.drawable.new_mail)
+     *     .setLargeIcon(aBitmap)
+     *     .setStyle(new Notification.BigTextStyle()
+     *         .bigText(aVeryLongString))
+     *     .build();
+     * </pre>
+     * 
+     * @see Notification#bigContentView
+     */
+    public static class BigTextStyle extends Style {
+        CharSequence mBigText;
+
+        public BigTextStyle() {
+        }
+
+        public BigTextStyle(Builder builder) {
+            setBuilder(builder);
+        }
+
+        /**
+         * Overrides ContentTitle in the big form of the template.
+         * This defaults to the value passed to setContentTitle().
+         */
+        public BigTextStyle setBigContentTitle(CharSequence title) {
+            mBigContentTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the first line of text after the detail section in the big form of the template.
+         */
+        public BigTextStyle setSummaryText(CharSequence cs) {
+            mSummaryText = cs;
+            mSummaryTextSet = true;
+            return this;
+        }
+
+        public BigTextStyle bigText(CharSequence cs) {
+            mBigText = cs;
+            return this;
+        }
+    }
+
+    /**
+     * Helper class for generating large-format notifications that include a list of (up to 5) strings.
+     * 
+     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like so:
+     * <pre class="prettyprint">
+     * Notification noti = new Notification.Builder()
+     *     .setContentTitle(&quot;5 New mails from &quot; + sender.toString())
+     *     .setContentText(subject)
+     *     .setSmallIcon(R.drawable.new_mail)
+     *     .setLargeIcon(aBitmap)
+     *     .setStyle(new Notification.InboxStyle()
+     *         .addLine(str1)
+     *         .addLine(str2)
+     *         .setContentTitle(&quot;&quot;)
+     *         .setSummaryText(&quot;+3 more&quot;))
+     *     .build();
+     * </pre>
+     * 
+     * @see Notification#bigContentView
+     */
+    public static class InboxStyle extends Style {
+        ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+
+        public InboxStyle() {
+        }
+
+        public InboxStyle(Builder builder) {
+            setBuilder(builder);
+        }
+
+        /**
+         * Overrides ContentTitle in the big form of the template.
+         * This defaults to the value passed to setContentTitle().
+         */
+        public InboxStyle setBigContentTitle(CharSequence title) {
+            mBigContentTitle = title;
+            return this;
+        }
+
+        /**
+         * Set the first line of text after the detail section in the big form of the template.
+         */
+        public InboxStyle setSummaryText(CharSequence cs) {
+            mSummaryText = cs;
+            mSummaryTextSet = true;
+            return this;
+        }
+
+        public InboxStyle addLine(CharSequence cs) {
+            mTexts.add(cs);
+            return this;
+        }
+    }
+
+    public static class Action {
+        public int icon;
+        public CharSequence title;
+        public PendingIntent actionIntent;
+
+        public Action(int icon_, CharSequence title_, PendingIntent intent_) {
+            this.icon = icon_;
+            this.title = title_;
+            this.actionIntent = intent_;
         }
     }
 }
diff --git a/v4/jellybean/android/support/v4/app/NotificationCompatJellybean.java b/v4/jellybean/android/support/v4/app/NotificationCompatJellybean.java
new file mode 100644
index 0000000..6b5f13f
--- /dev/null
+++ b/v4/jellybean/android/support/v4/app/NotificationCompatJellybean.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.app;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.widget.RemoteViews;
+import java.util.ArrayList;
+
+class NotificationCompatJellybean {
+    private Notification.Builder b;
+    public NotificationCompatJellybean(Context context, Notification n,
+            CharSequence contentTitle, CharSequence contentText, CharSequence contentInfo,
+            RemoteViews tickerView, int number,
+            PendingIntent contentIntent, PendingIntent fullScreenIntent, Bitmap largeIcon,
+            int mProgressMax, int mProgress, boolean mProgressIndeterminate,
+            boolean useChronometer, int priority, CharSequence subText) {
+        b = new Notification.Builder(context)
+            .setWhen(n.when)
+            .setSmallIcon(n.icon, n.iconLevel)
+            .setContent(n.contentView)
+            .setTicker(n.tickerText, tickerView)
+            .setSound(n.sound, n.audioStreamType)
+            .setVibrate(n.vibrate)
+            .setLights(n.ledARGB, n.ledOnMS, n.ledOffMS)
+            .setOngoing((n.flags & Notification.FLAG_ONGOING_EVENT) != 0)
+            .setOnlyAlertOnce((n.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)
+            .setAutoCancel((n.flags & Notification.FLAG_AUTO_CANCEL) != 0)
+            .setDefaults(n.defaults)
+            .setContentTitle(contentTitle)
+            .setContentText(contentText)
+            .setSubText(subText)
+            .setContentInfo(contentInfo)
+            .setContentIntent(contentIntent)
+            .setDeleteIntent(n.deleteIntent)
+            .setFullScreenIntent(fullScreenIntent,
+                    (n.flags & Notification.FLAG_HIGH_PRIORITY) != 0)
+            .setLargeIcon(largeIcon)
+            .setNumber(number)
+            .setUsesChronometer(useChronometer)
+            .setPriority(priority)
+            .setProgress(mProgressMax, mProgress, mProgressIndeterminate);
+    }
+
+    public void addAction(int icon, CharSequence title, PendingIntent intent) {
+        b.addAction(icon, title, intent);
+    }
+
+    public void addBigTextStyle(CharSequence bigContentTitle, boolean useSummary,
+            CharSequence summaryText, CharSequence bigText) {
+        Notification.BigTextStyle style = new Notification.BigTextStyle(b)
+            .setBigContentTitle(bigContentTitle)
+            .bigText(bigText);
+        if (useSummary) {
+            style.setSummaryText(summaryText);
+         }
+    }
+
+    public void addBigPictureStyle(CharSequence bigContentTitle, boolean useSummary,
+            CharSequence summaryText, Bitmap bigPicture) {
+       Notification.BigPictureStyle style = new Notification.BigPictureStyle(b)
+           .setBigContentTitle(bigContentTitle)
+           .bigPicture(bigPicture);
+        if (useSummary) {
+            style.setSummaryText(summaryText);
+         }
+    }
+
+    public void addInboxStyle(CharSequence bigContentTitle, boolean useSummary,
+            CharSequence summaryText, ArrayList<CharSequence> texts) {
+        Notification.InboxStyle style = new Notification.InboxStyle(b)
+            .setBigContentTitle(bigContentTitle);
+        if (useSummary) {
+            style.setSummaryText(summaryText);
+        }
+        for (CharSequence text: texts) {
+            style.addLine(text);
+        }
+    }
+
+    public Notification build() {
+        return b.build();
+    }
+}