אמצעי הבקרה של המדיה ב-Android נמצאים ליד ההגדרות המהירות. הסשנים מכמה אפליקציות מסודרים בקרוסלה שאפשר להחליק בה. ברשימת הקרוסלה מוצגות פעילויות בסדר הזה:
- שידורים חיים שמופעלים באופן מקומי בטלפון
- שידורים מרחוק, כמו אלה שמזוהים במכשירים חיצוניים או בסשנים של Cast
- סשנים קודמים שאפשר להמשיך מהם, לפי הסדר שבו הם הופעלו לאחרונה
החל מ-Android 13 (רמת API 33), כדי לוודא שהמשתמשים יוכלו לגשת למגוון רחב של אמצעי בקרה למדיה באפליקציות שמפעילות מדיה, לחצני הפעולה באמצעי הבקרה למדיה נגזרים ממצב Player
.
כך תוכלו להציג קבוצה עקבית של אמצעי בקרה למדיה וחוויה משופרת של אמצעי בקרה למדיה במכשירים שונים.
באיור 1 מוצגות דוגמאות לאופן שבו המודעה מוצגת בטלפון ובטאבלט, בהתאמה.
המערכת מציגה עד חמישה לחצני פעולה על סמך המצב של Player
, כפי שמתואר בטבלה הבאה. במצב קומפקטי, מוצגים רק שלושת משבצות הפעולה הראשונות. ההתנהגות הזו תואמת לאופן שבו כפתורי המדיה מוצגים בפלטפורמות אחרות של Android, כמו Auto, Assistant ו-Wear OS.
משבצת | קריטריונים | פעולה |
---|---|---|
1 |
playWhenReady הוא שקרי או שמצב ההפעלה הנוכחי
STATE_ENDED .
|
Play |
playWhenReady הוא true ו
מצב ההפעלה הנוכחי הוא STATE_BUFFERING .
|
סימן גרפי של טעינה מתבצעת | |
playWhenReady הוא true ו
מצב ההפעלה הנוכחי הוא STATE_READY .
|
השהיה | |
2 |
העדפות של כפתור המדיה כוללות כפתור בהתאמה אישית ל-CommandButton.SLOT_BACK
|
בהתאמה אישית |
פקודת Player
COMMAND_SEEK_TO_PREVIOUS או
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM זמינה.
|
הקודם | |
אין אפשרות להשתמש בכפתור בהתאמה אישית או באחת מהפקודות שמופיעות ברשימה. | ריק | |
3 |
העדפות כפתורי המדיה כוללות כפתור בהתאמה אישית ל-CommandButton.SLOT_FORWARD
|
בהתאמה אישית |
פקודת Player
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
כדי שלקוחות כמו המערכת יוכלו להתחבר לאפליקציית המדיה
כשמטמיעים MediaSession
באמצעות Jetpack Media3, PlaybackState
מתעדכן אוטומטית בהתאם לנגן המדיה. באופן דומה, כשמטמיעים MediaSessionService
, הספרייה מפרסמת באופן אוטומטי MediaStyle
התראה בשבילכם ודואגת שהיא תהיה עדכנית.
תגובה ללחצני פעולה
כשמשתמש מקיש על לחצן פעולה באמצעי הבקרה של המדיה במערכת, MediaController
של המערכת שולח פקודת הפעלה אל MediaSession
. הפקודות האלה מועברות מה-MediaSession
אל הנגן. הפקודות
שמוגדרות בממשק Player
של Media3 מטופלות באופן אוטומטי על ידי סשן המדיה.
הוראות להוספת תגובה לפקודה מותאמת אישית מופיעות במאמר הוספת פקודות מותאמות אישית.
תמיכה בהמשך הפעלה של מדיה
המשתמשים יכולים להפעיל מחדש סשנים קודמים מהקרוסלה בלי להפעיל את האפליקציה. כשההפעלה מתחילה, המשתמשים יכולים ליצור אינטראקציה עם אמצעי הבקרה של המדיה כרגיל.
אפשר להפעיל או להשבית את התכונה של המשך ההפעלה באמצעות אפליקציית ההגדרות, באפשרויות סאונד > מדיה. המשתמש יכול גם לגשת להגדרות על ידי הקשה על סמל גלגל השיניים שמופיע אחרי החלקה בקרוסלה המורחבת.
Media3 מציעה ממשקי API כדי להקל על תמיכה בהמשך הפעלה של מדיה. הוראות להטמעה של התכונה הזו זמינות במסמכים בנושא המשך הפעלה באמצעות Media3.
שימוש בממשקי API מדור קודם של מדיה
בקטע הזה מוסבר איך לשלב את האפליקציה עם אמצעי הבקרה של המדיה במערכת באמצעות ממשקי MediaCompat API מדור קודם.
המערכת מאחזרת את הפרטים הבאים מMediaSession
MediaMetadata
ומציגה אותם כשהם זמינים:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
-
METADATA_KEY_DURATION
(אם משך הזמן לא מוגדר, סרגל ההתקדמות לא מופיע)
כדי לוודא שקיבלתם התראה תקפה ומדויקת על אמצעי בקרה להפעלת מדיה, צריך להגדיר את הערך של המטא-נתונים METADATA_KEY_TITLE
או METADATA_KEY_DISPLAY_TITLE
לשם המדיה שמופעלת כרגע.
בנגן המדיה מוצג הזמן שחלף מאז תחילת ההפעלה של פריט המדיה הנוכחי, וגם סרגל ניווט שמופה ל-MediaSession
PlaybackState
.
בנגן המדיה מוצגת ההתקדמות של פריט המדיה שמופעל כרגע, לצד סרגל ניווט שממופה ל-MediaSession
PlaybackState
. סרגל הדילוג מאפשר למשתמשים לשנות את המיקום ומציג את הזמן שחלף של פריט המדיה. כדי להפעיל את סרגל ההתקדמות, צריך להטמיע את PlaybackState.Builder#setActions
ולכלול את ACTION_SEEK_TO
.
משבצת | פעולה | קריטריונים |
---|---|---|
1 | Play |
הסטטוס הנוכחי של PlaybackState הוא אחד מהבאים:
|
סימן גרפי של טעינה מתבצעת |
הסטטוס הנוכחי של PlaybackState הוא אחד מהבאים:
|
|
השהיה | המצב הנוכחי של PlaybackState הוא אף אחת מהאפשרויות שלמעלה. |
|
2 | הקודם | PlaybackState פעולות כוללות ACTION_SKIP_TO_PREVIOUS . |
בהתאמה אישית | PlaybackState actions לא כולל את ACTION_SKIP_TO_PREVIOUS ו-PlaybackState custom actions כולל פעולה מותאמת אישית שעדיין לא הוצבה. |
|
ריק | PlaybackState extras כולל ערך בוליאני true למפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | הבא | PlaybackState פעולות כוללות ACTION_SKIP_TO_NEXT . |
בהתאמה אישית | PlaybackState actions לא כולל את ACTION_SKIP_TO_NEXT ו-PlaybackState custom actions כולל פעולה מותאמת אישית שעדיין לא הוצבה. |
|
ריק | PlaybackState extras כולל ערך בוליאני 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); } } };
המשך הפעלה של המדיה
כדי שאפליקציית הנגן תופיע באזור ההגדרות המהירות, צריך ליצור MediaStyle
התראה עם טוקן MediaSession
תקין.
כדי להציג את הכותרת של ההתראה 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 מתאים עם title ו-subtitle לא ריקים. צריך גם להגדיר מזהה 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();