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

Commit 7c73162d authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Create BubbleMetadata use it instead of app overlay intent"

parents fb89e1da c39b4aed
Loading
Loading
Loading
Loading
+24 −5
Original line number Diff line number Diff line
@@ -5245,8 +5245,8 @@ package android.app {
    method public android.app.Notification clone();
    method public int describeContents();
    method public boolean getAllowSystemGeneratedContextualActions();
    method public android.app.PendingIntent getAppOverlayIntent();
    method public int getBadgeIconType();
    method public android.app.Notification.BubbleMetadata getBubbleMetadata();
    method public java.lang.String getChannelId();
    method public java.lang.String getGroup();
    method public int getGroupAlertBehavior();
@@ -5459,6 +5459,25 @@ package android.app {
    method public android.app.Notification.BigTextStyle setSummaryText(java.lang.CharSequence);
  }
  public static final class Notification.BubbleMetadata implements android.os.Parcelable {
    method public int describeContents();
    method public int getDesiredHeight();
    method public android.graphics.drawable.Icon getIcon();
    method public android.app.PendingIntent getIntent();
    method public java.lang.CharSequence getTitle();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR;
  }
  public static class Notification.BubbleMetadata.Builder {
    ctor public Notification.BubbleMetadata.Builder();
    method public android.app.Notification.BubbleMetadata build();
    method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int);
    method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon);
    method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent);
    method public android.app.Notification.BubbleMetadata.Builder setTitle(java.lang.CharSequence);
  }
  public static class Notification.Builder {
    ctor public Notification.Builder(android.content.Context, java.lang.String);
    ctor public deprecated Notification.Builder(android.content.Context);
@@ -5478,9 +5497,9 @@ package android.app {
    method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
    method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
    method public android.app.Notification.Builder setAllowSystemGeneratedContextualActions(boolean);
    method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
    method public android.app.Notification.Builder setAutoCancel(boolean);
    method public android.app.Notification.Builder setBadgeIconType(int);
    method public android.app.Notification.Builder setBubbleMetadata(android.app.Notification.BubbleMetadata);
    method public android.app.Notification.Builder setCategory(java.lang.String);
    method public android.app.Notification.Builder setChannelId(java.lang.String);
    method public android.app.Notification.Builder setChronometerCountDown(boolean);
@@ -5695,8 +5714,8 @@ package android.app {
  public final class NotificationChannel implements android.os.Parcelable {
    ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
    method public boolean canBubble();
    method public boolean canBypassDnd();
    method public boolean canOverlayApps();
    method public boolean canShowBadge();
    method public int describeContents();
    method public void enableLights(boolean);
@@ -5712,7 +5731,7 @@ package android.app {
    method public android.net.Uri getSound();
    method public long[] getVibrationPattern();
    method public boolean hasUserSetImportance();
    method public void setAllowAppOverlay(boolean);
    method public void setAllowBubbles(boolean);
    method public void setBypassDnd(boolean);
    method public void setDescription(java.lang.String);
    method public void setGroup(java.lang.String);
@@ -5746,7 +5765,7 @@ package android.app {
  public class NotificationManager {
    method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
    method public boolean areAppOverlaysAllowed();
    method public boolean areBubblesAllowed();
    method public boolean areNotificationsEnabled();
    method public boolean canNotifyAsPackage(java.lang.String);
    method public void cancel(int);
+3 −3
Original line number Diff line number Diff line
@@ -65,9 +65,9 @@ interface INotificationManager
    boolean areNotificationsEnabled(String pkg);
    int getPackageImportance(String pkg);

    void setAppOverlaysAllowed(String pkg, int uid, boolean allowed);
    boolean areAppOverlaysAllowed(String pkg);
    boolean areAppOverlaysAllowedForPackage(String pkg, int uid);
    void setBubblesAllowed(String pkg, int uid, boolean allowed);
    boolean areBubblesAllowed(String pkg);
    boolean areBubblesAllowedForPackage(String pkg, int uid);

    void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
    void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
+198 −19
Original line number Diff line number Diff line
@@ -1276,7 +1276,7 @@ public class Notification implements Parcelable
    private String mShortcutId;
    private CharSequence mSettingsText;

    private PendingIntent mAppOverlayIntent;
    private BubbleMetadata mBubbleMetadata;

    /** @hide */
    @IntDef(prefix = { "GROUP_ALERT_" }, value = {
@@ -2278,7 +2278,7 @@ public class Notification implements Parcelable

        mGroupAlertBehavior = parcel.readInt();
        if (parcel.readInt() != 0) {
            mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
            mBubbleMetadata = BubbleMetadata.CREATOR.createFromParcel(parcel);
        }

        mAllowSystemGeneratedContextualActions = parcel.readBoolean();
@@ -2396,7 +2396,7 @@ public class Notification implements Parcelable
        that.mBadgeIcon = this.mBadgeIcon;
        that.mSettingsText = this.mSettingsText;
        that.mGroupAlertBehavior = this.mGroupAlertBehavior;
        that.mAppOverlayIntent = this.mAppOverlayIntent;
        that.mBubbleMetadata = this.mBubbleMetadata;
        that.mAllowSystemGeneratedContextualActions = this.mAllowSystemGeneratedContextualActions;

        if (!heavy) {
@@ -2719,9 +2719,9 @@ public class Notification implements Parcelable

        parcel.writeInt(mGroupAlertBehavior);

        if (mAppOverlayIntent != null) {
        if (mBubbleMetadata != null) {
            parcel.writeInt(1);
            mAppOverlayIntent.writeToParcel(parcel, 0);
            mBubbleMetadata.writeToParcel(parcel, 0);
        } else {
            parcel.writeInt(0);
        }
@@ -3141,11 +3141,11 @@ public class Notification implements Parcelable
    }

    /**
     * Returns the intent that will be used to display app content in a floating window over the
     * existing foreground activity.
     * Returns the bubble metadata that will be used to display app content in a floating window
     * over the existing foreground activity.
     */
    public PendingIntent getAppOverlayIntent() {
        return mAppOverlayIntent;
    public BubbleMetadata getBubbleMetadata() {
        return mBubbleMetadata;
    }

    /**
@@ -3508,19 +3508,18 @@ public class Notification implements Parcelable
        }

        /**
         * Sets the intent that will be used to display app content in a floating window
         * over the existing foreground activity.
         * Sets the {@link BubbleMetadata} that will be used to display app content in a floating
         * window over the existing foreground activity.
         *
         * <p>This intent will be ignored unless this notification is posted to a channel that
         * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
         * <p>This data will be ignored unless the notification is posted to a channel that
         * allows {@link NotificationChannel#canBubble() bubbles}.</p>
         *
         * <p>Notifications with a valid and allowed app overlay intent will be displayed as
         * floating windows outside of the notification shade on unlocked devices. When a user
         * interacts with one of these windows, this app overlay intent will be invoked and
         * displayed.</p>
         * <b>Notifications with a valid and allowed bubble metadata will display in collapsed state
         * outside of the notification shade on unlocked devices. When a user interacts with the
         * collapsed state, the bubble intent will be invoked and displayed.</b>
         */
        public Builder setAppOverlayIntent(PendingIntent intent) {
            mN.mAppOverlayIntent = intent;
        public Builder setBubbleMetadata(BubbleMetadata data) {
            mN.mBubbleMetadata = data;
            return this;
        }

@@ -8422,6 +8421,186 @@ public class Notification implements Parcelable
        }
    }

    /**
     * Encapsulates the information needed to display a notification as a bubble.
     *
     * <p>A bubble is used to display app content in a floating window over the existing
     * foreground activity. A bubble has a collapsed state represented by an icon,
     * {@link BubbleMetadata.Builder#setIcon(Icon)} and an expanded state which is populated
     * via {@link BubbleMetadata.Builder#setIntent(PendingIntent)}.</p>
     *
     * <b>Notifications with a valid and allowed bubble will display in collapsed state
     * outside of the notification shade on unlocked devices. When a user interacts with the
     * collapsed bubble, the bubble intent will be invoked and displayed.</b>
     *
     * @see Notification.Builder#setBubbleMetadata(BubbleMetadata)
     */
    public static final class BubbleMetadata implements Parcelable {

        private PendingIntent mPendingIntent;
        private CharSequence mTitle;
        private Icon mIcon;
        private int mDesiredHeight;

        private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) {
            mPendingIntent = intent;
            mTitle = title;
            mIcon = icon;
            mDesiredHeight = height;
        }

        private BubbleMetadata(Parcel in) {
            mPendingIntent = PendingIntent.CREATOR.createFromParcel(in);
            mTitle = in.readCharSequence();
            mIcon = Icon.CREATOR.createFromParcel(in);
            mDesiredHeight = in.readInt();
        }

        /**
         * @return the pending intent used to populate the floating window for this bubble.
         */
        public PendingIntent getIntent() {
            return mPendingIntent;
        }

        /**
         * @return the title that will appear along with the app content defined by
         * {@link #getIntent()} for this bubble.
         */
        public CharSequence getTitle() {
            return mTitle;
        }

        /**
         * @return the icon that will be displayed for this bubble when it is collapsed.
         */
        public Icon getIcon() {
            return mIcon;
        }

        /**
         * @return the ideal height for the floating window that app content defined by
         * {@link #getIntent()} for this bubble.
         */
        public int getDesiredHeight() {
            return mDesiredHeight;
        }

        public static final Parcelable.Creator<BubbleMetadata> CREATOR =
                new Parcelable.Creator<BubbleMetadata>() {

                    @Override
                    public BubbleMetadata createFromParcel(Parcel source) {
                        return new BubbleMetadata(source);
                    }

                    @Override
                    public BubbleMetadata[] newArray(int size) {
                        return new BubbleMetadata[size];
                    }
                };

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            mPendingIntent.writeToParcel(out, 0);
            out.writeCharSequence(mTitle);
            mIcon.writeToParcel(out, 0);
            out.writeInt(mDesiredHeight);
        }

        /**
         * Builder to construct a {@link BubbleMetadata} object.
         */
        public static class Builder {

            private PendingIntent mPendingIntent;
            private CharSequence mTitle;
            private Icon mIcon;
            private int mDesiredHeight;

            /**
             * Constructs a new builder object.
             */
            public Builder() {
            }

            /**
             * Sets the intent that will be used when the bubble is expanded. This will display the
             * app content in a floating window over the existing foreground activity.
             */
            public BubbleMetadata.Builder setIntent(PendingIntent intent) {
                if (intent == null) {
                    throw new IllegalArgumentException("Bubble requires non-null pending intent");
                }
                mPendingIntent = intent;
                return this;
            }

            /**
             * Sets the title that will appear along with the app content for this bubble.
             *
             * <p>A title is required and should expect to fit on a single line and make sense when
             * shown with the content defined by {@link #setIntent(PendingIntent)}.</p>
             */
            public BubbleMetadata.Builder setTitle(CharSequence title) {
                if (TextUtils.isEmpty(title)) {
                    throw new IllegalArgumentException("Bubbles require non-null or empty title");
                }
                mTitle = title;
                return this;
            }

            /**
             * Sets the icon that will represent the bubble when it is collapsed.
             *
             * <p>An icon is required and should be representative of the content within the bubble.
             * If your app produces multiple bubbles, the image should be unique for each of them.
             * </p>
             */
            public BubbleMetadata.Builder setIcon(Icon icon) {
                if (icon == null) {
                    throw new IllegalArgumentException("Bubbles require non-null icon");
                }
                mIcon = icon;
                return this;
            }

            /**
             * Sets the desired height for the app content defined by
             * {@link #setIntent(PendingIntent)}, this height may not be respected if there is not
             * enough space on the screen or if the provided height is too small to be useful.
             */
            public BubbleMetadata.Builder setDesiredHeight(int height) {
                mDesiredHeight = Math.max(height, 0);
                return this;
            }

            /**
             * Creates the {@link BubbleMetadata} defined by this builder.
             * <p>Will throw {@link IllegalStateException} if required fields have not been set
             * on this builder.</p>
             */
            public BubbleMetadata build() {
                if (mPendingIntent == null) {
                    throw new IllegalStateException("Must supply pending intent to bubble");
                }
                if (TextUtils.isEmpty(mTitle)) {
                    throw new IllegalStateException("Must supply a title for the bubble");
                }
                if (mIcon == null) {
                    throw new IllegalStateException("Must supply an icon for the bubble");
                }
                return new BubbleMetadata(mPendingIntent, mTitle, mIcon, mDesiredHeight);
            }
        }
    }


    // When adding a new Style subclass here, don't forget to update
    // Builder.getNotificationStyleClass.

+25 −25
Original line number Diff line number Diff line
@@ -85,7 +85,7 @@ public final class NotificationChannel implements Parcelable {
    private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
    private static final String ATT_GROUP = "group";
    private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
    private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
    private static final String ATT_ALLOW_BUBBLE = "allow_bubble";
    private static final String DELIMITER = ",";

    /**
@@ -121,7 +121,7 @@ public final class NotificationChannel implements Parcelable {
    /**
     * @hide
     */
    public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
    public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100;

    /**
     * @hide
@@ -134,7 +134,7 @@ public final class NotificationChannel implements Parcelable {
            USER_LOCKED_VIBRATION,
            USER_LOCKED_SOUND,
            USER_LOCKED_SHOW_BADGE,
            USER_LOCKED_ALLOW_APP_OVERLAY
            USER_LOCKED_ALLOW_BUBBLE
    };

    private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -144,7 +144,7 @@ public final class NotificationChannel implements Parcelable {
            NotificationManager.IMPORTANCE_UNSPECIFIED;
    private static final boolean DEFAULT_DELETED = false;
    private static final boolean DEFAULT_SHOW_BADGE = true;
    private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
    private static final boolean DEFAULT_ALLOW_BUBBLE = true;

    @UnsupportedAppUsage
    private final String mId;
@@ -168,7 +168,7 @@ public final class NotificationChannel implements Parcelable {
    private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
    // If this is a blockable system notification channel.
    private boolean mBlockableSystem = false;
    private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
    private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
    private boolean mImportanceLockedByOEM;

    /**
@@ -231,7 +231,7 @@ public final class NotificationChannel implements Parcelable {
        mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
        mLightColor = in.readInt();
        mBlockableSystem = in.readBoolean();
        mAllowAppOverlay = in.readBoolean();
        mAllowBubbles = in.readBoolean();
        mImportanceLockedByOEM = in.readBoolean();
    }

@@ -285,7 +285,7 @@ public final class NotificationChannel implements Parcelable {
        }
        dest.writeInt(mLightColor);
        dest.writeBoolean(mBlockableSystem);
        dest.writeBoolean(mAllowAppOverlay);
        dest.writeBoolean(mAllowBubbles);
        dest.writeBoolean(mImportanceLockedByOEM);
    }

@@ -480,7 +480,7 @@ public final class NotificationChannel implements Parcelable {

    /**
     * Sets whether notifications posted to this channel can appear outside of the notification
     * shade, floating over other apps' content.
     * shade, floating over other apps' content as a bubble.
     *
     * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
     * channels whose {@link #getImportance() importance} is <
@@ -488,10 +488,10 @@ public final class NotificationChannel implements Parcelable {
     *
     * <p>Only modifiable before the channel is submitted to
     *      * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
     * @see Notification#getAppOverlayIntent()
     * @see Notification#getBubbleMetadata()
     */
    public void setAllowAppOverlay(boolean allowAppOverlay) {
        mAllowAppOverlay = allowAppOverlay;
    public void setAllowBubbles(boolean allowBubbles) {
        mAllowBubbles = allowBubbles;
    }

    /**
@@ -610,16 +610,16 @@ public final class NotificationChannel implements Parcelable {
     * Returns whether notifications posted to this channel can display outside of the notification
     * shade, in a floating window on top of other apps.
     */
    public boolean canOverlayApps() {
        return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
    public boolean canBubble() {
        return isBubbleAllowed() && getImportance() >= IMPORTANCE_HIGH;
    }

    /**
     * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
     * Like {@link #canBubble()}, but only checks the permission, not the importance.
     * @hide
     */
    public boolean isAppOverlayAllowed() {
        return mAllowAppOverlay;
    public boolean isBubbleAllowed() {
        return mAllowBubbles;
    }

    /**
@@ -719,7 +719,7 @@ public final class NotificationChannel implements Parcelable {
        lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
        setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
        setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
        setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
        setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
    }

    @Nullable
@@ -838,8 +838,8 @@ public final class NotificationChannel implements Parcelable {
        if (isBlockableSystem()) {
            out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
        }
        if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
            out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
        if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
            out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
        }

        out.endTag(null, TAG_CHANNEL);
@@ -883,7 +883,7 @@ public final class NotificationChannel implements Parcelable {
        record.put(ATT_DELETED, Boolean.toString(isDeleted()));
        record.put(ATT_GROUP, getGroup());
        record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
        record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
        record.put(ATT_ALLOW_BUBBLE, canBubble());
        return record;
    }

@@ -983,7 +983,7 @@ public final class NotificationChannel implements Parcelable {
                && mShowBadge == that.mShowBadge
                && isDeleted() == that.isDeleted()
                && isBlockableSystem() == that.isBlockableSystem()
                && mAllowAppOverlay == that.mAllowAppOverlay
                && mAllowBubbles == that.mAllowBubbles
                && Objects.equals(getId(), that.getId())
                && Objects.equals(getName(), that.getName())
                && Objects.equals(mDesc, that.mDesc)
@@ -1000,7 +1000,7 @@ public final class NotificationChannel implements Parcelable {
                getLockscreenVisibility(), getSound(), mLights, getLightColor(),
                getUserLockedFields(),
                isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
                getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay,
                getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
                mImportanceLockedByOEM);
        result = 31 * result + Arrays.hashCode(mVibration);
        return result;
@@ -1028,7 +1028,7 @@ public final class NotificationChannel implements Parcelable {
                + ", mGroup='" + mGroup + '\''
                + ", mAudioAttributes=" + mAudioAttributes
                + ", mBlockableSystem=" + mBlockableSystem
                + ", mAllowAppOverlay=" + mAllowAppOverlay
                + ", mAllowBubbles=" + mAllowBubbles
                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                + '}';
        pw.println(prefix + output);
@@ -1055,7 +1055,7 @@ public final class NotificationChannel implements Parcelable {
                + ", mGroup='" + mGroup + '\''
                + ", mAudioAttributes=" + mAudioAttributes
                + ", mBlockableSystem=" + mBlockableSystem
                + ", mAllowAppOverlay=" + mAllowAppOverlay
                + ", mAllowBubbles=" + mAllowBubbles
                + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
                + '}';
    }
@@ -1090,7 +1090,7 @@ public final class NotificationChannel implements Parcelable {
            mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
        }
        proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
        proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);

        proto.end(token);
    }
+4 −4
Original line number Diff line number Diff line
@@ -1080,14 +1080,14 @@ public class NotificationManager {
     * notification shade, floating over other apps' content.
     *
     * <p>This value will be ignored for notifications that are posted to channels that do not
     * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
     * allow bubbles ({@link NotificationChannel#canBubble()}.
     *
     * @see Notification#getAppOverlayIntent()
     * @see Notification#getBubbleMetadata()
     */
    public boolean areAppOverlaysAllowed() {
    public boolean areBubblesAllowed() {
        INotificationManager service = getService();
        try {
            return service.areAppOverlaysAllowed(mContext.getPackageName());
            return service.areBubblesAllowed(mContext.getPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
Loading