/* * Copyright (C) 2015 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.support.v7.app; import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; import android.annotation.TargetApi; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.support.annotation.RequiresApi; import android.support.annotation.RestrictTo; import android.support.v4.app.BundleCompat; import android.support.v4.app.NotificationBuilderWithBuilderAccessor; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.text.BidiFormatter; import android.support.v7.appcompat.R; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.TextAppearanceSpan; import android.widget.RemoteViews; import java.util.List; /** * An extension of {@link android.support.v4.app.NotificationCompat} which supports * {@link android.support.v7.app.NotificationCompat.MediaStyle}, * {@link android.support.v7.app.NotificationCompat.DecoratedCustomViewStyle}, * and {@link android.support.v7.app.NotificationCompat.DecoratedMediaCustomViewStyle}. * You should start using this variant if you need support any of these styles. */ public class NotificationCompat extends android.support.v4.app.NotificationCompat { /** * Extracts a {@link MediaSessionCompat.Token} from the extra values * in the {@link MediaStyle} {@link android.app.Notification notification}. * * @param notification The notification to extract a {@link MediaSessionCompat.Token} from. * @return The {@link MediaSessionCompat.Token} in the {@code notification} if it contains, * null otherwise. */ public static MediaSessionCompat.Token getMediaSession(Notification notification) { Bundle extras = getExtras(notification); if (extras != null) { if (Build.VERSION.SDK_INT >= 21) { Object tokenInner = extras.getParcelable(EXTRA_MEDIA_SESSION); if (tokenInner != null) { return MediaSessionCompat.Token.fromToken(tokenInner); } } else { IBinder tokenInner = BundleCompat.getBinder(extras, EXTRA_MEDIA_SESSION); if (tokenInner != null) { Parcel p = Parcel.obtain(); p.writeStrongBinder(tokenInner); p.setDataPosition(0); MediaSessionCompat.Token token = MediaSessionCompat.Token.CREATOR.createFromParcel(p); p.recycle(); return token; } } } return null; } @RequiresApi(24) @TargetApi(24) private static void addStyleToBuilderApi24(NotificationBuilderWithBuilderAccessor builder, android.support.v4.app.NotificationCompat.Builder b) { if (b.mStyle instanceof DecoratedCustomViewStyle) { NotificationCompatImpl24.addDecoratedCustomViewStyle(builder); } else if (b.mStyle instanceof DecoratedMediaCustomViewStyle) { NotificationCompatImpl24.addDecoratedMediaCustomViewStyle(builder); } else if (!(b.mStyle instanceof MessagingStyle)) { addStyleGetContentViewLollipop(builder, b); } } @RequiresApi(21) @TargetApi(21) private static RemoteViews addStyleGetContentViewLollipop( NotificationBuilderWithBuilderAccessor builder, android.support.v4.app.NotificationCompat.Builder b) { if (b.mStyle instanceof MediaStyle) { MediaStyle mediaStyle = (MediaStyle) b.mStyle; NotificationCompatImpl21.addMediaStyle(builder, mediaStyle.mActionsToShowInCompact, mediaStyle.mToken != null ? mediaStyle.mToken.getToken() : null); boolean hasContentView = b.getContentView() != null; // If we are on L/M the media notification will only be colored if the expanded version // is of media style, so we have to create a custom view for the collapsed version as // well in that case. boolean isMorL = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M; boolean createCustomContent = hasContentView || (isMorL && b.getBigContentView() != null); if (b.mStyle instanceof DecoratedMediaCustomViewStyle && createCustomContent) { RemoteViews contentViewMedia = NotificationCompatImplBase.overrideContentViewMedia( builder, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), b.mActions, mediaStyle.mActionsToShowInCompact, false /* no cancel button on L */, null /* cancelButtonIntent */, hasContentView /* isDecoratedCustomView */); if (hasContentView) { NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, contentViewMedia, b.getContentView()); } setBackgroundColor(b.mContext, contentViewMedia, b.getColor()); return contentViewMedia; } return null; } else if (b.mStyle instanceof DecoratedCustomViewStyle) { return getDecoratedContentView(b); } return addStyleGetContentViewJellybean(builder, b); } @RequiresApi(16) @TargetApi(16) private static RemoteViews addStyleGetContentViewJellybean( NotificationBuilderWithBuilderAccessor builder, android.support.v4.app.NotificationCompat.Builder b) { if (b.mStyle instanceof MessagingStyle) { addMessagingFallBackStyle((MessagingStyle) b.mStyle, builder, b); } return addStyleGetContentViewIcs(builder, b); } private static MessagingStyle.Message findLatestIncomingMessage(MessagingStyle style) { List<MessagingStyle.Message> messages = style.getMessages(); for (int i = messages.size() - 1; i >= 0; i--) { MessagingStyle.Message m = messages.get(i); // Incoming messages have a non-empty sender. if (!TextUtils.isEmpty(m.getSender())) { return m; } } if (!messages.isEmpty()) { // No incoming messages, fall back to outgoing message return messages.get(messages.size() - 1); } return null; } private static CharSequence makeMessageLine(android.support.v4.app.NotificationCompat.Builder b, MessagingStyle style, MessagingStyle.Message m) { BidiFormatter bidi = BidiFormatter.getInstance(); SpannableStringBuilder sb = new SpannableStringBuilder(); boolean afterLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; int color = afterLollipop || Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1 ? Color.BLACK : Color.WHITE; CharSequence replyName = m.getSender(); if (TextUtils.isEmpty(m.getSender())) { replyName = style.getUserDisplayName() == null ? "" : style.getUserDisplayName(); color = afterLollipop && b.getColor() != NotificationCompat.COLOR_DEFAULT ? b.getColor() : color; } CharSequence senderText = bidi.unicodeWrap(replyName); sb.append(senderText); sb.setSpan(makeFontColorSpan(color), sb.length() - senderText.length(), sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE /* flags */); CharSequence text = m.getText() == null ? "" : m.getText(); sb.append(" ").append(bidi.unicodeWrap(text)); return sb; } private static TextAppearanceSpan makeFontColorSpan(int color) { return new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(color), null); } private static void addMessagingFallBackStyle(MessagingStyle style, NotificationBuilderWithBuilderAccessor builder, android.support.v4.app.NotificationCompat.Builder b) { SpannableStringBuilder completeMessage = new SpannableStringBuilder(); List<MessagingStyle.Message> messages = style.getMessages(); boolean showNames = style.getConversationTitle() != null || hasMessagesWithoutSender(style.getMessages()); for (int i = messages.size() - 1; i >= 0; i--) { MessagingStyle.Message m = messages.get(i); CharSequence line; line = showNames ? makeMessageLine(b, style, m) : m.getText(); if (i != messages.size() - 1) { completeMessage.insert(0, "\n"); } completeMessage.insert(0, line); } NotificationCompatImplJellybean.addBigTextStyle(builder, completeMessage); } private static boolean hasMessagesWithoutSender( List<MessagingStyle.Message> messages) { for (int i = messages.size() - 1; i >= 0; i--) { MessagingStyle.Message m = messages.get(i); if (m.getSender() == null) { return true; } } return false; } @RequiresApi(14) @TargetApi(14) private static RemoteViews addStyleGetContentViewIcs( NotificationBuilderWithBuilderAccessor builder, android.support.v4.app.NotificationCompat.Builder b) { if (b.mStyle instanceof MediaStyle) { MediaStyle mediaStyle = (MediaStyle) b.mStyle; boolean isDecorated = b.mStyle instanceof DecoratedMediaCustomViewStyle && b.getContentView() != null; RemoteViews contentViewMedia = NotificationCompatImplBase.overrideContentViewMedia( builder, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), b.mActions, mediaStyle.mActionsToShowInCompact, mediaStyle.mShowCancelButton, mediaStyle.mCancelButtonIntent, isDecorated); if (isDecorated) { NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, contentViewMedia, b.getContentView()); return contentViewMedia; } } else if (b.mStyle instanceof DecoratedCustomViewStyle) { return getDecoratedContentView(b); } return null; } @RequiresApi(16) @TargetApi(16) private static void addBigStyleToBuilderJellybean(Notification n, android.support.v4.app.NotificationCompat.Builder b) { if (b.mStyle instanceof MediaStyle) { MediaStyle mediaStyle = (MediaStyle) b.mStyle; RemoteViews innerView = b.getBigContentView() != null ? b.getBigContentView() : b.getContentView(); boolean isDecorated = b.mStyle instanceof DecoratedMediaCustomViewStyle && innerView != null; NotificationCompatImplBase.overrideMediaBigContentView(n, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), 0, b.mActions, mediaStyle.mShowCancelButton, mediaStyle.mCancelButtonIntent, isDecorated); if (isDecorated) { NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.bigContentView, innerView); } } else if (b.mStyle instanceof DecoratedCustomViewStyle) { addDecoratedBigStyleToBuilderJellybean(n, b); } } private static RemoteViews getDecoratedContentView( android.support.v4.app.NotificationCompat.Builder b) { if (b.getContentView() == null) { // No special content view return null; } RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions( b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mNotification.icon, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), b.getColor(), R.layout.notification_template_custom_big, false /* fitIn1U */, null /* actions */); NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews, b.getContentView()); return remoteViews; } @RequiresApi(16) @TargetApi(16) private static void addDecoratedBigStyleToBuilderJellybean(Notification n, android.support.v4.app.NotificationCompat.Builder b) { RemoteViews bigContentView = b.getBigContentView(); RemoteViews innerView = bigContentView != null ? bigContentView : b.getContentView(); if (innerView == null) { // No expandable notification return; } RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions( b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, n.icon ,b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), b.getColor(), R.layout.notification_template_custom_big, false /* fitIn1U */, b.mActions); NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews, innerView); n.bigContentView = remoteViews; } @RequiresApi(21) @TargetApi(21) private static void addDecoratedHeadsUpToBuilderLollipop(Notification n, android.support.v4.app.NotificationCompat.Builder b) { RemoteViews headsUp = b.getHeadsUpContentView(); RemoteViews innerView = headsUp != null ? headsUp : b.getContentView(); if (headsUp == null) { // No expandable notification return; } RemoteViews remoteViews = NotificationCompatImplBase.applyStandardTemplateWithActions( b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, n.icon, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), b.getColor(), R.layout.notification_template_custom_big, false /* fitIn1U */, b.mActions); NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, remoteViews, innerView); n.headsUpContentView = remoteViews; } @RequiresApi(21) @TargetApi(21) private static void addBigStyleToBuilderLollipop(Notification n, android.support.v4.app.NotificationCompat.Builder b) { RemoteViews innerView = b.getBigContentView() != null ? b.getBigContentView() : b.getContentView(); if (b.mStyle instanceof DecoratedMediaCustomViewStyle && innerView != null) { NotificationCompatImplBase.overrideMediaBigContentView(n, b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), 0, b.mActions, false /* showCancelButton */, null /* cancelButtonIntent */, true /* decoratedCustomView */); NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.bigContentView, innerView); setBackgroundColor(b.mContext, n.bigContentView, b.getColor()); } else if (b.mStyle instanceof DecoratedCustomViewStyle) { addDecoratedBigStyleToBuilderJellybean(n, b); } } private static void setBackgroundColor(Context context, RemoteViews views, int color) { if (color == COLOR_DEFAULT) { color = context.getResources().getColor( R.color.notification_material_background_media_default_color); } views.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor", color); } @RequiresApi(21) @TargetApi(21) private static void addHeadsUpToBuilderLollipop(Notification n, android.support.v4.app.NotificationCompat.Builder b) { RemoteViews innerView = b.getHeadsUpContentView() != null ? b.getHeadsUpContentView() : b.getContentView(); if (b.mStyle instanceof DecoratedMediaCustomViewStyle && innerView != null) { n.headsUpContentView = NotificationCompatImplBase.generateMediaBigView(b.mContext, b.mContentTitle, b.mContentText, b.mContentInfo, b.mNumber, b.mLargeIcon, b.mSubText, b.mUseChronometer, b.getWhenIfShowing(), b.getPriority(), 0, b.mActions, false /* showCancelButton */, null /* cancelButtonIntent */, true /* decoratedCustomView */); NotificationCompatImplBase.buildIntoRemoteViews(b.mContext, n.headsUpContentView, innerView); setBackgroundColor(b.mContext, n.headsUpContentView, b.getColor()); } else if (b.mStyle instanceof DecoratedCustomViewStyle) { addDecoratedHeadsUpToBuilderLollipop(n, b); } } /** * See {@link android.support.v4.app.NotificationCompat}. In addition to the builder in v4, this * builder also supports {@link MediaStyle}. */ public static class Builder extends android.support.v4.app.NotificationCompat.Builder { /** * @inheritDoc */ public Builder(Context context) { super(context); } /** * @return the text of the notification * * @hide */ @RestrictTo(LIBRARY_GROUP) @Override protected CharSequence resolveText() { if (mStyle instanceof MessagingStyle) { MessagingStyle style = (MessagingStyle) mStyle; MessagingStyle.Message m = findLatestIncomingMessage(style); CharSequence conversationTitle = style.getConversationTitle(); if (m != null) { return conversationTitle != null ? makeMessageLine(this, style, m) : m.getText(); } } return super.resolveText(); } /** * @return the title of the notification * * @hide */ @RestrictTo(LIBRARY_GROUP) @Override protected CharSequence resolveTitle() { if (mStyle instanceof MessagingStyle) { MessagingStyle style = (MessagingStyle) mStyle; MessagingStyle.Message m = findLatestIncomingMessage(style); CharSequence conversationTitle = style.getConversationTitle(); if (conversationTitle != null || m != null) { return conversationTitle != null ? conversationTitle : m.getSender(); } } return super.resolveTitle(); } /** * @hide */ @RestrictTo(LIBRARY_GROUP) @Override protected BuilderExtender getExtender() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return new Api24Extender(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return new LollipopExtender(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { return new JellybeanExtender(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { return new IceCreamSandwichExtender(); } else { return super.getExtender(); } } } private static class IceCreamSandwichExtender extends BuilderExtender { IceCreamSandwichExtender() { } @Override public Notification build(android.support.v4.app.NotificationCompat.Builder b, NotificationBuilderWithBuilderAccessor builder) { RemoteViews contentView = addStyleGetContentViewIcs(builder, b); Notification n = builder.build(); // The above call might override decorated content views again, let's make sure it // sticks. if (contentView != null) { n.contentView = contentView; } else if (b.getContentView() != null) { n.contentView = b.getContentView(); } return n; } } private static class JellybeanExtender extends BuilderExtender { JellybeanExtender() { } @Override public Notification build(android.support.v4.app.NotificationCompat.Builder b, NotificationBuilderWithBuilderAccessor builder) { RemoteViews contentView = addStyleGetContentViewJellybean(builder, b); Notification n = builder.build(); // The above call might override decorated content views again, let's make sure it // sticks. if (contentView != null) { n.contentView = contentView; } addBigStyleToBuilderJellybean(n, b); return n; } } private static class LollipopExtender extends BuilderExtender { LollipopExtender() { } @Override public Notification build(android.support.v4.app.NotificationCompat.Builder b, NotificationBuilderWithBuilderAccessor builder) { RemoteViews contentView = addStyleGetContentViewLollipop(builder, b); Notification n = builder.build(); // The above call might override decorated content views again, let's make sure it // sticks. if (contentView != null) { n.contentView = contentView; } addBigStyleToBuilderLollipop(n, b); addHeadsUpToBuilderLollipop(n, b); return n; } } private static class Api24Extender extends BuilderExtender { @Override public Notification build(android.support.v4.app.NotificationCompat.Builder b, NotificationBuilderWithBuilderAccessor builder) { addStyleToBuilderApi24(builder, b); return builder.build(); } } /** * Notification style for media playback notifications. * * In the expanded form, {@link Notification#bigContentView}, up to 5 * {@link android.support.v4.app.NotificationCompat.Action}s specified with * {@link NotificationCompat.Builder#addAction(int, CharSequence, PendingIntent) addAction} will * be shown as icon-only pushbuttons, suitable for transport controls. The Bitmap given to * {@link NotificationCompat.Builder#setLargeIcon(android.graphics.Bitmap) setLargeIcon()} will * be treated as album artwork. * * Unlike the other styles provided here, MediaStyle can also modify the standard-size * {@link Notification#contentView}; by providing action indices to * {@link #setShowActionsInCompactView(int...)} you can promote up to 3 actions to be displayed * in the standard view alongside the usual content. * * Notifications created with MediaStyle will have their category set to * {@link Notification#CATEGORY_TRANSPORT CATEGORY_TRANSPORT} unless you set a different * category using {@link NotificationCompat.Builder#setCategory(String) setCategory()}. * * Finally, if you attach a {@link android.media.session.MediaSession.Token} using * {@link android.support.v7.app.NotificationCompat.MediaStyle#setMediaSession}, the System UI * can identify this as a notification representing an active media session and respond * accordingly (by showing album artwork in the lockscreen, for example). * * To use this style with your Notification, feed it to * {@link NotificationCompat.Builder#setStyle} like so: * <pre class="prettyprint"> * Notification noti = new NotificationCompat.Builder() * .setSmallIcon(R.drawable.ic_stat_player) * .setContentTitle("Track title") * .setContentText("Artist - Album") * .setLargeIcon(albumArtBitmap)) * .setStyle(<b>new NotificationCompat.MediaStyle()</b> * .setMediaSession(mySession)) * .build(); * </pre> * * @see Notification#bigContentView */ public static class MediaStyle extends android.support.v4.app.NotificationCompat.Style { int[] mActionsToShowInCompact = null; MediaSessionCompat.Token mToken; boolean mShowCancelButton; PendingIntent mCancelButtonIntent; public MediaStyle() { } public MediaStyle(android.support.v4.app.NotificationCompat.Builder builder) { setBuilder(builder); } /** * Requests up to 3 actions (by index in the order of addition) to be shown in the compact * notification view. * * @param actions the indices of the actions to show in the compact notification view */ public MediaStyle setShowActionsInCompactView(int...actions) { mActionsToShowInCompact = actions; return this; } /** * Attaches a {@link MediaSessionCompat.Token} to this Notification * to provide additional playback information and control to the SystemUI. */ public MediaStyle setMediaSession(MediaSessionCompat.Token token) { mToken = token; return this; } /** * Sets whether a cancel button at the top right should be shown in the notification on * platforms before Lollipop. * * <p>Prior to Lollipop, there was a bug in the framework which prevented the developer to * make a notification dismissable again after having used the same notification as the * ongoing notification for a foreground service. When the notification was posted by * {@link android.app.Service#startForeground}, but then the service exited foreground mode * via {@link android.app.Service#stopForeground}, without removing the notification, the * notification stayed ongoing, and thus not dismissable. * * <p>This is a common scenario for media notifications, as this is exactly the service * lifecycle that happens when playing/pausing media. Thus, a workaround is provided by the * support library: Instead of making the notification ongoing depending on the playback * state, the support library provides the ability to add an explicit cancel button to the * notification. * * <p>Note that the notification is enforced to be ongoing if a cancel button is shown to * provide a consistent user experience. * * <p>Also note that this method is a no-op when running on Lollipop and later. * * @param show whether to show a cancel button */ public MediaStyle setShowCancelButton(boolean show) { mShowCancelButton = show; return this; } /** * Sets the pending intent to be sent when the cancel button is pressed. See {@link * #setShowCancelButton}. * * @param pendingIntent the intent to be sent when the cancel button is pressed */ public MediaStyle setCancelButtonIntent(PendingIntent pendingIntent) { mCancelButtonIntent = pendingIntent; return this; } } /** * Notification style for custom views that are decorated by the system. * * <p>Instead of providing a notification that is completely custom, a developer can set this * style and still obtain system decorations like the notification header with the expand * affordance and actions. * * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)}, * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the * corresponding custom views to display. * * <p>To use this style with your Notification, feed it to * {@link NotificationCompat.Builder#setStyle(Style)} like so: * <pre class="prettyprint"> * Notification noti = new NotificationCompat.Builder() * .setSmallIcon(R.drawable.ic_stat_player) * .setLargeIcon(albumArtBitmap)) * .setCustomContentView(contentView) * .setStyle(<b>new NotificationCompat.DecoratedCustomViewStyle()</b>) * .build(); * </pre> * * <p>If you are using this style, consider using the corresponding styles like * {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification} or * {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification_Title} in * your custom views in order to get the correct styling on each platform version. */ public static class DecoratedCustomViewStyle extends Style { public DecoratedCustomViewStyle() { } } /** * Notification style for media custom views that are decorated by the system. * * <p>Instead of providing a media notification that is completely custom, a developer can set * this style and still obtain system decorations like the notification header with the expand * affordance and actions. * * <p>Use {@link android.app.Notification.Builder#setCustomContentView(RemoteViews)}, * {@link android.app.Notification.Builder#setCustomBigContentView(RemoteViews)} and * {@link android.app.Notification.Builder#setCustomHeadsUpContentView(RemoteViews)} to set the * corresponding custom views to display. * * <p>To use this style with your Notification, feed it to * {@link NotificationCompat.Builder#setStyle(Style)} like so: * <pre class="prettyprint"> * Notification noti = new Notification.Builder() * .setSmallIcon(R.drawable.ic_stat_player) * .setLargeIcon(albumArtBitmap)) * .setCustomContentView(contentView) * .setStyle(<b>new NotificationCompat.DecoratedMediaCustomViewStyle()</b> * .setMediaSession(mySession)) * .build(); * </pre> * * <p>If you are using this style, consider using the corresponding styles like * {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification_Media} or * {@link * android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Notification_Title_Media} in * your custom views in order to get the correct styling on each platform version. * * @see DecoratedCustomViewStyle * @see MediaStyle */ public static class DecoratedMediaCustomViewStyle extends MediaStyle { public DecoratedMediaCustomViewStyle() { } } }