کنترلهای رسانه در اندروید در نزدیکی تنظیمات سریع قرار دارند. جلسات از چندین برنامه در یک چرخ فلک قابل کشیدن مرتب شده اند. چرخ فلک جلسات را به این ترتیب فهرست می کند:
- پخش جریانی به صورت محلی روی تلفن
- جریانهای از راه دور، مانند آنهایی که در دستگاههای خارجی یا جلسات ارسال محتوا شناسایی میشوند
- جلسات قابل ازسرگیری قبلی، به ترتیبی که آخرین بار بازی شده است
با شروع در Android 13 (سطح API 33)، برای اطمینان از اینکه کاربران میتوانند به مجموعهای غنی از کنترلهای رسانه برای برنامههای پخش رسانه دسترسی داشته باشند، دکمههای عملکرد روی کنترلهای رسانه از حالت Player
مشتق شدهاند.
به این ترتیب، میتوانید مجموعهای ثابت از کنترلهای رسانه و تجربه کنترل رسانه صیقلیتری را در سراسر دستگاهها ارائه دهید.
شکل 1 نمونه ای از نحوه ظاهر این دستگاه را به ترتیب در تلفن و تبلت نشان می دهد.
سیستم حداکثر پنج دکمه عمل را بر اساس وضعیت Player
همانطور که در جدول زیر توضیح داده شده است نمایش می دهد. در حالت فشرده، تنها سه اسلات عمل اول نمایش داده می شوند. این با نحوه نمایش کنترلهای رسانه در دیگر پلتفرمهای Android مانند Auto، Assistant و Wear OS همخوانی دارد.
اسلات | معیارها | اقدام |
---|---|---|
1 | playWhenReady نادرست است یا وضعیت پخش فعلی STATE_ENDED است. | بازی کنید |
playWhenReady درست است و وضعیت پخش فعلی STATE_BUFFERING است. | در حال بارگیری اسپینر | |
playWhenReady درست است و وضعیت پخش فعلی STATE_READY است. | مکث کنید | |
2 | فرمان پخش کننده COMMAND_SEEK_TO_PREVIOUS یا COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM در دسترس است. | قبلی |
نه فرمان پخش کننده COMMAND_SEEK_TO_PREVIOUS و نه COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM موجود نیست و یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی | |
موارد اضافی جلسه شامل یک مقدار بولی true برای کلید EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV است. | خالی | |
3 | فرمان پخش کننده COMMAND_SEEK_TO_NEXT یا COMMAND_SEEK_TO_NEXT_MEDIA_ITEM در دسترس است. | بعدی |
نه فرمان پخش کننده COMMAND_SEEK_TO_NEXT و نه COMMAND_SEEK_TO_NEXT_MEDIA_ITEM موجود نیست و یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی | |
موارد اضافی جلسه شامل یک مقدار بولی true برای کلید EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT است. | خالی | |
4 | یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی |
5 | یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی |
دستورات سفارشی به ترتیبی که به چیدمان سفارشی اضافه شده اند قرار می گیرند.
سفارشی کردن دکمه های فرمان
برای سفارشی کردن کنترلهای رسانه سیستم با Jetpack Media3 ، میتوانید هنگام اجرای MediaSessionService
، طرحبندی سفارشی جلسه و دستورات موجود کنترلرها را بر اساس آن تنظیم کنید:
در
onCreate()
یک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() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setCustomLayout(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) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build() ) .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() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .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()) .setCustomLayout(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) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build()) .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 به طور خودکار توسط جلسه رسانه مدیریت می شوند.
برای راهنمایی در مورد نحوه پاسخگویی به یک فرمان سفارشی به افزودن دستورات سفارشی مراجعه کنید.
رفتار پیش از اندروید 13
برای سازگاری به عقب، سیستم UI به ارائه یک طرح بندی جایگزین ادامه می دهد که از اقدامات اعلان برای برنامه هایی استفاده می کند که برای هدف قرار دادن Android 13 به روز نمی شوند یا اطلاعات 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) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();
حمایت از از سرگیری رسانه ها
از سرگیری رسانه به کاربران اجازه میدهد جلسات قبلی را بدون نیاز به راهاندازی برنامه از چرخ فلک مجدداً راهاندازی کنند. هنگامی که پخش شروع می شود، کاربر به روش معمول با کنترل های رسانه در تعامل است.
ویژگی ازسرگیری پخش را می توان با استفاده از برنامه تنظیمات، در زیر گزینه های صدا > رسانه، روشن و خاموش کرد. کاربر همچنین می تواند با ضربه زدن روی نماد چرخ دنده که پس از کشیدن انگشت روی چرخ فلک باز شده ظاهر می شود، به تنظیمات دسترسی پیدا کند.
Media3 برای تسهیل پشتیبانی از از سرگیری رسانه، APIهایی را ارائه می دهد. برای راهنمایی در مورد اجرای این ویژگی، از سرگیری پخش با مستندات Media3 را ببینید.
استفاده از API های رسانه قدیمی
این بخش نحوه ادغام با کنترل های رسانه سیستم را با استفاده از API های قدیمی MediaCompat توضیح می دهد.
این سیستم اطلاعات زیر را از 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 | بازی کنید | وضعیت فعلی 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
شما باید callback onPlay()
را اجرا کند.
اجرای MediaBrowserService
پس از راهاندازی دستگاه، سیستم به دنبال پنج برنامه رسانهای که اخیراً استفاده شدهاند میگردد و کنترلهایی را ارائه میکند که میتوانند برای شروع مجدد پخش از هر برنامه استفاده شوند.
سیستم سعی می کند با یک اتصال از SystemUI با MediaBrowserService
شما تماس بگیرد. برنامه شما باید چنین اتصالاتی را مجاز کند، در غیر این صورت نمی تواند از سرگیری پخش پشتیبانی کند.
اتصالات از SystemUI را می توان با استفاده از نام بسته com.android.systemui
و امضا شناسایی و تأیید کرد. SystemUI با امضای پلتفرم امضا شده است. نمونه ای از نحوه بررسی امضای پلت فرم را می توان در برنامه UAMP یافت.
به منظور پشتیبانی از ازسرگیری پخش، MediaBrowserService
شما باید این رفتارها را اجرا کند:
onGetRoot()
باید یک ریشه غیر تهی را به سرعت برگرداند. منطق پیچیده دیگر باید درonLoadChildren()
مدیریت شود.هنگامی که
onLoadChildren()
در شناسه رسانه ریشه فراخوانی می شود، نتیجه باید حاوی یک فرزند FLAG_PLAYABLE باشد.MediaBrowserService
باید آخرین آیتم رسانه ای پخش شده را هنگامی که درخواست EXTRA_RECENT دریافت می کند، برگرداند. مقدار بازگشتی باید یک آیتم رسانه واقعی باشد تا تابع عمومی.MediaBrowserService
باید MediaDescription مناسب با عنوان و زیرنویس غیر خالی ارائه دهد. همچنین باید یک نماد URI یا یک بیت مپ آیکون تنظیم کند.
مثالهای کد زیر نحوه پیادهسازی 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 (سطح API 33)، برای اطمینان از اینکه کاربران میتوانند به مجموعهای غنی از کنترلهای رسانه برای برنامههای پخش رسانه دسترسی داشته باشند، دکمههای عملکرد روی کنترلهای رسانه از حالت Player
مشتق شدهاند.
به این ترتیب، میتوانید مجموعهای ثابت از کنترلهای رسانه و تجربه کنترل رسانه صیقلیتری را در سراسر دستگاهها ارائه دهید.
شکل 1 نمونه ای از نحوه ظاهر این دستگاه را به ترتیب در تلفن و تبلت نشان می دهد.
سیستم حداکثر پنج دکمه عمل را بر اساس وضعیت Player
همانطور که در جدول زیر توضیح داده شده است نمایش می دهد. در حالت فشرده، تنها سه اسلات عمل اول نمایش داده می شوند. این با نحوه نمایش کنترلهای رسانه در دیگر پلتفرمهای Android مانند Auto، Assistant و Wear OS همخوانی دارد.
اسلات | معیارها | اقدام |
---|---|---|
1 | playWhenReady نادرست است یا وضعیت پخش فعلی STATE_ENDED است. | بازی کنید |
playWhenReady درست است و وضعیت پخش فعلی STATE_BUFFERING است. | در حال بارگیری اسپینر | |
playWhenReady درست است و وضعیت پخش فعلی STATE_READY است. | مکث کنید | |
2 | فرمان پخش کننده COMMAND_SEEK_TO_PREVIOUS یا COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM در دسترس است. | قبلی |
نه فرمان پخش کننده COMMAND_SEEK_TO_PREVIOUS و نه COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM موجود نیست و یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی | |
موارد اضافی جلسه شامل یک مقدار بولی true برای کلید EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV است. | خالی | |
3 | فرمان پخش کننده COMMAND_SEEK_TO_NEXT یا COMMAND_SEEK_TO_NEXT_MEDIA_ITEM در دسترس است. | بعدی |
نه فرمان پخش کننده COMMAND_SEEK_TO_NEXT و نه COMMAND_SEEK_TO_NEXT_MEDIA_ITEM موجود نیست و یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی | |
موارد اضافی جلسه شامل یک مقدار بولی true برای کلید EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT است. | خالی | |
4 | یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی |
5 | یک فرمان سفارشی از طرح سفارشی که هنوز قرار داده نشده است برای پر کردن شکاف موجود است. | سفارشی |
دستورات سفارشی به ترتیبی که به چیدمان سفارشی اضافه شده اند قرار می گیرند.
سفارشی کردن دکمه های فرمان
برای سفارشی کردن کنترلهای رسانه سیستم با Jetpack Media3 ، میتوانید هنگام اجرای MediaSessionService
، طرحبندی سفارشی جلسه و دستورات موجود کنترلرها را بر اساس آن تنظیم کنید:
در
onCreate()
یک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() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setCustomLayout(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) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build() ) .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() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .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()) .setCustomLayout(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) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build()) .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 به طور خودکار توسط جلسه رسانه مدیریت می شوند.
برای راهنمایی در مورد نحوه پاسخگویی به یک فرمان سفارشی به افزودن دستورات سفارشی مراجعه کنید.
رفتار پیش از اندروید 13
برای سازگاری به عقب، سیستم UI به ارائه یک طرح بندی جایگزین ادامه می دهد که از اقدامات اعلان برای برنامه هایی استفاده می کند که برای هدف قرار دادن Android 13 به روز نمی شوند یا اطلاعات 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) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();
حمایت از از سرگیری رسانه ها
از سرگیری رسانه به کاربران اجازه میدهد جلسات قبلی را بدون نیاز به راهاندازی برنامه از چرخ فلک مجدداً راهاندازی کنند. هنگامی که پخش شروع می شود، کاربر به روش معمول با کنترل های رسانه در تعامل است.
ویژگی ازسرگیری پخش را می توان با استفاده از برنامه تنظیمات، در زیر گزینه های صدا > رسانه، روشن و خاموش کرد. کاربر همچنین می تواند با ضربه زدن روی نماد چرخ دنده که پس از کشیدن انگشت روی چرخ فلک باز شده ظاهر می شود، به تنظیمات دسترسی پیدا کند.
Media3 برای تسهیل پشتیبانی از از سرگیری رسانه، APIهایی را ارائه می دهد. برای راهنمایی در مورد اجرای این ویژگی، از سرگیری پخش با مستندات Media3 را ببینید.
استفاده از API های رسانه قدیمی
این بخش نحوه ادغام با کنترل های رسانه سیستم را با استفاده از API های قدیمی MediaCompat توضیح می دهد.
این سیستم اطلاعات زیر را از 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 | بازی کنید | وضعیت فعلی 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
شما باید callback onPlay()
را اجرا کند.
اجرای MediaBrowserService
پس از راهاندازی دستگاه، سیستم به دنبال پنج برنامه رسانهای که اخیراً استفاده شدهاند میگردد و کنترلهایی را ارائه میکند که میتوانند برای شروع مجدد پخش از هر برنامه استفاده شوند.
سیستم سعی می کند با یک اتصال از SystemUI با MediaBrowserService
شما تماس بگیرد. برنامه شما باید چنین اتصالاتی را مجاز کند، در غیر این صورت نمی تواند از سرگیری پخش پشتیبانی کند.
اتصالات از SystemUI را می توان با استفاده از نام بسته com.android.systemui
و امضا شناسایی و تأیید کرد. SystemUI با امضای پلتفرم امضا شده است. نمونه ای از نحوه بررسی امضای پلت فرم را می توان در برنامه UAMP یافت.
به منظور پشتیبانی از ازسرگیری پخش، MediaBrowserService
شما باید این رفتارها را اجرا کند:
onGetRoot()
باید یک ریشه غیر تهی را به سرعت برگرداند. منطق پیچیده دیگر باید درonLoadChildren()
مدیریت شود.هنگامی که
onLoadChildren()
در شناسه رسانه ریشه فراخوانی می شود، نتیجه باید حاوی یک فرزند FLAG_PLAYABLE باشد.MediaBrowserService
باید آخرین آیتم رسانه ای پخش شده را هنگامی که درخواست EXTRA_RECENT دریافت می کند، برگرداند. مقدار بازگشتی باید یک آیتم رسانه واقعی باشد تا تابع عمومی.MediaBrowserService
باید MediaDescription مناسب با عنوان و زیرنویس غیر خالی ارائه دهد. همچنین باید یک نماد URI یا یک بیت مپ آیکون تنظیم کند.
مثالهای کد زیر نحوه پیادهسازی 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); }