کنترلهای رسانه در Android در نزدیکی «تنظیمات فوری» قرار دارند. جلسههای چند برنامه در گردونهای با قابلیت تند کشیدن مرتب شدهاند. گردونه جلسهها را به این ترتیب فهرست میکند:
- جاریسازیهایی که بهطور محلی در تلفن پخش میشود
- جاریسازیهای ازراهدور، مثل مواردی که در دستگاههای خارجی یا جلسههای ارسال محتوا شناسایی شدهاند
- جلسههای قابلازسرگیری قبلی، بهترتیب آخرین باری که پخش شدهاند
از Android 13 (سطح میانای برنامهسازی کاربردی ۳۳) به بعد، برای اطمینان از اینکه کاربران میتوانند به مجموعه غنیای از کنترلهای رسانه برای برنامههایی که رسانه پخش میکنند دسترسی داشته باشند، دکمههای کنش در کنترلهای رسانه از وضعیت Player مشتق میشوند.
بهاینترتیب، میتوانید مجموعه ثابتی از کنترلهای رسانه و تجربه کنترل رسانه بهتری را در دستگاههای مختلف ارائه دهید.
شکل ۱ نمونهای از نحوه نمایش این مورد را بهترتیب در تلفن و رایانه لوحی نشان میدهد.
سیستم حداکثر پنج دکمه کنش را براساس وضعیت Player نمایش میدهد، همانطور که در جدول زیر شرح داده شده است. در حالت فشرده، فقط سه جایگاه کنش اول
نمایش داده میشود. این با نحوه ارائه کنترلهای رسانه در سایر پلاتفرمهای Android مانند Auto، «دستیار»، و Wear OS مطابقت دارد.
| جایگاه | معیارها | کنش |
|---|---|---|
| 1 |
playWhenReady نادرست است یا
وضعیت بازپخش فعلی STATE_ENDED است.
|
پخش کردن |
playWhenReady درست است و
وضعیت بازپخش فعلی STATE_BUFFERING است.
|
درحال بار کردن گردونه | |
playWhenReady درست است و
وضعیت بازپخش فعلی 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()، به فرمان سفارشی انتخابشده توسط کاربر پاسخ دهید.
کاتلین
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) } } }
جاوا
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 بهگونهای که کلاینتهایی مثل
سیستم بتوانند به برنامه رسانه شما متصل شوند،
اعطای کنترل به کلاینتهای دیگر را ببینید.
با Jetpack Media3، وقتی MediaSession را پیادهسازی میکنید، PlaybackState شما
بهطور خودکار با پخشکننده رسانه بهروز نگه داشته میشود. بهطور مشابه، وقتی
MediaSessionService را پیادهسازی میکنید، کتابخانه بهطور خودکار
MediaStyle اعلان
را برای شما منتشر میکند و آن را بهروز نگه میدارد.
پاسخ دادن به دکمههای کنش
وقتی کاربر روی دکمه کنش در کنترلهای رسانه سیستم تکضرب میزند،
MediaController سیستم فرمان بازپخش را به MediaSession شما ارسال میکند. سپس
MediaSession آن فرمانها را به پخشکننده واگذار میکند. فرمانهای تعریفشده در میانای Player Media3 بهطور خودکار توسط جلسه رسانه مدیریت میشوند.
برای راهنمایی درباره نحوه پاسخ دادن به فرمان سفارشی، به افزودن فرمانهای سفارشی مراجعه کنید.
پشتیبانی از ازسرگیری رسانه
ازسرگیری رسانه به کاربران اجازه میدهد جلسههای قبلی را از گردونه بدون نیاز به راهاندازی برنامه ازسر بگیرند. وقتی بازپخش شروع میشود، کاربر به روش معمول با کنترلهای رسانه تعامل برقرار میکند.
ویژگی ازسرگیری بازپخش را میتوان بااستفاده از برنامه «تنظیمات» در گزینههای صدا > رسانه روشن و خاموش کرد. کاربر همچنین میتواند با تکضرب زدن روی نماد چرخدنده که پساز تند کشیدن روی گردونه گستردهشده نشان داده میشود، به «تنظیمات» دسترسی پیدا کند.
Media3 میاناهای برنامهسازی کاربردیای ارائه میدهد که پشتیبانی از ازسرگیری رسانه را آسانتر میکند. برای راهنمایی درباره پیادهسازی این ویژگی، به سند ازسرگیری بازپخش با Media3 مراجعه کنید.
استفاده از میاناهای برنامهسازی کاربردی رسانه قدیمی
این بخش نحوه ادغام با کنترلهای رسانه سیستم را بااستفاده از میاناهای برنامهسازی کاربردی قدیمی MediaCompat توضیح میدهد.
سیستم اطلاعات زیر را از
MediaMetadata MediaSession بازیابی میکند و درصورت دردسترس بودن، آن را نمایش میدهد:
METADATA_KEY_ALBUM_ART_URIMETADATA_KEY_TITLEMETADATA_KEY_DISPLAY_TITLEMETADATA_KEY_ARTISTMETADATA_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 برای جلسه رسانه تنظیم کنید.
کاتلین
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)
جاوا
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 را اضافه نکنید و بهجای آن، موارد اضافی را به
جلسه اضافه کنید:
کاتلین
session.setExtras(Bundle().apply { putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true) putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true) })
جاوا
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 اضافه کنید. این کنشها بهترتیب زمان اضافه شدن نشان داده میشوند.
کاتلین
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)
جاوا
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 استفاده میکند. باید یک فراخوانی برگشتی ثبت کنید
که بتواند بهدرستی به این رویدادها پاسخ دهد.
کاتلین
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)
جاوا
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
پساز راهاندازی دستگاه، سیستم پنج برنامه رسانهای را که اخیراً استفاده شدهاند جستجو میکند و کنترلهایی را ارائه میدهد که میتوان از آنها برای بازراهاندازی پخش از هر برنامه استفاده کرد.
سیستم تلاش میکند با اتصالی از SystemUI با MediaBrowserService شما تماس بگیرد. برنامهتان باید چنین اتصالهایی را مجاز کند، درغیراینصورت نمیتواند از ازسرگیری بازپخش پشتیبانی کند.
اتصالهای SystemUI را میتوان بااستفاده از نام بسته
com.android.systemui و امضا شناسایی و درستیسنجی کرد. SystemUI با امضای پلاتفرم امضا شده است. نمونهای از نحوه بررسی دربرابر امضای پلاتفرم را میتوانید
در برنامه UAMP پیدا کنید.
برای پشتیبانی از ازسرگیری بازپخش، MediaBrowserService شما باید
این رفتارها را پیادهسازی کند:
onGetRoot()باید بهسرعت ریشه غیرتهی برگرداند. منطق پیچیده دیگر باید درonLoadChildren()مدیریت شودوقتی
onLoadChildren()در شناسه رسانه ریشه فراخوانی میشود، نتیجه باید حاوی فرزند FLAG_PLAYABLE باشد.MediaBrowserServiceباید جدیدترین فایل رسانهای پخششده را هنگام دریافت پُرسمان EXTRA_RECENT برگرداند. مقدار برگرداندهشده باید یک فایل رسانهای واقعی باشد نه یک تابع عمومی.MediaBrowserServiceباید MediaDescription مناسبی با عنوان و زیرعنوان غیرخالی ارائه دهد. همچنین باید شناسه منبع یکسان نماد یا نقشه بیتی نماد تنظیم کند.
نمونه کدهای زیر نحوه پیادهسازی onGetRoot() را نشان میدهند.
کاتلین
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)
جاوا
@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 13
برای سازگاری با نسخههای قدیمی، «میانای کاربری سیستم» همچنان چیدمان جایگزینی ارائه میدهد
که از کنشهای اعلان برای برنامههایی استفاده میکند که برای هدف قرار دادن Android نسخه ۱۳ بهروزرسانی نمیشوند،
یا اطلاعات PlaybackState را دربرندارند. دکمههای کنش از فهرست Notification.Action پیوستشده به اعلان MediaStyle مشتق شدهاند. سیستم حداکثر پنج کنش را بهترتیبی که اضافه شدهاند نمایش میدهد. در حالت فشرده، حداکثر سه دکمه نشان داده میشود که براساس مقادیر ارسالشده به setShowActionsInCompactView() تعیین میشود.
کنشهای سفارشی براساس ترتیبی که به PlaybackState اضافه شدهاند قرار میگیرند.
نمونه کد زیر نحوه افزودن کنشها به اعلان MediaStyle را نشان میدهد :
کاتلین
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()
جاوا
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();