diff options
12 files changed, 336 insertions, 111 deletions
diff --git a/api/current.txt b/api/current.txt index 00d4cb5f97dd..d495cf380c0c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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); diff --git a/api/system-current.txt b/api/system-current.txt index f6866041ec13..3b2ee5969ffa 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1284,8 +1284,8 @@ package android.content { field public static final java.lang.String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE"; field public static final java.lang.String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES"; - field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME"; + field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final java.lang.String EXTRA_REASON = "android.intent.extra.REASON"; field public static final java.lang.String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final java.lang.String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 163be8efc8fd..199c1338c50c 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -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); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index b657a916604b..72819cb1493b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -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. diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 950e9aa939f8..e95d62fc96eb 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -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); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index aad32532bbe8..43614feb28a4 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -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(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index d7bf77da1011..957d772be730 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -284,8 +284,9 @@ public class BubbleController { @Nullable private PendingIntent getAppOverlayIntent(NotificationEntry notif) { Notification notification = notif.notification.getNotification(); - if (canLaunchInActivityView(notification.getAppOverlayIntent())) { - return notification.getAppOverlayIntent(); + if (canLaunchInActivityView(notification.getBubbleMetadata() != null + ? notification.getBubbleMetadata().getIntent() : null)) { + return notification.getBubbleMetadata().getIntent(); } else if (shouldUseContentIntent(mContext) && canLaunchInActivityView(notification.contentIntent)) { Log.d(TAG, "[addBubble " + notif.key @@ -446,15 +447,16 @@ public class BubbleController { StatusBarNotification n = entry.notification; boolean canAppOverlay = false; try { - canAppOverlay = mNotificationManagerService.areAppOverlaysAllowedForPackage( + canAppOverlay = mNotificationManagerService.areBubblesAllowedForPackage( n.getPackageName(), n.getUid()); } catch (RemoteException e) { Log.w(TAG, "Error calling NoMan to determine if app can overlay", e); } boolean canChannelOverlay = mNotificationEntryManager.getNotificationData().getChannel( - entry.key).canOverlayApps(); - boolean hasOverlayIntent = n.getNotification().getAppOverlayIntent() != null; + entry.key).canBubble(); + boolean hasOverlayIntent = n.getNotification().getBubbleMetadata() != null + && n.getNotification().getBubbleMetadata().getIntent() != null; return hasOverlayIntent && canChannelOverlay && canAppOverlay; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index 2b13f864b9a5..89525944b685 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -27,6 +27,7 @@ import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Icon; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; @@ -220,8 +221,7 @@ public class NotificationTestHelper { notificationBuilder.setGroup(groupKey); } if (isBubble) { - PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); - notificationBuilder.setAppOverlayIntent(bubbleIntent); + notificationBuilder.setBubbleMetadata(makeBubbleMetadata()); } return notificationBuilder.build(); } @@ -282,4 +282,14 @@ public class NotificationTestHelper { mGroupManager.onEntryAdded(entry); return row; } + + private Notification.BubbleMetadata makeBubbleMetadata() { + PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); + return new Notification.BubbleMetadata.Builder() + .setIntent(bubbleIntent) + .setTitle("bubble title") + .setIcon(Icon.createWithResource(mContext, 1)) + .setDesiredHeight(314) + .build(); + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 20eebe7bf2fc..7323e93ebf84 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2311,22 +2311,22 @@ public class NotificationManagerService extends SystemService { } @Override - public boolean areAppOverlaysAllowed(String pkg) { - return areAppOverlaysAllowedForPackage(pkg, Binder.getCallingUid()); + public boolean areBubblesAllowed(String pkg) { + return areBubblesAllowedForPackage(pkg, Binder.getCallingUid()); } @Override - public boolean areAppOverlaysAllowedForPackage(String pkg, int uid) { - enforceSystemOrSystemUIOrSamePackage("Caller not system or systemui or same package", - pkg); - return mPreferencesHelper.areAppOverlaysAllowed(pkg, uid); + public boolean areBubblesAllowedForPackage(String pkg, int uid) { + enforceSystemOrSystemUIOrSamePackage(pkg, + "Caller not system or systemui or same package"); + return mPreferencesHelper.areBubblessAllowed(pkg, uid); } @Override - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { checkCallerIsSystem(); - mPreferencesHelper.setAppOverlaysAllowed(pkg, uid, allowed); + mPreferencesHelper.setBubblesAllowed(pkg, uid, allowed); handleSavePolicyFile(); } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 28f6972636be..7a21aa208dfd 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -81,7 +81,7 @@ public class PreferencesHelper implements RankingConfig { private static final String ATT_NAME = "name"; private static final String ATT_UID = "uid"; private static final String ATT_ID = "id"; - private static final String ATT_APP_OVERLAY = "overlay"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; private static final String ATT_PRIORITY = "priority"; private static final String ATT_VISIBILITY = "visibility"; private static final String ATT_IMPORTANCE = "importance"; @@ -94,8 +94,9 @@ public class PreferencesHelper implements RankingConfig { private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE; private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED; 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; private static final boolean DEFAULT_OEM_LOCKED_IMPORTANCE = false; + /** * Default value for what fields are user locked. See {@link LockableAppFields} for all lockable * fields. @@ -108,7 +109,7 @@ public class PreferencesHelper implements RankingConfig { @IntDef({LockableAppFields.USER_LOCKED_IMPORTANCE}) public @interface LockableAppFields { int USER_LOCKED_IMPORTANCE = 0x00000001; - int USER_LOCKED_APP_OVERLAY = 0x00000002; + int USER_LOCKED_BUBBLE = 0x00000002; } // pkg|uid => PackagePreferences @@ -176,7 +177,7 @@ public class PreferencesHelper implements RankingConfig { XmlUtils.readBooleanAttribute( parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), XmlUtils.readBooleanAttribute( - parser, ATT_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY)); + parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); r.importance = XmlUtils.readIntAttribute( parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); r.priority = XmlUtils.readIntAttribute( @@ -272,11 +273,11 @@ public class PreferencesHelper implements RankingConfig { private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) { return getOrCreatePackagePreferences(pkg, uid, DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE, - DEFAULT_ALLOW_APP_OVERLAY); + DEFAULT_ALLOW_BUBBLE); } private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance, - int priority, int visibility, boolean showBadge, boolean allowAppOverlay) { + int priority, int visibility, boolean showBadge, boolean allowBubble) { final String key = packagePreferencesKey(pkg, uid); synchronized (mPackagePreferences) { PackagePreferences @@ -290,7 +291,7 @@ public class PreferencesHelper implements RankingConfig { r.priority = priority; r.visibility = visibility; r.showBadge = showBadge; - r.appOverlay = allowAppOverlay; + r.allowBubble = allowBubble; try { createDefaultChannelIfNeeded(r); @@ -392,7 +393,7 @@ public class PreferencesHelper implements RankingConfig { || r.channels.size() > 0 || r.groups.size() > 0 || r.delegate != null - || r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY; + || r.allowBubble != DEFAULT_ALLOW_BUBBLE; if (hasNonDefaultSettings) { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); @@ -405,8 +406,8 @@ public class PreferencesHelper implements RankingConfig { if (r.visibility != DEFAULT_VISIBILITY) { out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); } - if (r.appOverlay != DEFAULT_ALLOW_APP_OVERLAY) { - out.attribute(null, ATT_APP_OVERLAY, Boolean.toString(r.appOverlay)); + if (r.allowBubble != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(r.allowBubble)); } out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, @@ -452,14 +453,28 @@ public class PreferencesHelper implements RankingConfig { out.endTag(null, TAG_RANKING); } - public void setAppOverlaysAllowed(String pkg, int uid, boolean allowed) { + /** + * Sets whether bubbles are allowed. + * + * @param pkg the package to allow or not allow bubbles for. + * @param uid the uid to allow or not allow bubbles for. + * @param allowed whether bubbles are allowed. + */ + public void setBubblesAllowed(String pkg, int uid, boolean allowed) { PackagePreferences p = getOrCreatePackagePreferences(pkg, uid); - p.appOverlay = allowed; - p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_APP_OVERLAY; + p.allowBubble = allowed; + p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE; } - public boolean areAppOverlaysAllowed(String pkg, int uid) { - return getOrCreatePackagePreferences(pkg, uid).appOverlay; + /** + * Whether bubbles are allowed. + * + * @param pkg the package to check if bubbles are allowed for + * @param uid the uid to check if bubbles are allowed for. + * @return whether bubbles are allowed. + */ + public boolean areBubblessAllowed(String pkg, int uid) { + return getOrCreatePackagePreferences(pkg, uid).allowBubble; } public int getAppLockedFields(String pkg, int uid) { @@ -1232,8 +1247,8 @@ public class PreferencesHelper implements RankingConfig { if (original.canShowBadge() != update.canShowBadge()) { update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); } - if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) { - update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY); + if (original.isBubbleAllowed() != update.isBubbleAllowed()) { + update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); } } @@ -1654,7 +1669,7 @@ public class PreferencesHelper implements RankingConfig { int priority = DEFAULT_PRIORITY; int visibility = DEFAULT_VISIBILITY; boolean showBadge = DEFAULT_SHOW_BADGE; - boolean appOverlay = DEFAULT_ALLOW_APP_OVERLAY; + boolean allowBubble = DEFAULT_ALLOW_BUBBLE; int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS; boolean oemLockedImportance = DEFAULT_OEM_LOCKED_IMPORTANCE; List<String> futureOemLockedChannels = new ArrayList<>(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 83c1c7670338..94b21af65799 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3511,9 +3511,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testAppOverlay() throws Exception { - mBinderService.setAppOverlaysAllowed(PKG, mUid, false); - assertFalse(mBinderService.areAppOverlaysAllowedForPackage(PKG, mUid)); + public void testBubble() throws Exception { + mBinderService.setBubblesAllowed(PKG, mUid, false); + assertFalse(mBinderService.areBubblesAllowedForPackage(PKG, mUid)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 0fcfea716e8f..24a1f8c19f12 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -809,7 +809,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -826,7 +826,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); verify(mHandler, never()).requestSort(); } @@ -840,7 +840,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { channel.setBypassDnd(true); channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); channel.setShowBadge(true); - channel.setAllowAppOverlay(false); + channel.setAllowBubbles(false); int lockMask = 0; for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; @@ -857,7 +857,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(savedChannel.canBypassDnd()); assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); - assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps()); + assertEquals(channel.canBubble(), savedChannel.canBubble()); } @Test @@ -969,16 +969,16 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testLockFields_appOverlay() { + public void testLockFields_allowBubble() { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false); assertEquals(0, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false) .getUserLockedFields()); final NotificationChannel update = getChannel(); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true); - assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY, + assertEquals(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false) .getUserLockedFields()); } @@ -2161,30 +2161,30 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testAllowAppOverlay_defaults() throws Exception { - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + public void testAllowBubbles_defaults() throws Exception { + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertTrue(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); + assertTrue(mHelper.areBubblessAllowed(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); } @Test - public void testAllowAppOverlay_xml() throws Exception { - mHelper.setAppOverlaysAllowed(PKG_O, UID_O, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + public void testAllowBubbles_xml() throws Exception { + mHelper.setBubblesAllowed(PKG_O, UID_O, false); + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper); loadStreamXml(baos, false); - assertFalse(mHelper.areAppOverlaysAllowed(PKG_O, UID_O)); - assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_APP_OVERLAY, + assertFalse(mHelper.areBubblessAllowed(PKG_O, UID_O)); + assertEquals(PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE, mHelper.getAppLockedFields(PKG_O, UID_O)); } @@ -2290,14 +2290,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.lockChannelsForOEM(new String[] {PKG_O}); NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); - update.setAllowAppOverlay(false); + update.setAllowBubbles(false); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); assertEquals(IMPORTANCE_HIGH, mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); assertEquals(false, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canOverlayApps()); + mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); |