diff options
| author | Mady Mellor <madym@google.com> | 2019-01-09 17:11:37 -0800 |
|---|---|---|
| committer | Mady Mellor <madym@google.com> | 2019-01-15 10:28:31 -0800 |
| commit | c39b4aedb0a382d4c9717bbbc81e11c8dfdc3632 (patch) | |
| tree | ca28ce57fb9c243e3ddbd3d0673dda9771f18155 /core/java/android | |
| parent | 4a09436a63ba29888c39ba7ac631c6473f349a66 (diff) | |
Create BubbleMetadata use it instead of app overlay intent
* BubbleMetadata encapsulates necessary info to display a bubble
* Replaces app overlay intent usages with BubbleMetadata
* Renames existing bubble APIs to use 'bubble' rather than 'app overlay'
Bug: 111236845
Test: existing tests pass
Change-Id: I6a85d3c41dda47139fb8d960cadf1c8e109cf29b
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/app/INotificationManager.aidl | 6 | ||||
| -rw-r--r-- | core/java/android/app/Notification.java | 217 | ||||
| -rw-r--r-- | core/java/android/app/NotificationChannel.java | 50 | ||||
| -rw-r--r-- | core/java/android/app/NotificationManager.java | 8 |
4 files changed, 230 insertions, 51 deletions
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(); } |
