کنترل های رسانه ای

کنترل‌های رسانه در اندروید در نزدیکی تنظیمات سریع قرار دارند. جلسات از چندین برنامه در یک چرخ فلک قابل کشیدن مرتب شده‌اند. این چرخ فلک جلسات را به این ترتیب فهرست می‌کند:

  • پخش استریم‌ها به صورت محلی روی گوشی
  • استریم‌های از راه دور، مانند آن‌هایی که در دستگاه‌های خارجی یا جلسات پخش شناسایی می‌شوند
  • جلسات قابل از سرگیری قبلی، به ترتیب آخرین بازی انجام شده

از اندروید ۱۳ (سطح API ۳۳)، برای اطمینان از اینکه کاربران می‌توانند به مجموعه‌ای غنی از کنترل‌های رسانه‌ای برای برنامه‌های پخش رسانه دسترسی داشته باشند، دکمه‌های عملیاتی روی کنترل‌های رسانه‌ای از حالت Player state) مشتق می‌شوند.

به این ترتیب، می‌توانید مجموعه‌ای منسجم از کنترل‌های رسانه‌ای و تجربه‌ای روان‌تر از کنترل رسانه را در دستگاه‌های مختلف ارائه دهید.

شکل ۱ نمونه‌ای از نحوه نمایش این موارد را به ترتیب در تلفن همراه و تبلت نشان می‌دهد.

کنترل‌های رسانه‌ای از نظر نحوه نمایش آنها در دستگاه‌های تلفن و تبلت، با استفاده از یک نمونه آهنگ که نحوه نمایش دکمه‌ها را نشان می‌دهد
شکل ۱: کنترل‌های رسانه‌ای در دستگاه‌های تلفن و تبلت

سیستم حداکثر پنج دکمه عملیاتی را بر اساس وضعیت Player ، همانطور که در جدول زیر توضیح داده شده است، نمایش می‌دهد. در حالت فشرده، فقط سه جایگاه عملیاتی اول نمایش داده می‌شوند. این با نحوه رندر کنترل‌های رسانه‌ای در سایر پلتفرم‌های اندروید مانند Auto، Assistant و Wear OS همسو است.

اسلات معیارها اکشن
۱ playWhenReady نادرست است یا وضعیت پخش فعلی STATE_ENDED است. بازی
playWhenReady برابر با true و وضعیت پخش فعلی STATE_BUFFERING است. بارگیری اسپینر
playWhenReady برابر با true و وضعیت پخش فعلی STATE_READY است. مکث
۲ تنظیمات دکمه رسانه شامل یک دکمه سفارشی برای CommandButton.SLOT_BACK است. سفارشی
دستور پخش‌کننده COMMAND_SEEK_TO_PREVIOUS یا COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM در دسترس است. قبلی
نه دکمه سفارشی و نه هیچ یک از دستورات ذکر شده در دسترس نیست. خالی
۳ تنظیمات دکمه رسانه شامل یک دکمه سفارشی برای CommandButton.SLOT_FORWARD است. سفارشی
دستور پخش‌کننده COMMAND_SEEK_TO_NEXT یا COMMAND_SEEK_TO_NEXT_MEDIA_ITEM در دسترس است. بعدی
نه دکمه سفارشی و نه هیچ یک از دستورات ذکر شده در دسترس نیست. خالی
۴ تنظیمات دکمه رسانه شامل یک دکمه سفارشی برای CommandButton.SLOT_OVERFLOW است که هنوز قرار داده نشده است. سفارشی
۵ تنظیمات دکمه رسانه شامل یک دکمه سفارشی برای CommandButton.SLOT_OVERFLOW است که هنوز قرار داده نشده است. سفارشی

دکمه‌های سرریز سفارشی به ترتیبی که به تنظیمات دکمه رسانه اضافه شده‌اند، قرار می‌گیرند.

سفارشی سازی دکمه های فرمان

