عناصر التحكّم في الوسائط

تقع عناصر التحكم في الوسائط في Android بالقرب من "الإعدادات السريعة". جلسات من يتم ترتيب عدة تطبيقات في لوحة عرض دوّارة قابلة للتمرير السريع. تدرج لوحة العرض الدوّارة الجلسات بهذا الترتيب:

  • عمليات البث التي يتم تشغيلها محليًا على الهاتف
  • أحداث البث عن بُعد، مثل البث الذي يتم رصده على أجهزة خارجية أو جلسات البث
  • الجلسات السابقة القابلة للاستئناف، بالترتيب لآخر مرة تم تشغيلها بها

لضمان إمكانية وصول المستخدمين إلى محتوى من نظام التشغيل Android 13 (المستوى 33 من واجهة برمجة التطبيقات) مجموعة من عناصر التحكّم في الوسائط للتطبيقات التي تشغِّل الوسائط، وأزرار الإجراءات على عناصر التحكّم في الوسائط يتم اشتقاقها من الحالة Player.

بهذه الطريقة، يمكنك تقديم مجموعة متسقة من عناصر التحكم في الوسائط وجعل تجربة التحكم في الوسائط على جميع الأجهزة

يوضح الشكل 1 مثالاً على كيفية ظهور ذلك على هاتف أو جهاز لوحي، على التوالي.

أدوات التحكم في الوسائط من حيث كيفية ظهورها على الهواتف والأجهزة اللوحية،
            باستخدام مثال لعيّنة من مسار يوضّح طريقة ظهور الأزرار
الشكل 1: عناصر التحكّم في الوسائط على الهواتف والأجهزة اللوحية

يعرض النظام ما يصل إلى خمسة أزرار إجراءات استنادًا إلى حالة Player. الموضحة في الجدول التالي. في الوضع المكثف، لا يتم إجراء سوى أول ثلاثة إجراءات يتم عرض الخانات. يتوافق هذا مع كيفية عرض عناصر التحكم في الوسائط في منصات Android، مثل Auto و"مساعد Google" و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، كما يتوفّر أمر مخصّص من التنسيق المخصّص الذي لم يتم وضعه بعد لملء الخانة. قرض مخصص
(غير متوافقة بعد مع Media3) PlaybackState العناصر الإضافية تشمل قيمة منطقية 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، كما يتوفّر أمر مخصّص من التنسيق المخصّص الذي لم يتم وضعه بعد لملء الخانة. قرض مخصص
(غير متوافقة بعد مع Media3) PlaybackState العناصر الإضافية تشمل قيمة منطقية true للمفتاح EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT. فارغ
4 يتوفّر أمر مخصّص من التنسيق المخصّص الذي لم يتم وضعه بعد لملء الخانة. قرض مخصص
5 يتوفّر أمر مخصّص من التنسيق المخصّص الذي لم يتم وضعه بعد لملء الخانة. قرض مخصص

يتم وضع الأوامر المخصصة بترتيب إضافتها إلى تخطيط مخصص.

تخصيص أزرار الأوامر

لتخصيص عناصر التحكّم في وسائط النظام باستخدام Jetpack Media3، يُرجى اتّباع الخطوات التالية: يمكنك تعيين التخطيط المخصص للجلسة والأوامر المتاحة وفقًا لذلك، عند تنفيذ MediaSessionService:

  1. في "onCreate()"، عليك إنشاء MediaSession. وتحديد التنسيق المخصّص من أزرار الأوامر.

  2. في MediaSession.Callback.onConnect()، تفويض وحدات التحكم من خلال تحديد أوامرها المتاحة، بما في ذلك الطلبات المخصّصة، في ConnectionResult.

  3. في 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()
        .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)
    }
  }
}

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()
            .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 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 تتم معالجة واجهة المستخدم تلقائيًا بواسطة الوسائط جلسة المراجعة.

يمكنك الاطّلاع على مقالة إضافة طلبات مخصّصة. للحصول على إرشادات حول كيفية الاستجابة إلى أمر مخصص.

