Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5cadc3b0 authored by Griff Hazen's avatar Griff Hazen
Browse files

Add RemoteInput, Grouping, and Extender to Notification api.

RemoteInputs annotate actions and content intents to request
input from the user as part of an intent being sent. Results
are sent along using ClipData to avoid unparcel of developer
provided bundle values. A helper method is expexted to be used
to extract results.

Grouping allows notifications to be bundled together, with an optional
summary notification for display on older platforms. SortKey is an
important part of grouping since child notifications will likely have
a prescribed ordering. It is also useful in top level notifications
for apps that want to provide an ordering all other fields being equal.

Also provide a fluid way to to extend Actions and Notifications
using Extenders, e.g.:

Notification n = new NotificationCompat.Builder(context)
    .setOption1()
    .apply(new SomeExtender()
        .setOption2())
    .build();

This helps extension libraries provide a nice API experience for devs.

Change-Id: Ib3438ef854772c2c34d21bf1eb4ed7c9e032106f
parent e0d6a6b0
Loading
Loading
Loading
Loading
+44 −1
Original line number Diff line number Diff line
@@ -3916,6 +3916,8 @@ package android.app {
    ctor public Notification(android.os.Parcel);
    method public android.app.Notification clone();
    method public int describeContents();
    method public java.lang.String getGroup();
    method public java.lang.String getSortKey();
    method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
@@ -3942,6 +3944,7 @@ package android.app {
    field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
    field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
    field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
    field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
    field public static final deprecated int FLAG_HIGH_PRIORITY = 128; // 0x80
    field public static final int FLAG_INSISTENT = 4; // 0x4
    field public static final int FLAG_LOCAL_ONLY = 256; // 0x100
@@ -3985,6 +3988,7 @@ package android.app {
    method public android.app.Notification.Action clone();
    method public int describeContents();
    method public android.os.Bundle getExtras();
    method public android.app.RemoteInput[] getRemoteInputs();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
    field public android.app.PendingIntent actionIntent;
@@ -3992,14 +3996,20 @@ package android.app {
    field public java.lang.CharSequence title;
  }
  public static class Notification.Action.Builder {
  public static final class Notification.Action.Builder {
    ctor public Notification.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent);
    ctor public Notification.Action.Builder(android.app.Notification.Action);
    method public android.app.Notification.Action.Builder addExtras(android.os.Bundle);
    method public android.app.Notification.Action.Builder addRemoteInput(android.app.RemoteInput);
    method public android.app.Notification.Action.Builder apply(android.app.Notification.Action.Builder.Extender);
    method public android.app.Notification.Action build();
    method public android.os.Bundle getExtras();
  }
  public static abstract interface Notification.Action.Builder.Extender {
    method public abstract android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
  }
  public static class Notification.BigPictureStyle extends android.app.Notification.Style {
    ctor public Notification.BigPictureStyle();
    ctor public Notification.BigPictureStyle(android.app.Notification.Builder);
@@ -4022,6 +4032,7 @@ package android.app {
    method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
    method public android.app.Notification.Builder addAction(android.app.Notification.Action);
    method public android.app.Notification.Builder addExtras(android.os.Bundle);
    method public android.app.Notification.Builder apply(android.app.Notification.Builder.Extender);
    method public android.app.Notification build();
    method public android.os.Bundle getExtras();
    method public deprecated android.app.Notification getNotification();
@@ -4035,6 +4046,8 @@ package android.app {
    method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
    method public android.app.Notification.Builder setExtras(android.os.Bundle);
    method public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
    method public android.app.Notification.Builder setGroup(java.lang.String);
    method public android.app.Notification.Builder setGroupSummary(boolean);
    method public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
    method public android.app.Notification.Builder setLights(int, int, int);
    method public android.app.Notification.Builder setLocalOnly(boolean);
@@ -4046,6 +4059,7 @@ package android.app {
    method public android.app.Notification.Builder setShowWhen(boolean);
    method public android.app.Notification.Builder setSmallIcon(int);
    method public android.app.Notification.Builder setSmallIcon(int, int);
    method public android.app.Notification.Builder setSortKey(java.lang.String);
    method public android.app.Notification.Builder setSound(android.net.Uri);
    method public android.app.Notification.Builder setSound(android.net.Uri, int);
    method public android.app.Notification.Builder setStyle(android.app.Notification.Style);
@@ -4057,6 +4071,10 @@ package android.app {
    method public android.app.Notification.Builder setWhen(long);
  }
  public static abstract interface Notification.Builder.Extender {
    method public abstract android.app.Notification.Builder applyTo(android.app.Notification.Builder);
  }
  public static class Notification.InboxStyle extends android.app.Notification.Style {
    ctor public Notification.InboxStyle();
    ctor public Notification.InboxStyle(android.app.Notification.Builder);
@@ -4160,6 +4178,31 @@ package android.app {
    field public static final int STYLE_SPINNER = 0; // 0x0
  }
  public final class RemoteInput implements android.os.Parcelable {
    method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
    method public int describeContents();
    method public boolean getAllowFreeFormInput();
    method public java.lang.CharSequence[] getChoices();
    method public android.os.Bundle getExtras();
    method public java.lang.CharSequence getLabel();
    method public java.lang.String getResultKey();
    method public static android.os.Bundle getResultsFromIntent(android.content.Intent);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final java.lang.String EXTRA_RESULTS_DATA = "android.remoteinput.resultsData";
    field public static final java.lang.String RESULTS_CLIP_LABEL = "android.remoteinput.results";
  }
  public static final class RemoteInput.Builder {
    ctor public RemoteInput.Builder(java.lang.String);
    method public android.app.RemoteInput.Builder addExtras(android.os.Bundle);
    method public android.app.RemoteInput build();
    method public android.os.Bundle getExtras();
    method public android.app.RemoteInput.Builder setAllowFreeFormInput(boolean);
    method public android.app.RemoteInput.Builder setChoices(java.lang.CharSequence[]);
    method public android.app.RemoteInput.Builder setLabel(java.lang.CharSequence);
  }
  public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
    method public android.content.ComponentName getGlobalSearchActivity();
    method public android.app.SearchableInfo getSearchableInfo(android.content.ComponentName);
+206 −13
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import com.android.internal.R;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;

/**
 * A class that represents how a persistent notification is to be presented to
@@ -355,6 +356,14 @@ public class Notification implements Parcelable
     */
    public static final int FLAG_LOCAL_ONLY         = 0x00000100;

    /**
     * Bit to be bitswise-ored into the {@link #flags} field that should be
     * set if this notification is the group summary for a group of notifications.
     * Grouped notifications may display in a cluster or stack on devices which
     * support such rendering. Requires a group key also be set using {@link Builder#setGroup}.
     */
    public static final int FLAG_GROUP_SUMMARY      = 0x00000200;

    public int flags;

    /**
@@ -493,6 +502,34 @@ public class Notification implements Parcelable
     */
    public String category;

    private String mGroupKey;

    /**
     * Get the key used to group this notification into a cluster or stack
     * with other notifications on devices which support such rendering.
     */
    public String getGroup() {
        return mGroupKey;
    }

    private String mSortKey;

    /**
     * Get a sort key that orders this notification among other notifications from the
     * same package. This can be useful if an external sort was already applied and an app
     * would like to preserve this. Notifications will be sorted lexicographically using this
     * value, although providing different priorities in addition to providing sort key may
     * cause this value to be ignored.
     *
     * <p>This sort key can also be used to order members of a notification group. See
     * {@link Builder#setGroup}.
     *
     * @see String#compareTo(String)
     */
    public String getSortKey() {
        return mSortKey;
    }

    /**
     * Additional semantic data to be carried around with this Notification.
     * <p>
@@ -659,15 +696,18 @@ public class Notification implements Parcelable
     */
    public static class Action implements Parcelable {
        private final Bundle mExtras;
        private RemoteInput[] mRemoteInputs;

        /**
         * Small icon representing the action.
         */
        public int icon;

        /**
         * Title of the action.
         */
        public CharSequence title;

        /**
         * Intent to send when the user invokes this action. May be null, in which case the action
         * may be rendered in a disabled presentation by the system UI.
@@ -681,19 +721,23 @@ public class Notification implements Parcelable
                actionIntent = PendingIntent.CREATOR.createFromParcel(in);
            }
            mExtras = in.readBundle();
            mRemoteInputs = in.createTypedArray(RemoteInput.CREATOR);
        }

        /**
         * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}.
         */
        public Action(int icon, CharSequence title, PendingIntent intent) {
            this(icon, title, intent, new Bundle());
            this(icon, title, intent, new Bundle(), null);
        }

        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
        private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras,
                RemoteInput[] remoteInputs) {
            this.icon = icon;
            this.title = title;
            this.actionIntent = intent;
            this.mExtras = extras != null ? extras : new Bundle();
            this.mRemoteInputs = remoteInputs;
        }

        /**
@@ -703,14 +747,23 @@ public class Notification implements Parcelable
            return mExtras;
        }

        /**
         * Get the list of inputs to be collected from the user when this action is sent.
         * May return null if no remote inputs were added.
         */
        public RemoteInput[] getRemoteInputs() {
            return mRemoteInputs;
        }

        /**
         * Builder class for {@link Action} objects.
         */
        public static class Builder {
        public static final class Builder {
            private final int mIcon;
            private final CharSequence mTitle;
            private final PendingIntent mIntent;
            private final Bundle mExtras;
            private ArrayList<RemoteInput> mRemoteInputs;

            /**
             * Construct a new builder for {@link Action} object.
@@ -719,7 +772,7 @@ public class Notification implements Parcelable
             * @param intent the {@link PendingIntent} to fire when users trigger this action
             */
            public Builder(int icon, CharSequence title, PendingIntent intent) {
                this(icon, title, intent, new Bundle());
                this(icon, title, intent, new Bundle(), null);
            }

            /**
@@ -728,14 +781,20 @@ public class Notification implements Parcelable
             * @param action the action to read fields from.
             */
            public Builder(Action action) {
                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras));
                this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras),
                        action.getRemoteInputs());
            }

            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) {
            private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras,
                    RemoteInput[] remoteInputs) {
                mIcon = icon;
                mTitle = title;
                mIntent = intent;
                mExtras = extras;
                if (remoteInputs != null) {
                    mRemoteInputs = new ArrayList<RemoteInput>(remoteInputs.length);
                    Collections.addAll(mRemoteInputs, remoteInputs);
                }
            }

            /**
@@ -761,23 +820,63 @@ public class Notification implements Parcelable
                return mExtras;
            }

            /**
             * Add an input to be collected from the user when this action is sent.
             * Response values can be retrieved from the fired intent by using the
             * {@link RemoteInput#getResultsFromIntent} function.
             * @param remoteInput a {@link RemoteInput} to add to the action
             * @return this object for method chaining
             */
            public Builder addRemoteInput(RemoteInput remoteInput) {
                if (mRemoteInputs == null) {
                    mRemoteInputs = new ArrayList<RemoteInput>();
                }
                mRemoteInputs.add(remoteInput);
                return this;
            }

            /**
             * Apply an extender to this action builder. Extenders may be used to add
             * metadata or change options on this builder.
             */
            public Builder apply(Extender extender) {
                extender.applyTo(this);
                return this;
            }

            /**
             * Extender interface for use with {@link #apply}. Extenders may be used to add
             * metadata or change options on this builder.
             */
            public interface Extender {
                /**
                 * Apply this extender to a notification action builder.
                 * @param builder the builder to be modified.
                 * @return the build object for chaining.
                 */
                public Builder applyTo(Builder builder);
            }

            /**
             * Combine all of the options that have been set and return a new {@link Action}
             * object.
             * @return the built action
             */
            public Action build() {
                return new Action(mIcon, mTitle, mIntent, mExtras);
                RemoteInput[] remoteInputs = mRemoteInputs != null
                        ? mRemoteInputs.toArray(new RemoteInput[mRemoteInputs.size()]) : null;
                return new Action(mIcon, mTitle, mIntent, mExtras, remoteInputs);
            }
        }

        @Override
        public Action clone() {
            return new Action(
                this.icon,
                this.title,
                this.actionIntent, // safe to alias
                new Bundle(this.mExtras));
                    icon,
                    title,
                    actionIntent, // safe to alias
                    new Bundle(mExtras),
                    getRemoteInputs());
        }
        @Override
        public int describeContents() {
@@ -794,6 +893,7 @@ public class Notification implements Parcelable
                out.writeInt(0);
            }
            out.writeBundle(mExtras);
            out.writeTypedArray(mRemoteInputs, flags);
        }
        public static final Parcelable.Creator<Action> CREATOR =
                new Parcelable.Creator<Action>() {
@@ -906,6 +1006,10 @@ public class Notification implements Parcelable

        category = parcel.readString();

        mGroupKey = parcel.readString();

        mSortKey = parcel.readString();

        extras = parcel.readBundle(); // may be null

        actions = parcel.createTypedArray(Action.CREATOR); // may be null
@@ -971,6 +1075,10 @@ public class Notification implements Parcelable

        that.category = this.category;

        that.mGroupKey = this.mGroupKey;

        that.mSortKey = this.mSortKey;

        if (this.extras != null) {
            try {
                that.extras = new Bundle(this.extras);
@@ -1108,6 +1216,10 @@ public class Notification implements Parcelable

        parcel.writeString(category);

        parcel.writeString(mGroupKey);

        parcel.writeString(mSortKey);

        parcel.writeBundle(extras); // null ok

        parcel.writeTypedArray(actions, 0); // null ok
@@ -1226,7 +1338,18 @@ public class Notification implements Parcelable
        sb.append(Integer.toHexString(this.defaults));
        sb.append(" flags=0x");
        sb.append(Integer.toHexString(this.flags));
        sb.append(" category="); sb.append(this.category);
        if (this.category != null) {
            sb.append(" category=");
            sb.append(this.category);
        }
        if (this.mGroupKey != null) {
            sb.append(" groupKey=");
            sb.append(this.mGroupKey);
        }
        if (this.mSortKey != null) {
            sb.append(" sortKey=");
            sb.append(this.mSortKey);
        }
        if (actions != null) {
            sb.append(" ");
            sb.append(actions.length);
@@ -1306,6 +1429,8 @@ public class Notification implements Parcelable
        private int mProgress;
        private boolean mProgressIndeterminate;
        private String mCategory;
        private String mGroupKey;
        private String mSortKey;
        private Bundle mExtras;
        private int mPriority;
        private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
@@ -1717,6 +1842,51 @@ public class Notification implements Parcelable
            return this;
        }

        /**
         * Set this notification to be part of a group of notifications sharing the same key.
         * Grouped notifications may display in a cluster or stack on devices which
         * support such rendering.
         *
         * <p>To make this notification the summary for its group, also call
         * {@link #setGroupSummary}. A sort order can be specified for group members by using
         * {@link #setSortKey}.
         * @param groupKey The group key of the group.
         * @return this object for method chaining
         */
        public Builder setGroup(String groupKey) {
            mGroupKey = groupKey;
            return this;
        }

        /**
         * Set this notification to be the group summary for a group of notifications.
         * Grouped notifications may display in a cluster or stack on devices which
         * support such rendering. Requires a group key also be set using {@link #setGroup}.
         * @param isGroupSummary Whether this notification should be a group summary.
         * @return this object for method chaining
         */
        public Builder setGroupSummary(boolean isGroupSummary) {
            setFlag(FLAG_GROUP_SUMMARY, isGroupSummary);
            return this;
        }

        /**
         * Set a sort key that orders this notification among other notifications from the
         * same package. This can be useful if an external sort was already applied and an app
         * would like to preserve this. Notifications will be sorted lexicographically using this
         * value, although providing different priorities in addition to providing sort key may
         * cause this value to be ignored.
         *
         * <p>This sort key can also be used to order members of a notification group. See
         * {@link Builder#setGroup}.
         *
         * @see String#compareTo(String)
         */
        public Builder setSortKey(String sortKey) {
            mSortKey = sortKey;
            return this;
        }

        /**
         * Merge additional metadata into this notification.
         *
@@ -1826,6 +1996,28 @@ public class Notification implements Parcelable
            return this;
        }

        /**
         * Apply an extender to this notification builder. Extenders may be used to add
         * metadata or change options on this builder.
         */
        public Builder apply(Extender extender) {
            extender.applyTo(this);
            return this;
        }

        /**
         * Extender interface for use with {@link #apply}. Extenders may be used to add
         * metadata or change options on this builder.
         */
        public interface Extender {
            /**
             * Apply this extender to a notification builder.
             * @param builder the builder to be modified.
             * @return the build object for chaining.
             */
            public Builder applyTo(Builder builder);
        }

        private void setFlag(int mask, boolean value) {
            if (value) {
                mFlags |= mask;
@@ -2028,12 +2220,13 @@ public class Notification implements Parcelable
                n.flags |= FLAG_SHOW_LIGHTS;
            }
            n.category = mCategory;
            n.mGroupKey = mGroupKey;
            n.mSortKey = mSortKey;
            n.priority = mPriority;
            if (mActions.size() > 0) {
                n.actions = new Action[mActions.size()];
                mActions.toArray(n.actions);
            }

            return n;
        }

+297 −0

File added.

Preview size limit exceeded, changes collapsed.