ממשק השליטה במדיה ב-Android נמצא ליד ההגדרות המהירות. אם יש סשנים מכמה אפליקציות, הם מסודרים בקרוסלה שאפשר להחליק בה. הסשנים בקרוסלה מוצגים בסדר הזה:
- שידורים שמופעלים באופן מקומי בטלפון
- שידורים מרחוק, כמו אלה שמזוהים במכשירים חיצוניים או בסשנים של Cast
- סשנים קודמים שאפשר להמשיך, לפי הסדר שבו הופעלו לאחרונה
החל מ-Android 13 (רמת API 33), כפתורי הפעולה בממשקי השליטה במדיה נגזרים ממצב Player. זה מאפשר למשתמשים לגשת למגוון רחב של ממשקי שליטה באפליקציות שמפעילות מדיה.
כך תוכלו להציג קבוצה עקבית של ממשקי שליטה למדיה ולשפר את חוויית השימוש בהם במכשירים שונים.
תמונה 1 מציגה איך המודעה נראית בטלפון ובטאבלט, בהתאמה.
המערכת מציגה עד חמישה כפתורי פעולה על סמך המצב של Player, כפי שמתואר בטבלה הבאה. במצב קומפקטי מוצגות רק שלוש משבצות הפעולה הראשונות. זה תואם לאופן שבו כפתורי המדיה מוצגים בפלטפורמות אחרות של Android, כמו Auto, Assistant ו-Wear OS.
| משבצת | קריטריונים | פעולה |
|---|---|---|
| 1 |
playWhenReady במצב FALSE או
שמצב ההפעלה הנוכחי הוא STATE_ENDED.
|
הפעלה |
playWhenReady במצב TRUE
ומצב ההפעלה הנוכחי הוא STATE_BUFFERING.
|
סימן גרפי של טעינה מתבצעת | |
playWhenReady במצב TRUE
ומצב ההפעלה הנוכחי הוא STATE_READY.
|
השהיה | |
| 2 |
ההעדפות של כפתור המדיה כוללות כפתור בהתאמה אישית ל-CommandButton.SLOT_BACK
|
בהתאמה אישית |
פקודת ההפעלה
COMMAND_SEEK_TO_PREVIOUS או
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM זמינה.
|
הקודם | |
| אי אפשר להשתמש בכפתור בהתאמה אישית או באחת מהפקודות ברשימה. | ריק | |
| 3 |
ההעדפות של כפתורי המדיה כוללות כפתור בהתאמה אישית ל-CommandButton.SLOT_FORWARD
|
בהתאמה אישית |
פקודת ההפעלה
COMMAND_SEEK_TO_NEXT או
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM זמינה.
|
הבא | |
| אי אפשר להשתמש בכפתור בהתאמה אישית או באחת מהפקודות ברשימה. | ריק | |
| 4 |
בהעדפות של כפתורי המדיה יש כפתור בהתאמה אישית ל-CommandButton.SLOT_OVERFLOW שעדיין לא נבחר לו מיקום.
|
בהתאמה אישית |
| 5 |
בהעדפות של כפתורי המדיה יש כפתור בהתאמה אישית ל-CommandButton.SLOT_OVERFLOW שעדיין לא נבחר לו מיקום.
|
בהתאמה אישית |
כפתורים בהתאמה אישית לאפשרויות נוספות ממוקמים באותו סדר שבו הם נוספו להעדפות של כפתורי המדיה.
התאמה אישית של כפתורי הפקודות
כדי להתאים אישית את ממשקי השליטה במדיה של המערכת באמצעות Jetpack Media3, אפשר להגדיר את העדפות כפתורי המדיה של הסשן ואת הפקודות הזמינות של הבקרים בהתאם:
יוצרים
MediaSessionומגדירים את ההעדפות של כפתורי המדיה לכפתורי פקודות בהתאמה אישית.ב-
MediaSession.Callback.onConnect(), מגדירים את הפקודות הזמינות של הבקרים ב-ConnectionResult, כולל פקודות בהתאמה אישית, כדי לאשר את הבקרים.ב-
MediaSession.Callback.onCustomCommand(), מגיבים לפקודה המותאמת אישית שהמשתמש בחר.
Kotlin
class PlaybackService : MediaSessionService() { private val customCommandFavorites = SessionCommand(ACTION_FAVORITES, Bundle.EMPTY) private var mediaSession: MediaSession? = null override fun onCreate() { super.onCreate() val favoriteButton = CommandButton.Builder(CommandButton.ICON_HEART_UNFILLED) .setDisplayName("Save to favorites") .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setMediaButtonPreferences(ImmutableList.of(favoriteButton)) .build() } private inner class MyCallback : MediaSession.Callback { override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { // Set available player and session commands. return AcceptedResultBuilder(session) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandFavorites) .build() ) .build() } override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture{ if (customCommand.customAction == ACTION_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } return super.onCustomCommand(session, controller, customCommand, args) } } }
Java
public class PlaybackService extends MediaSessionService { private static final SessionCommand CUSTOM_COMMAND_FAVORITES = new SessionCommand("ACTION_FAVORITES", Bundle.EMPTY); @Nullable private MediaSession mediaSession; public void onCreate() { super.onCreate(); CommandButton favoriteButton = new CommandButton.Builder(CommandButton.ICON_HEART_UNFILLED) .setDisplayName("Save to favorites") .setSessionCommand(CUSTOM_COMMAND_FAVORITES) .build(); Player player = new ExoPlayer.Builder(this).build(); // Build the session with a custom layout. mediaSession = new MediaSession.Builder(this, player) .setCallback(new MyCallback()) .setMediaButtonPreferences(ImmutableList.of(favoriteButton)) .build(); } private static class MyCallback implements MediaSession.Callback { @Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { // Set available player and session commands. return new AcceptedResultBuilder(session) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(CUSTOM_COMMAND_FAVORITES) .build()) .build(); } public ListenableFutureonCustomCommand( MediaSession session, MediaSession.ControllerInfo controller, SessionCommand customCommand, Bundle args) { if (customCommand.customAction.equals(CUSTOM_COMMAND_FAVORITES.customAction)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } return MediaSession.Callback.super.onCustomCommand( session, controller, customCommand, args); } } }
אם אתם רוצים לעיין במידע נוסף על ההגדרות של MediaSession כך שלקוחות (כמו המערכת) יוכלו להתחבר לאפליקציית המדיה שלכם, אתם יכולים לעיין בקטע בנושא מתן שליטה ללקוחות אחרים.
כשמטמיעים MediaSession באמצעות Jetpack Media3, PlaybackState
מתעדכן אוטומטית בהתאם לנגן המדיה. באופן דומה, כשמטמיעים MediaSessionService, הספרייה מפרסמת באופן אוטומטי התראת MediaStyle מעודכנת בשבילכם.
תגובה לכפתורי פעולה
כשמשתמש לוחץ על כפתור פעולה בממשקי השליטה במדיה של המערכת, MediaController של המערכת שולח פקודת הפעלה אל MediaSession. הפקודות האלה מועברות מה-MediaSession אל הנגן. פקודות
שמוגדרות בממשק Player של Media3 מטופלות באופן אוטומטי בפעולת המדיה.
במאמר הוספת פקודות מותאמות אישית תוכלו לקרוא איך להוסיף פקודות כאלה.
תמיכה בהמשך הפעלה של מדיה
המשתמשים יכולים להפעיל מחדש סשנים קודמים מהקרוסלה בלי להפעיל את האפליקציה. כשההפעלה מתחילה, המשתמשים יכולים ללהפעיל את ממשקי השליטה במדיה כרגיל.
אפשר להפעיל או להשבית את התכונה "המשך הפעלה" באמצעות אפליקציית ההגדרות, בקטע סאונד > מדיה. כדי להיכנס להגדרות, אפשר ללחוץ על סמל גלגל השיניים שמופיע אחרי שמחליקים בקרוסלה המורחבת.
Media3 מציעה ממשקי API שמקלים על תמיכה בהמשך הפעלה של מדיה. הוראות להטמעה של התכונה הזו זמינות במסמכים בנושא המשך הפעלה באמצעות Media3.
שימוש בממשקי API קודמים של מדיה
בקטע הזה מוסבר איך לשלב את האפליקציה עם אמצעי הבקרה של המדיה במערכת באמצעות ממשקי MediaCompat API מדור קודם.
המערכת מאחזרת את הפרטים הבאים מ-MediaMetadata של MediaSession ומציגה אותם כשהם זמינים:
METADATA_KEY_ALBUM_ART_URIMETADATA_KEY_TITLEMETADATA_KEY_DISPLAY_TITLEMETADATA_KEY_ARTIST-
METADATA_KEY_DURATION(אם משך הזמן לא מוגדר, סרגל ההתקדמות לא מופיע)
כדי לוודא שקיבלתם התראה תקפה ומדויקת על ממשקי השליטה במדיה, אתם צריכים להגדיר את הערך של המטא-נתונים METADATA_KEY_TITLE או METADATA_KEY_DISPLAY_TITLE לשם של המדיה שמופעלת כרגע.
בנגן המדיה מוצג הזמן שחלף מאז תחילת ההפעלה של פריט המדיה שמופעל כרגע, לצד סרגל ניווט שממופה ל-MediaSession
PlaybackState.
בנגן המדיה מוצגת ההתקדמות של פריט המדיה שמופעל כרגע, לצד סרגל ניווט שממופה ל-MediaSession PlaybackState. סרגל הדילוג מאפשר למשתמשים לשנות את המיקום ומציג את הזמן שחלף בהפעלה של פריט המדיה. כדי להפעיל את סרגל ההתקדמות, צריך להטמיע את PlaybackState.Builder#setActions ולכלול את ACTION_SEEK_TO.
| משבצת | פעולה | קריטריונים |
|---|---|---|
| 1 | הפעלה |
המצב הנוכחי של PlaybackState הוא אחד מהבאים:
|
| סימן גרפי של טעינה מתבצעת |
המצב הנוכחי של PlaybackState הוא אחד מהבאים:
|
|
| השהיה | המצב הנוכחי של PlaybackState הוא אף אחת מהאפשרויות שלמעלה. |
|
| 2 | הקודם | פעולות PlaybackState כוללות את ACTION_SKIP_TO_PREVIOUS. |
| בהתאמה אישית | פעולות PlaybackState לא כוללות את ACTION_SKIP_TO_PREVIOUS ופעולות בהתאמה אישית מסוג PlaybackState כוללות פעולה מותאמת אישית שעדיין לא נבחר לה מיקום. |
|
| ריק | תוספות מסוג PlaybackState כוללות ערך בוליאני true למפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV. |
|
| 3 | הבא | פעולות PlaybackState כוללות את ACTION_SKIP_TO_NEXT. |
| בהתאמה אישית | פעולות PlaybackState לא כוללות את ACTION_SKIP_TO_NEXT ופעולות בהתאמה אישית מסוג PlaybackState כוללות פעולה מותאמת אישית שעדיין לא נבחר לה מיקום. |
|
| ריק | תוספות מסוג PlaybackState כוללות ערך בוליאני true למפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT. |
|
| 4 | בהתאמה אישית | פעולות בהתאמה אישית מסוג PlaybackState כוללות פעולה בהתאמה אישית שעדיין לא נבחר לה מיקום. |
| 5 | בהתאמה אישית | פעולות בהתאמה אישית מסוג PlaybackState כוללות פעולה בהתאמה אישית שעדיין לא נבחר לה מיקום. |
הוספת פעולות רגילות
בדוגמאות הקוד הבאות אפשר לראות איך מוסיפים פעולות PlaybackState רגילות ופעולות מותאמות אישית.
כדי להגדיר את הפעולות "הפעלה", "השהיה", "הקודם" ו"הבא", צריך להגדיר אותן ב-PlaybackState של סשן המדיה.
Kotlin
val session = MediaSessionCompat(context, TAG) val playbackStateBuilder = PlaybackStateCompat.Builder() val style = NotificationCompat.MediaStyle() // For this example, the media is currently paused: val state = PlaybackStateCompat.STATE_PAUSED val position = 0L val playbackSpeed = 1f playbackStateBuilder.setState(state, position, playbackSpeed) // And the user can play, skip to next or previous, and seek val stateActions = PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or PlaybackStateCompat.ACTION_SKIP_TO_NEXT or PlaybackStateCompat.ACTION_SEEK_TO // adding the seek action enables seeking with the seekbar playbackStateBuilder.setActions(stateActions) // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()) style.setMediaSession(session.sessionToken) notificationBuilder.setStyle(style)
Java
MediaSessionCompat session = new MediaSessionCompat(context, TAG); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle(); // For this example, the media is currently paused: int state = PlaybackStateCompat.STATE_PAUSED; long position = 0L; float playbackSpeed = 1f; playbackStateBuilder.setState(state, position, playbackSpeed); // And the user can play, skip to next or previous, and seek long stateActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SEEK_TO; // adding this enables the seekbar thumb playbackStateBuilder.setActions(stateActions); // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()); style.setMediaSession(session.getSessionToken()); notificationBuilder.setStyle(style);
אם לא רוצים להוסיף כפתורים במשבצת הקודמת או הבאה, לא מוסיפים את ACTION_SKIP_TO_PREVIOUS או ACTION_SKIP_TO_NEXT, אלא תוספים לסשן:
Kotlin
session.setExtras(Bundle().apply { putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true) putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true) })
Java
Bundle extras = new Bundle(); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true); session.setExtras(extras);
הוספת פעולות מותאמות אישית
כדי להציג פעולות אחרות בממשקי השליטה במדיה, אפשר ליצור PlaybackStateCompat.CustomAction ולהוסיף אותו ל-PlaybackState. הפעולות האלה מוצגות לפי הסדר שבו הן נוספו.
Kotlin
val customAction = PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build() playbackStateBuilder.addCustomAction(customAction)
Java
PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build(); playbackStateBuilder.addCustomAction(customAction);
תגובה לפעולות של PlaybackState
כשמשתמש לוחץ על כפתור, SystemUI משתמש ב-MediaController.TransportControls כדי לשלוח פקודה בחזרה אל MediaSession. צריך לרשום פונקציית קריאה חוזרת שיכולה להגיב לאירועים האלה בצורה נכונה.
Kotlin
val callback = object: MediaSession.Callback() { override fun onPlay() { // start playback } override fun onPause() { // pause playback } override fun onSkipToPrevious() { // skip to previous } override fun onSkipToNext() { // skip to next } override fun onSeekTo(pos: Long) { // jump to position in track } override fun onCustomAction(action: String, extras: Bundle?) { when (action) { CUSTOM_ACTION_1 -> doCustomAction1(extras) CUSTOM_ACTION_2 -> doCustomAction2(extras) else -> { Log.w(TAG, "Unknown custom action $action") } } } } session.setCallback(callback)
Java
MediaSession.Callback callback = new MediaSession.Callback() { @Override public void onPlay() { // start playback } @Override public void onPause() { // pause playback } @Override public void onSkipToPrevious() { // skip to previous } @Override public void onSkipToNext() { // skip to next } @Override public void onSeekTo(long pos) { // jump to position in track } @Override public void onCustomAction(String action, Bundle extras) { if (action.equals(CUSTOM_ACTION_1)) { doCustomAction1(extras); } else if (action.equals(CUSTOM_ACTION_2)) { doCustomAction2(extras); } else { Log.w(TAG, "Unknown custom action " + action); } } };
המשך הפעלה של המדיה
כדי שאפליקציית הנגן תופיע באזור ההגדרות המהירות, צריך ליצור התראה עם טוקן MediaSession תקין.MediaStyle
כדי להציג את הכותרת של התראת ה-MediaStyle, משתמשים ב-NotificationBuilder.setContentTitle().
כדי להציג את סמל המותג בנגן המדיה, משתמשים ב-NotificationBuilder.setSmallIcon().
כדי לתמוך בהמשך הפעלה, האפליקציות צריכות להטמיע את MediaBrowserService ואת MediaSession. ב-MediaSession צריך להטמיע את הקריאה החוזרת onPlay().
הטמעה של MediaBrowserService
אחרי שהמכשיר מופעל, המערכת מחפשת את חמש האפליקציות האחרונות שהפעילו מדיה, ומספקת אמצעי בקרה כדי להפעיל את המדיה מחדש מכל אפליקציה.
המערכת מנסה ליצור קשר עם MediaBrowserService באמצעות חיבור מ-SystemUI. האפליקציה צריכה לאפשר חיבורים כאלה, אחרת היא לא תוכל לתמוך בהמשך הפעלה.
אפשר לזהות ולאמת חיבורים מ-SystemUI באמצעות שם החבילה
com.android.systemui והחתימה. המערכת SystemUI חתומה בחתימת הפלטפורמה. באפליקציית UAMP אפשר למצוא דוגמה לאופן הבדיקה של חתימת הפלטפורמה.
כדי לתמוך בהמשך הפעלה, עליך להטמיע את ההתנהגויות הבאות ב-MediaBrowserService:
הפונקציה
onGetRoot()צריכה להחזיר שורש שאינו null במהירות. לוגיקה מורכבת אחרת צריכה להיות מטופלת ב-onLoadChildren()כשקוראים לפונקציה
onLoadChildren()במזהה המדיה הבסיסי, התוצאה צריכה להכיל צאצא FLAG_PLAYABLE.הפונקציה
MediaBrowserServiceצריכה להחזיר את פריט המדיה שהופעל לאחרונה כשמתקבלת שאילתה מסוג EXTRA_RECENT. הערך שמוחזר צריך להיות פריט מדיה בפועל ולא פונקציה כללית.
MediaBrowserServiceצריך לספק MediaDescription מתאים עם שדות מלאים של כותרת ותת כותרת. צריך גם להגדיר מזהה URI של סמל או מפת סיביות של סמל.
בדוגמאות הקוד הבאות אפשר לראות איך מטמיעים onGetRoot().
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { rootHints?.let { if (it.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. val extras = Bundle().apply { putBoolean(BrowserRoot.EXTRA_RECENT, true) } return BrowserRoot(MY_RECENTS_ROOT_ID, extras) } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return BrowserRoot(MY_MEDIA_ROOT_ID, null) } // Return an empty tree to disallow browsing. return BrowserRoot(MY_EMPTY_ROOT_ID, null)
Java
@Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { if (rootHints != null) { if (rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. Bundle extras = new Bundle(); extras.putBoolean(BrowserRoot.EXTRA_RECENT, true); return new BrowserRoot(MY_RECENTS_ROOT_ID, extras); } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return new BrowserRoot(MY_MEDIA_ROOT_ID, null); } // Return an empty tree to disallow browsing. return new BrowserRoot(MY_EMPTY_ROOT_ID, null); }
התנהגות בגרסאות Android קודמות ל-Android 13
לצורך תאימות לאחור, ממשק המשתמש של המערכת ממשיך לספק פריסה חלופית שמשתמשת בפעולות של התראות באפליקציות שלא מעודכנות לטירגוט Android 13, או שלא כוללות מידע על PlaybackState. כפתורי הפעולה נגזרים מרשימת Notification.Action שמצורפת להתראה MediaStyle. המערכת מציגה עד חמש פעולות לפי הסדר שבו הן נוספו. במצב קומפקטי מוצגים עד שלושה כפתורים, בהתאם לערכים שמועברים אל setShowActionsInCompactView().
פעולות בהתאמה אישית ממוקמות לפי הסדר שבו הן נוספו לPlaybackState.
בדוגמת הקוד הבאה אפשר לראות איך מוסיפים פעולות להתראת MediaStyle:
Kotlin
import androidx.core.app.NotificationCompat import androidx.media3.session.MediaStyleNotificationHelper var notification = NotificationCompat.Builder(context, CHANNEL_ID) // Show controls on lock screen even when user hides sensitive content. .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) // Add media control buttons that invoke intents in your media service .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 // Apply the media style template .setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build()
Java
import androidx.core.app.NotificationCompat; import androidx.media3.session.MediaStyleNotificationHelper; NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_ID) // Show controls on lock screen even when user hides sensitive content. .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) // Add media control buttons that invoke intents in your media service .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 // Apply the media style template .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();