السلوك قبل الإصدار 13 من نظام التشغيل Android

للتوافق مع الأنظمة القديمة، تستمر واجهة مستخدم النظام في توفير تنسيق بديل. تستخدم إجراءات الإشعارات للتطبيقات التي لا يتم تحديثها لاستهداف الإصدار 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)
        .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 واجهات برمجة تطبيقات لتسهيل إمكانية استئناف الوسائط. يمكنك الاطّلاع على استئناف التشغيل باستخدام Media3 مستندات للحصول على إرشادات حول استخدام هذه الميزة.

استخدام واجهات برمجة التطبيقات القديمة للوسائط

يشرح هذا القسم كيفية الدمج مع عناصر التحكّم في وسائط النظام باستخدام واجهات برمجة تطبيقات 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

يعرض مشغّل الوسائط التقدم المحرز في الوسائط التي يتم تشغيلها حاليًا، بالإضافة إلى شريط بحث مرتبط بـ PlaybackState في MediaSession. شريط التقديم/الترجيع يسمح للمستخدمين بتغيير الموضع وعرض الوقت المنقضي للوسائط عنصر واحد. لتفعيل شريط البحث، يجب تنفيذ PlaybackState.Builder#setActions بما في ذلك ACTION_SEEK_TO.

الشريحة الإجراء المعايير
1 تشغيل الولاية الحالية من PlaybackState هي واحدة مما يلي:
  • STATE_NONE
  • STATE_STOPPED
  • STATE_PAUSED
  • STATE_ERROR
مؤشر سريان العمل الولاية الحالية لـ PlaybackState هي واحدة مما يلي:
  • STATE_CONNECTING
  • STATE_BUFFERING
إيقاف مؤقت حالة PlaybackState الحالية هي لا شيء مما سبق.
2 الصفحة السابقة تشمل إجراءات PlaybackState ACTION_SKIP_TO_PREVIOUS.
قرض مخصص PlaybackState لا تشمل الإجراءات ACTION_SKIP_TO_PREVIOUS، بينما تتضمّن الإجراءات المخصّصة PlaybackState إجراءً مخصّصًا لم يتم وضعه إلى الآن.
فارغ تتضمّن العناصر الإضافية في PlaybackState قيمة منطقية true للمفتاح SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV.
3 التالي تشمل إجراءات PlaybackState ACTION_SKIP_TO_NEXT.
قرض مخصص PlaybackState لا تشمل الإجراءات ACTION_SKIP_TO_NEXT، بينما تتضمّن الإجراءات المخصّصة PlaybackState إجراءً مخصّصًا لم يتم وضعه إلى الآن.
فارغ تتضمّن العناصر الإضافية في PlaybackState قيمة منطقية true للمفتاح SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT.
4 قرض مخصص تتضمّن الإجراءات المخصّصة في PlaybackState إجراءً مخصّصًا لم يتم وضعه بعد.
5 قرض مخصص تتضمّن الإجراءات المخصّصة في PlaybackState إجراءً مخصّصًا لم يتم وضعه بعد.

إضافة إجراءات عادية

توضح أمثلة الرموز التالية كيفية إضافة معيار PlaybackState إجراءات مخصصة.

لتشغيل المحتوى وإيقافه مؤقتًا والسابق والتالي، اضبط هذه الإجراءات على PlaybackState لجلسة الوسائط.

Kotlin

val session = MediaSessionCompat(context, TAG)
val playbackStateBuilder = PlaybackStateCompat.Builder()
val style = NotificationCompat.MediaStyle()

// For this example, the media is currently paused:
val state = PlaybackStateCompat.STATE_PAUSED
val position = 0L
val playbackSpeed = 1f
playbackStateBuilder.setState(state, position, playbackSpeed)