برای سفارشی‌سازی کنترل‌های رسانه سیستم با Jetpack Media3 ، می‌توانید تنظیمات دکمه رسانه مربوط به جلسه و دستورات موجود کنترل‌کننده‌ها را بر این اساس تنظیم کنید:

  1. یک MediaSession بسازید و تنظیمات دکمه رسانه را برای دکمه‌های فرمان سفارشی تعریف کنید .

  2. در MediaSession.Callback.onConnect() ، با تعریف دستورات موجود، از جمله دستورات سفارشی ، در ConnectionResult ، کنترلرها را مجاز کنید.

  3. در 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 ListenableFuture onCustomCommand(
        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 رابط‌های برنامه‌نویسی کاربردی (API) ارائه می‌دهد تا پشتیبانی از ازسرگیری پخش رسانه را آسان‌تر کند. برای راهنمایی در مورد پیاده‌سازی این ویژگی، به مستندات ازسرگیری پخش با Media3 مراجعه کنید.

استفاده از APIهای رسانه‌ای قدیمی

این بخش نحوه ادغام با کنترل‌های رسانه سیستم با استفاده از APIهای قدیمی MediaCompat را توضیح می‌دهد.

سیستم اطلاعات زیر را از MediaMetadata مربوط به MediaSession بازیابی می‌کند و در صورت موجود بودن، آن را نمایش می‌دهد:

  • 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 نیز در آن بگنجانید.

اسلات اکشن معیارها
۱ بازی وضعیت فعلی PlaybackState یکی از موارد زیر است:
  • STATE_NONE
  • STATE_STOPPED
  • STATE_PAUSED
  • STATE_ERROR
بارگیری اسپینر وضعیت فعلی PlaybackState یکی از موارد زیر است:
  • STATE_CONNECTING
  • STATE_BUFFERING
مکث وضعیت فعلی PlaybackState هیچ یک از موارد فوق نیست.
۲ قبلی اقدامات PlaybackState شامل ACTION_SKIP_TO_PREVIOUS می‌شود.
سفارشی اکشن‌های PlaybackState شامل ACTION_SKIP_TO_PREVIOUS نمی‌شوند و اکشن‌های سفارشی PlaybackState شامل یک اکشن سفارشی هستند که هنوز قرار داده نشده است.
خالی موارد اضافی PlaybackState شامل یک مقدار بولی true برای کلید SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV است.
۳ بعدی اقدامات PlaybackState شامل ACTION_SKIP_TO_NEXT می‌شود.
سفارشی اکشن‌های PlaybackState شامل ACTION_SKIP_TO_NEXT نمی‌شوند و اکشن‌های سفارشی PlaybackState شامل یک اکشن سفارشی هستند که هنوز قرار داده نشده است.
خالی موارد اضافی PlaybackState شامل یک مقدار بولی true برای کلید SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT است.
۴ سفارشی اقدامات سفارشی PlaybackState شامل یک اقدام سفارشی است که هنوز قرار داده نشده است.
۵ سفارشی اقدامات سفارشی 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 استفاده می‌کند. شما باید یک callback ثبت کنید که بتواند به درستی به این رویدادها پاسخ دهد.

کاتلین

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() باید به سرعت یک ریشه غیر تهی (non-null) را برگرداند. منطق پیچیده دیگر باید در onLoadChildren() مدیریت شود.

  • وقتی onLoadChildren() روی شناسه رسانه ریشه فراخوانی می‌شود، نتیجه باید شامل یک فرزند FLAG_PLAYABLE باشد.

  • MediaBrowserService باید هنگام دریافت یک پرس‌وجوی EXTRA_RECENT، آخرین آیتم رسانه‌ای پخش‌شده را برگرداند. مقدار برگردانده‌شده باید یک آیتم رسانه‌ای واقعی باشد، نه یک تابع عمومی.

  • MediaBrowserService باید یک MediaDescription مناسب با عنوان و زیرعنوان غیر خالی ارائه دهد. همچنین باید یک URL آیکون یا یک نقشه بیتی آیکون تنظیم کند.

مثال‌های کد زیر نحوه پیاده‌سازی 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);
}

رفتار قبل از اندروید ۱۳

برای سازگاری با نسخه‌های قبلی، رابط کاربری سیستم همچنان یک طرح‌بندی جایگزین ارائه می‌دهد که از اقدامات اعلان برای برنامه‌هایی که برای هدف قرار دادن اندروید ۱۳ به‌روزرسانی نمی‌شوند، یا اطلاعات 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();