am 7ea18e1c: Add wearable notification extensions to frameworks/base.

* commit '7ea18e1c92ab84d1a0d00087e6e3784646661fe8':
  Add wearable notification extensions to frameworks/base.
diff --git a/api/current.txt b/api/current.txt
index fafcd6f..3cd607e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4661,6 +4661,80 @@
 
 }
 
+package android.app.wearable {
+
+  public final class WearableActionExtensions implements android.app.Notification.Action.Builder.Extender android.os.Parcelable {
+    method public android.app.Notification.Action.Builder applyTo(android.app.Notification.Action.Builder);
+    method public int describeContents();
+    method public static android.app.wearable.WearableActionExtensions from(android.app.Notification.Action);
+    method public boolean isAvailableOffline();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
+  public static final class WearableActionExtensions.Builder {
+    ctor public WearableActionExtensions.Builder();
+    ctor public WearableActionExtensions.Builder(android.app.wearable.WearableActionExtensions);
+    method public android.app.wearable.WearableActionExtensions build();
+    method public android.app.wearable.WearableActionExtensions.Builder setAvailableOffline(boolean);
+  }
+
+  public final class WearableNotificationExtensions implements android.app.Notification.Builder.Extender android.os.Parcelable {
+    method public android.app.Notification.Builder applyTo(android.app.Notification.Builder);
+    method public int describeContents();
+    method public static android.app.wearable.WearableNotificationExtensions from(android.app.Notification);
+    method public android.app.Notification.Action getAction(int);
+    method public int getActionCount();
+    method public android.app.Notification.Action[] getActions();
+    method public android.graphics.Bitmap getBackground();
+    method public int getContentAction();
+    method public int getContentIcon();
+    method public int getContentIconGravity();
+    method public boolean getContentIntentAvailableOffline();
+    method public int getCustomContentHeight();
+    method public int getCustomSizePreset();
+    method public android.app.PendingIntent getDisplayIntent();
+    method public int getGravity();
+    method public boolean getHintHideIcon();
+    method public boolean getHintShowBackgroundOnly();
+    method public android.app.Notification[] getPages();
+    method public boolean getStartScrollBottom();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULLSCREEN = 16; // 0x10
+    field public static final int SIZE_LARGE = 256; // 0x100
+    field public static final int SIZE_MEDIUM = 128; // 0x80
+    field public static final int SIZE_SMALL = 64; // 0x40
+    field public static final int SIZE_XSMALL = 32; // 0x20
+    field public static final int UNSET_ACTION_INDEX = -1; // 0xffffffff
+  }
+
+  public static final class WearableNotificationExtensions.Builder {
+    ctor public WearableNotificationExtensions.Builder();
+    ctor public WearableNotificationExtensions.Builder(android.app.wearable.WearableNotificationExtensions);
+    method public android.app.wearable.WearableNotificationExtensions.Builder addAction(android.app.Notification.Action);
+    method public android.app.wearable.WearableNotificationExtensions.Builder addActions(java.util.List<android.app.Notification.Action>);
+    method public android.app.wearable.WearableNotificationExtensions.Builder addPage(android.app.Notification);
+    method public android.app.wearable.WearableNotificationExtensions.Builder addPages(java.util.List<android.app.Notification>);
+    method public android.app.wearable.WearableNotificationExtensions build();
+    method public android.app.wearable.WearableNotificationExtensions.Builder clearActions();
+    method public android.app.wearable.WearableNotificationExtensions.Builder clearPages();
+    method public android.app.wearable.WearableNotificationExtensions.Builder setBackground(android.graphics.Bitmap);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setContentAction(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIcon(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIconGravity(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setContentIntentAvailableOffline(boolean);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setCustomContentHeight(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setCustomSizePreset(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setDisplayIntent(android.app.PendingIntent);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setGravity(int);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setHintHideIcon(boolean);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setHintShowBackgroundOnly(boolean);
+    method public android.app.wearable.WearableNotificationExtensions.Builder setStartScrollBottom(boolean);
+  }
+
+}
+
 package android.appwidget {
 
   public class AppWidgetHost {
diff --git a/core/java/android/app/wearable/WearableActionExtensions.java b/core/java/android/app/wearable/WearableActionExtensions.java
new file mode 100644
index 0000000..1888fcf
--- /dev/null
+++ b/core/java/android/app/wearable/WearableActionExtensions.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 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.app.wearable;
+
+import android.app.Notification;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Wearable extensions to notification actions. To add extensions to an action,
+ * create a new {@link WearableActionExtensions} object using
+ * {@link WearableActionExtensions.Builder} and apply it to a
+ * {@link android.app.Notification.Action.Builder}.
+ *
+ * <pre class="prettyprint">
+ * Notification.Action action = new Notification.Action.Builder(
+ *         R.drawable.archive_all, "Archive all", actionIntent)
+ *         .apply(new WearableActionExtensions.Builder()
+ *                 .setAvailableOffline(false)
+ *                 .build())
+ *         .build();
+ * </pre>
+ */
+public final class WearableActionExtensions implements Notification.Action.Builder.Extender,
+        Parcelable {
+    /** Notification action extra which contains wearable extensions */
+    static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+    // Flags bitwise-ored to mFlags
+    static final int FLAG_AVAILABLE_OFFLINE = 1 << 0;
+
+    // Default value for flags integer
+    static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
+
+    private final int mFlags;
+
+    private WearableActionExtensions(int flags) {
+        mFlags = flags;
+    }
+
+    private WearableActionExtensions(Parcel in) {
+        mFlags = in.readInt();
+    }
+
+    /**
+     * Create a {@link WearableActionExtensions} by reading wearable extensions present on an
+     * existing notification action.
+     * @param action the notification action to inspect.
+     * @return a new {@link WearableActionExtensions} object.
+     */
+    public static WearableActionExtensions from(Notification.Action action) {
+        WearableActionExtensions extensions = action.getExtras().getParcelable(
+                EXTRA_WEARABLE_EXTENSIONS);
+        if (extensions != null) {
+            return extensions;
+        } else {
+            // Return a WearableActionExtensions with default values.
+            return new Builder().build();
+        }
+    }
+
+    /**
+     * Get whether this action is available when the wearable device is not connected to
+     * a companion device. The user can still trigger this action when the wearable device is
+     * offline, but a visual hint will indicate that the action may not be available.
+     * Defaults to true.
+     */
+    public boolean isAvailableOffline() {
+        return (mFlags & FLAG_AVAILABLE_OFFLINE) != 0;
+    }
+
+    @Override
+    public Notification.Action.Builder applyTo(Notification.Action.Builder builder) {
+        builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
+        return builder;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mFlags);
+    }
+
+    /**
+     * Builder for {@link WearableActionExtensions} objects, which adds wearable extensions to
+     * notification actions. To extend an action, create an instance of this class, call the set
+     * methods present, call {@link #build}, and finally apply the options to a
+     * {@link Notification.Builder} using its {@link android.app.Notification.Builder#apply} method.
+     */
+    public static final class Builder {
+        private int mFlags = DEFAULT_FLAGS;
+
+        /**
+         * Construct a builder to be used for adding wearable extensions to notification actions.
+         *
+         * <pre class="prettyprint">
+         * Notification.Action action = new Notification.Action.Builder(
+         *         R.drawable.archive_all, "Archive all", actionIntent)
+         *         .apply(new WearableActionExtensions.Builder()
+         *                 .setAvailableOffline(false)
+         *                 .build())
+         *         .build();</pre>
+         */
+        public Builder() {
+        }
+
+        /**
+         * Create a {@link Builder} by reading wearable extensions present on an
+         * existing {@code WearableActionExtensions} object.
+         * @param other the existing extensions to inspect.
+         */
+        public Builder(WearableActionExtensions other) {
+            mFlags = other.mFlags;
+        }
+
+        /**
+         * Set whether this action is available when the wearable device is not connected to
+         * a companion device. The user can still trigger this action when the wearable device is
+         * offline, but a visual hint will indicate that the action may not be available.
+         * Defaults to true.
+         */
+        public Builder setAvailableOffline(boolean availableOffline) {
+            setFlag(FLAG_AVAILABLE_OFFLINE, availableOffline);
+            return this;
+        }
+
+        /**
+         * Build a new {@link WearableActionExtensions} object with the extensions
+         * currently present on this builder.
+         * @return the extensions object.
+         */
+        public WearableActionExtensions build() {
+            return new WearableActionExtensions(mFlags);
+        }
+
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mFlags |= mask;
+            } else {
+                mFlags &= ~mask;
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/wearable/WearableNotificationExtensions.java b/core/java/android/app/wearable/WearableNotificationExtensions.java
new file mode 100644
index 0000000..75f7f85
--- /dev/null
+++ b/core/java/android/app/wearable/WearableNotificationExtensions.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2014 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.app.wearable;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Gravity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Helper class that contains wearable extensions for notifications.
+ * <p class="note"> See
+ * <a href="{@docRoot}wear/notifications/creating.html">Creating Notifications
+ * for Android Wear</a> for more information on how to use this class.
+ * <p>
+ * To create a notification with wearable extensions:
+ * <ol>
+ *   <li>Create a {@link Notification.Builder}, setting any desired
+ *   properties.
+ *   <li>Create a {@link WearableNotificationExtensions.Builder}.
+ *   <li>Set wearable-specific properties using the
+ *   {@code add} and {@code set} methods of {@link WearableNotificationExtensions.Builder}.
+ *   <li>Call {@link WearableNotificationExtensions.Builder#build} to build the extensions
+ *   object.
+ *   <li>Call {@link Notification.Builder#apply} to apply the extensions to a notification.
+ *   <li>Post the notification to the notification system with the
+ *   {@code NotificationManager.notify(...)} methods.
+ * </ol>
+ *
+ * <pre class="prettyprint">
+ * Notification notif = new Notification.Builder(mContext)
+ *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+ *         .setContentText(subject)
+ *         .setSmallIcon(R.drawable.new_mail)
+ *         .apply(new new WearableNotificationExtensions.Builder()
+ *                 .setContentIcon(R.drawable.new_mail)
+ *                 .build())
+ *         .build();
+ * NotificationManager notificationManger =
+ *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ * notificationManger.notify(0, notif);</pre>
+ *
+ * <p>Wearable extensions can be accessed on an existing notification by using the
+ * {@link WearableNotificationExtensions#from} function.
+ *
+ * <pre class="prettyprint">
+ * WearableNotificationExtensions wearableExtensions = WearableNotificationExtensions.from(
+ *         notification);
+ * Notification[] pages = wearableExtensions.getPages();
+ * </pre>
+ */
+public final class WearableNotificationExtensions implements Notification.Builder.Extender,
+        Parcelable {
+    /**
+     * Sentinel value for an action index that is unset.
+     */
+    public static final int UNSET_ACTION_INDEX = -1;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification with
+     * default sizing.
+     * <p>For custom display notifications created using {@link Builder#setDisplayIntent},
+     * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+     * on their content.
+     */
+    public static final int SIZE_DEFAULT = 0;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification full
+     * screen.
+     */
+    public static final int SIZE_FULLSCREEN = 1 << 4;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
+     * with an extra small size.
+     * <p>This value is only applicable for custom display notifications created using
+     * {@link Builder#setDisplayIntent}.
+     */
+    public static final int SIZE_XSMALL = 1 << 5;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
+     * with a small size.
+     * <p>This value is only applicable for custom display notifications created using
+     * {@link Builder#setDisplayIntent}.
+     */
+    public static final int SIZE_SMALL = 1 << 6;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
+     * with a medium size.
+     * <p>This value is only applicable for custom display notifications created using
+     * {@link Builder#setDisplayIntent}.
+     */
+    public static final int SIZE_MEDIUM = 1 << 7;
+
+    /**
+     * Size value for use with {@link Builder#setCustomSizePreset} to show this notification
+     * with a large size.
+     * <p>This value is only applicable for custom display notifications created using
+     * {@link Builder#setDisplayIntent}.
+     */
+    public static final int SIZE_LARGE = 1 << 8;
+
+    /** Notification extra which contains wearable extensions */
+    static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
+
+    // Flags bitwise-ored to mFlags
+    static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 1 << 0;
+    static final int FLAG_HINT_HIDE_ICON = 1 << 1;
+    static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
+    static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
+    static final int FLAG_SIZE_FULLSCREEN = SIZE_FULLSCREEN;
+    static final int FLAG_SIZE_XSMALL = SIZE_XSMALL;
+    static final int FLAG_SIZE_SMALL = SIZE_SMALL;
+    static final int FLAG_SIZE_MEDIUM = SIZE_MEDIUM;
+    static final int FLAG_SIZE_LARGE = SIZE_LARGE;
+
+    // Default value for flags integer
+    static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
+
+    // Mask that will match all mutually exclusive size flags
+    static final int SIZE_FLAGS_MASK = FLAG_SIZE_XSMALL | FLAG_SIZE_SMALL | FLAG_SIZE_MEDIUM
+            | FLAG_SIZE_LARGE | FLAG_SIZE_FULLSCREEN;
+
+    private final Notification.Action[] mActions;
+    private final int mFlags;
+    private final PendingIntent mDisplayIntent;
+    private final Notification[] mPages;
+    private final Bitmap mBackground;
+    private final int mContentIcon;
+    private final int mContentIconGravity;
+    private final int mContentActionIndex;
+    private final int mCustomContentHeight;
+    private final int mGravity;
+
+    private WearableNotificationExtensions(Notification.Action[] actions, int flags,
+            PendingIntent displayIntent, Notification[] pages, Bitmap background,
+            int contentIcon, int contentIconGravity, int contentActionIndex,
+            int customContentHeight, int gravity) {
+        mActions = actions;
+        mFlags = flags;
+        mDisplayIntent = displayIntent;
+        mPages = pages;
+        mBackground = background;
+        mContentIcon = contentIcon;
+        mContentIconGravity = contentIconGravity;
+        mContentActionIndex = contentActionIndex;
+        mCustomContentHeight = customContentHeight;
+        mGravity = gravity;
+    }
+
+    private WearableNotificationExtensions(Parcel in) {
+        mActions = in.createTypedArray(Notification.Action.CREATOR);
+        mFlags = in.readInt();
+        mDisplayIntent = in.readParcelable(PendingIntent.class.getClassLoader());
+        mPages = in.createTypedArray(Notification.CREATOR);
+        mBackground = in.readParcelable(Bitmap.class.getClassLoader());
+        mContentIcon = in.readInt();
+        mContentIconGravity = in.readInt();
+        mContentActionIndex = in.readInt();
+        mCustomContentHeight = in.readInt();
+        mGravity = in.readInt();
+    }
+
+    /**
+     * Create a {@link WearableNotificationExtensions} by reading wearable extensions present on an
+     * existing notification.
+     * @param notif the notification to inspect.
+     * @return a new {@link WearableNotificationExtensions} object.
+     */
+    public static WearableNotificationExtensions from(Notification notif) {
+        WearableNotificationExtensions extensions = notif.extras.getParcelable(
+                EXTRA_WEARABLE_EXTENSIONS);
+        if (extensions != null) {
+            return extensions;
+        } else {
+            // Return a WearableNotificationExtensions with default values.
+            return new Builder().build();
+        }
+    }
+
+    /**
+     * Apply wearable extensions to a notification that is being built. This is typically
+     * called by {@link Notification.Builder#apply} method of {@link Notification.Builder}.
+     */
+    @Override
+    public Notification.Builder applyTo(Notification.Builder builder) {
+        builder.getExtras().putParcelable(EXTRA_WEARABLE_EXTENSIONS, this);
+        return builder;
+    }
+
+    /**
+     * Get the number of wearable actions present on this notification.
+     *
+     * @return the number of wearable actions for this notification
+     */
+    public int getActionCount() {
+        return mActions.length;
+    }
+
+    /**
+     * Get a {@link Notification.Action} for the wearable action at {@code actionIndex}.
+     * @param actionIndex the index of the desired wearable action
+     */
+    public Notification.Action getAction(int actionIndex) {
+        return mActions[actionIndex];
+    }
+
+    /**
+     * Get the wearable actions present on this notification.
+     */
+    public Notification.Action[] getActions() {
+        return mActions;
+    }
+
+    /**
+     * Get the intent to launch inside of an activity view when displaying this
+     * notification. This {@code PendingIntent} should be for an activity.
+     */
+    public PendingIntent getDisplayIntent() {
+        return mDisplayIntent;
+    }
+
+    /**
+     * Get the array of additional pages of content for displaying this notification. The
+     * current notification forms the first page, and elements within this array form
+     * subsequent pages. This field can be used to separate a notification into multiple
+     * sections.
+     * @return the pages for this notification
+     */
+    public Notification[] getPages() {
+        return mPages;
+    }
+
+    /**
+     * Get a background image to be displayed behind the notification content.
+     * Contrary to the {@link Notification.BigPictureStyle}, this background
+     * will work with any notification style.
+     *
+     * @return the background image
+     * @see Builder#setBackground
+     */
+    public Bitmap getBackground() {
+        return mBackground;
+    }
+
+    /**
+     * Get an icon that goes with the content of this notification.
+     */
+    public int getContentIcon() {
+        return mContentIcon;
+    }
+
+    /**
+     * Get the gravity that the content icon should have within the notification display.
+     * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
+     * value is {@link android.view.Gravity#END}.
+     * @see #getContentIcon
+     */
+    public int getContentIconGravity() {
+        return mContentIconGravity;
+    }
+
+    /**
+     * Get the action index of an action from this notification to show as clickable with
+     * the content of this notification page. When the user clicks this notification page,
+     * this action will trigger. This action will no longer display separately from the
+     * notification content. The action's icon will display with optional subtext provided
+     * by the action's title.
+     *
+     * <p>If wearable specific actions are present, this index will apply to that list,
+     * otherwise it will apply to the main notification's actions list.
+     */
+    public int getContentAction() {
+        return mContentActionIndex;
+    }
+
+    /**
+     * Get the gravity that this notification should have within the available viewport space.
+     * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
+     * {@link android.view.Gravity#BOTTOM}. The default value is
+     * {@link android.view.Gravity#BOTTOM}.
+     */
+    public int getGravity() {
+        return mGravity;
+    }
+
+    /**
+     * Get the custom size preset for the display of this notification out of the available
+     * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
+     * <p>Some custom size presets are only applicable for custom display notifications created
+     * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in question.
+     * See also {@link Builder#setCustomContentHeight} and {@link Builder#setCustomSizePreset}.
+     */
+    public int getCustomSizePreset() {
+        return mFlags & SIZE_FLAGS_MASK;
+    }
+
+    /**
+     * Get the custom height in pixels for the display of this notification's content.
+     * <p>This option is only available for custom display notifications created
+     * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
+     * {@link Builder#setCustomContentHeight}.
+     */
+    public int getCustomContentHeight() {
+        return mCustomContentHeight;
+    }
+
+    /**
+     * Get whether the scrolling position for the contents of this notification should start
+     * at the bottom of the contents instead of the top when the contents are too long to
+     * display within the screen. Default is false (start scroll at the top).
+     */
+    public boolean getStartScrollBottom() {
+        return (mFlags & FLAG_START_SCROLL_BOTTOM) != 0;
+    }
+
+    /**
+     * Get whether the content intent is available when the wearable device is not connected
+     * to a companion device.  The user can still trigger this intent when the wearable device is
+     * offline, but a visual hint will indicate that the content intent may not be available.
+     * Defaults to true.
+     */
+    public boolean getContentIntentAvailableOffline() {
+        return (mFlags & FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE) != 0;
+    }
+
+    /**
+     * Get a hint that this notification's icon should not be displayed.
+     * @return {@code true} if this icon should not be displayed, false otherwise.
+     * The default value is {@code false} if this was never set.
+     */
+    public boolean getHintHideIcon() {
+        return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
+    }
+
+    /**
+     * Get a visual hint that only the background image of this notification should be
+     * displayed, and other semantic content should be hidden. This hint is only applicable
+     * to sub-pages added using {@link Builder#addPage}.
+     */
+    public boolean getHintShowBackgroundOnly() {
+        return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedArray(mActions, flags);
+        out.writeInt(mFlags);
+        out.writeParcelable(mDisplayIntent, flags);
+        out.writeTypedArray(mPages, flags);
+        out.writeParcelable(mBackground, flags);
+        out.writeInt(mContentIcon);
+        out.writeInt(mContentIconGravity);
+        out.writeInt(mContentActionIndex);
+        out.writeInt(mCustomContentHeight);
+        out.writeInt(mGravity);
+    }
+
+    /**
+     * Builder to apply wearable notification extensions to a {@link Notification.Builder}
+     * object.
+     *
+     * <p>You can chain the "set" methods for this builder in any order,
+     * but you must call the {@link #build} method and then the {@link Notification.Builder#apply}
+     * method to apply your extensions to a notification.
+     *
+     * <pre class="prettyprint">
+     * Notification notif = new Notification.Builder(mContext)
+     *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+     *         .setContentText(subject)
+     *         .setSmallIcon(R.drawable.new_mail);
+     *         .apply(new WearableNotificationExtensions.Builder()
+     *                 .setContentIcon(R.drawable.new_mail)
+     *                 .build())
+     *         .build();
+     * NotificationManager notificationManger =
+     *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+     * notificationManager.notify(0, notif);</pre>
+     */
+    public static final class Builder {
+        private final List<Notification.Action> mActions =
+                new ArrayList<Notification.Action>();
+        private int mFlags = DEFAULT_FLAGS;
+        private PendingIntent mDisplayIntent;
+        private final List<Notification> mPages = new ArrayList<Notification>();
+        private Bitmap mBackground;
+        private int mContentIcon;
+        private int mContentIconGravity = Gravity.END;
+        private int mContentActionIndex = UNSET_ACTION_INDEX;
+        private int mCustomContentHeight;
+        private int mGravity = Gravity.BOTTOM;
+
+        /**
+         * Construct a builder to be used for adding wearable extensions to notifications.
+         *
+         * <pre class="prettyprint">
+         * Notification notif = new Notification.Builder(mContext)
+         *         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+         *         .setContentText(subject)
+         *         .setSmallIcon(R.drawable.new_mail);
+         *         .apply(new WearableNotificationExtensions.Builder()
+         *                 .setContentIcon(R.drawable.new_mail)
+         *                 .build())
+         *         .build();
+         * NotificationManager notificationManger =
+         *         (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+         * notificationManager.notify(0, notif);</pre>
+         */
+        public Builder() {
+        }
+
+        /**
+         * Create a {@link Builder} by reading wearable extensions present on an
+         * existing {@code WearableNotificationExtensions} object.
+         * @param other the existing extensions to inspect.
+         */
+        public Builder(WearableNotificationExtensions other) {
+            Collections.addAll(mActions, other.mActions);
+            mFlags = other.mFlags;
+            mDisplayIntent = other.mDisplayIntent;
+            Collections.addAll(mPages, other.mPages);
+            mBackground = other.mBackground;
+            mContentIcon = other.mContentIcon;
+            mContentIconGravity = other.mContentIconGravity;
+            mContentActionIndex = other.mContentActionIndex;
+            mCustomContentHeight = other.mCustomContentHeight;
+            mGravity = other.mGravity;
+        }
+
+        /**
+         * Add a wearable action to this notification.
+         *
+         * <p>When wearable actions are added using this method, the set of actions that
+         * show on a wearable device splits from devices that only show actions added
+         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+         * of which actions display on different devices.
+         *
+         * @param action the action to add to this notification
+         * @return this object for method chaining
+         * @see Notification.Action
+         */
+        public Builder addAction(Notification.Action action) {
+            mActions.add(action);
+            return this;
+        }
+
+        /**
+         * Adds wearable actions to this notification.
+         *
+         * <p>When wearable actions are added using this method, the set of actions that
+         * show on a wearable device splits from devices that only show actions added
+         * using {@link android.app.Notification.Builder#addAction}. This allows for customization
+         * of which actions display on different devices.
+         *
+         * @param actions the actions to add to this notification
+         * @return this object for method chaining
+         * @see Notification.Action
+         */
+        public Builder addActions(List<Notification.Action> actions) {
+            mActions.addAll(actions);
+            return this;
+        }
+
+        /**
+         * Clear all wearable actions present on this builder.
+         * @return this object for method chaining.
+         * @see #addAction
+         */
+        public Builder clearActions() {
+            mActions.clear();
+            return this;
+        }
+
+        /**
+         * Set an intent to launch inside of an activity view when displaying
+         * this notification. This {@link android.app.PendingIntent} should be for an activity.
+         *
+         * @param intent the {@link android.app.PendingIntent} for an activity
+         * @return this object for method chaining
+         * @see WearableNotificationExtensions#getDisplayIntent
+         */
+        public Builder setDisplayIntent(PendingIntent intent) {
+            mDisplayIntent = intent;
+            return this;
+        }
+
+        /**
+         * Add an additional page of content to display with this notification. The current
+         * notification forms the first page, and pages added using this function form
+         * subsequent pages. This field can be used to separate a notification into multiple
+         * sections.
+         *
+         * @param page the notification to add as another page
+         * @return this object for method chaining
+         * @see WearableNotificationExtensions#getPages
+         */
+        public Builder addPage(Notification page) {
+            mPages.add(page);
+            return this;
+        }
+
+        /**
+         * Add additional pages of content to display with this notification. The current
+         * notification forms the first page, and pages added using this function form
+         * subsequent pages. This field can be used to separate a notification into multiple
+         * sections.
+         *
+         * @param pages a list of notifications
+         * @return this object for method chaining
+         * @see WearableNotificationExtensions#getPages
+         */
+        public Builder addPages(List<Notification> pages) {
+            mPages.addAll(pages);
+            return this;
+        }
+
+        /**
+         * Clear all additional pages present on this builder.
+         * @return this object for method chaining.
+         * @see #addPage
+         */
+        public Builder clearPages() {
+            mPages.clear();
+            return this;
+        }
+
+        /**
+         * Set a background image to be displayed behind the notification content.
+         * Contrary to the {@link Notification.BigPictureStyle}, this background
+         * will work with any notification style.
+         *
+         * @param background the background bitmap
+         * @return this object for method chaining
+         * @see WearableNotificationExtensions#getBackground
+         */
+        public Builder setBackground(Bitmap background) {
+            mBackground = background;
+            return this;
+        }
+
+        /**
+         * Set an icon that goes with the content of this notification.
+         */
+        public Builder setContentIcon(int icon) {
+            mContentIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set the gravity that the content icon should have within the notification display.
+         * Supported values include {@link Gravity#START} and {@link Gravity#END}. The default
+         * value is {@link android.view.Gravity#END}.
+         * @see #setContentIcon
+         */
+        public Builder setContentIconGravity(int contentIconGravity) {
+            mContentIconGravity = contentIconGravity;
+            return this;
+        }
+
+        /**
+         * Set an action from this notification's actions to be clickable with the content of
+         * this notification page. This action will no longer display separately from the
+         * notification content. This action's icon will display with optional subtext provided
+         * by the action's title.
+         * @param actionIndex The index of the action to hoist on the current notification page.
+         *                    If wearable actions are present, this index will apply to that list,
+         *                    otherwise it will apply to the main notification's actions list.
+         */
+        public Builder setContentAction(int actionIndex) {
+            mContentActionIndex = actionIndex;
+            return this;
+        }
+
+        /**
+         * Set the gravity that this notification should have within the available viewport space.
+         * Supported values include {@link Gravity#TOP}, {@link Gravity#CENTER_VERTICAL} and
+         * {@link Gravity#BOTTOM}. The default value is {@link Gravity#BOTTOM}.
+         */
+        public Builder setGravity(int gravity) {
+            mGravity = gravity;
+            return this;
+        }
+
+        /**
+         * Set the custom size preset for the display of this notification out of the available
+         * presets found in {@link WearableNotificationExtensions}, e.g. {@link #SIZE_LARGE}.
+         * <p>Some custom size presets are only applicable for custom display notifications created
+         * using {@link Builder#setDisplayIntent}. Check the documentation for the preset in
+         * question. See also {@link Builder#setCustomContentHeight} and
+         * {@link #getCustomSizePreset}.
+         */
+        public Builder setCustomSizePreset(int sizePreset) {
+            mFlags &= ~SIZE_FLAGS_MASK;  // Clear existing size preset bits
+            mFlags |= sizePreset & SIZE_FLAGS_MASK;  // And merge in the new value with protection
+            return this;
+        }
+
+        /**
+         * Set the custom height in pixels for the display of this notification's content.
+         * <p>This option is only available for custom display notifications created
+         * using {@link Builder#setDisplayIntent}. See also {@link Builder#setCustomSizePreset} and
+         * {@link #getCustomContentHeight}.
+         */
+        public Builder setCustomContentHeight(int height) {
+            mCustomContentHeight = height;
+            return this;
+        }
+
+        /**
+         * Set whether the scrolling position for the contents of this notification should start
+         * at the bottom of the contents instead of the top when the contents are too long to
+         * display within the screen.  Default is false (start scroll at the top).
+         */
+        public Builder setStartScrollBottom(boolean startScrollBottom) {
+            setFlag(FLAG_START_SCROLL_BOTTOM, startScrollBottom);
+            return this;
+        }
+
+        /**
+         * Set whether the content intent is available when the wearable device is not connected
+         * to a companion device.  The user can still trigger this intent when the wearable device
+         * is offline, but a visual hint will indicate that the content intent may not be available.
+         * Defaults to true.
+         */
+        public Builder setContentIntentAvailableOffline(boolean contentIntentAvailableOffline) {
+            setFlag(FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE, contentIntentAvailableOffline);
+            return this;
+        }
+
+        /**
+         * Set a hint that this notification's icon should not be displayed.
+         * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
+         * @return this object for method chaining
+         */
+        public Builder setHintHideIcon(boolean hintHideIcon) {
+            setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
+            return this;
+        }
+
+        /**
+         * Set a visual hint that only the background image of this notification should be
+         * displayed, and other semantic content should be hidden. This hint is only applicable
+         * to sub-pages added using {@link #addPage}.
+         */
+        public Builder setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
+            setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
+            return this;
+        }
+
+        /**
+         * Build a new {@link WearableNotificationExtensions} object with the extensions
+         * currently present on this builder.
+         * @return the extensions object.
+         */
+        public WearableNotificationExtensions build() {
+            return new WearableNotificationExtensions(
+                    mActions.toArray(new Notification.Action[mActions.size()]), mFlags,
+                    mDisplayIntent, mPages.toArray(new Notification[mPages.size()]),
+                    mBackground, mContentIcon, mContentIconGravity, mContentActionIndex,
+                    mCustomContentHeight, mGravity);
+        }
+
+        private void setFlag(int mask, boolean value) {
+            if (value) {
+                mFlags |= mask;
+            } else {
+                mFlags &= ~mask;
+            }
+        }
+    }
+
+    public static final Creator<WearableNotificationExtensions> CREATOR =
+            new Creator<WearableNotificationExtensions>() {
+        @Override
+        public WearableNotificationExtensions createFromParcel(Parcel in) {
+            return new WearableNotificationExtensions(in);
+        }
+
+        @Override
+        public WearableNotificationExtensions[] newArray(int size) {
+            return new WearableNotificationExtensions[size];
+        }
+    };
+}