// And the user can play, skip to next or previous, and seek
val stateActions = PlaybackStateCompat.ACTION_PLAY
    or PlaybackStateCompat.ACTION_PLAY_PAUSE
    or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
    or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
    or PlaybackStateCompat.ACTION_SEEK_TO // adding the seek action enables seeking with the seekbar
playbackStateBuilder.setActions(stateActions)

// ... do more setup here ...

session.setPlaybackState(playbackStateBuilder.build())
style.setMediaSession(session.sessionToken)
notificationBuilder.setStyle(style)

Java

MediaSessionCompat session = new MediaSessionCompat(context, TAG);
PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();

// For this example, the media is currently paused:
int state = PlaybackStateCompat.STATE_PAUSED;
long position = 0L;
float playbackSpeed = 1f;
playbackStateBuilder.setState(state, position, playbackSpeed);

// And the user can play, skip to next or previous, and seek
long stateActions = PlaybackStateCompat.ACTION_PLAY
    | PlaybackStateCompat.ACTION_PLAY_PAUSE
    | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
    | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
    | PlaybackStateCompat.ACTION_SEEK_TO; // adding this enables the seekbar thumb
playbackStateBuilder.setActions(stateActions);

// ... do more setup here ...

session.setPlaybackState(playbackStateBuilder.build());
style.setMediaSession(session.getSessionToken());
notificationBuilder.setStyle(style);

إذا كنت لا تريد أي أزرار في الخانات السابقة أو التالية، لا تضف ACTION_SKIP_TO_PREVIOUS أو ACTION_SKIP_TO_NEXT، وبدلاً من ذلك يمكنك إضافة ميزات إضافية إلى الجلسة:

Kotlin

session.setExtras(Bundle().apply {
    putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true)
    putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true)
})

Java

Bundle extras = new Bundle();
extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true);
extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true);
session.setExtras(extras);

إضافة إجراءات مخصّصة

بالنسبة إلى الإجراءات الأخرى التي تريد عرضها في عناصر التحكّم في الوسائط، يمكنك إنشاء PlaybackStateCompat.CustomAction وإضافته إلى PlaybackState بدلاً من ذلك يتم عرض هذه الإجراءات في وترتيب إضافتها.

Kotlin

val customAction = PlaybackStateCompat.CustomAction.Builder(
    "com.example.MY_CUSTOM_ACTION", // action ID
    "Custom Action", // title - used as content description for the button
    R.drawable.ic_custom_action
).build()

playbackStateBuilder.addCustomAction(customAction)

Java

PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction.Builder(
        "com.example.MY_CUSTOM_ACTION", // action ID
        "Custom Action", // title - used as content description for the button
        R.drawable.ic_custom_action
).build();

playbackStateBuilder.addCustomAction(customAction);

الاستجابة لإجراءات حالة التشغيل

عندما ينقر المستخدم على زر ما، يستخدم 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 والتوقيع. تم توقيع واجهة مستخدم النظام باستخدام النظام الأساسي التوقيع. مثال على كيفية التحقق من توقيع المنصة في تطبيق UAMP

لإتاحة استئناف التشغيل، يجب أن يكون MediaBrowserService وتنفيذ هذه السلوكيات:

  • يجب أن تعرض الدالة onGetRoot() جذرًا غير فارغ بسرعة. ينبغي أن يكون المنطق المعقد الآخر ستتم معالجتها في onLoadChildren()

  • فعندما يتم استدعاء onLoadChildren() على معرّف الوسائط الجذر، ويجب أن تحتوي النتيجة على FLAG_PLAYABLE طفل

  • يجب أن يعرض MediaBrowserService ملف الوسائط الذي تم تشغيله مؤخرًا عند يتلقى EXTRA_حديثة طلب البحث. يجب أن تكون القيمة التي يتم عرضها عنصر وسائط فعليًا وليس عامًا الأخرى.

  • يجب أن يقدم MediaBrowserService قيمة MediaDescription مع قيمة غير فارغة title و العنوان الفرعي. يجب أيضًا تعيين معرّف الموارد المنتظم (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);
}