دليل نقل البيانات إلى AndroidX Media3

التطبيقات التي تستخدم حاليًا "com.google.android.exoplayer2" المستقل المكتبة وandroidx.media إلى androidx.media3. استخدام النص البرمجي للترحيل لنقل ملفات إصدار Gradle، وJava ملفات مصدر Kotlin وملفات تنسيق XML من ExoPlayer من 2.19.1 إلى AndroidX Media3 1.1.1

نظرة عامة

قبل نقل البيانات، راجِع الأقسام التالية للاطّلاع على مزيد من المعلومات عن: مزايا واجهات برمجة التطبيقات الجديدة وواجهات برمجة التطبيقات المراد نقلها والمتطلبات الأساسية التي ينبغي أن يلبيها مشروع تطبيقك.

أسباب نقل البيانات إلى Jetpack Media3

  • إنه الموطن الجديد لـ ExoPlayer، في حين أنّ com.google.android.exoplayer2 نهائيًا.
  • الوصول إلى Player API في مختلف المكوّنات/العمليات باستخدام MediaBrowser/MediaController
  • استخدِم الإمكانات المحسَّنة المتوفّرة في كل من MediaSession MediaController API.
  • الإعلان عن إمكانات التشغيل باستخدام عنصر تحكّم في الوصول بالغ الدقة
  • تبسيط تطبيقك من خلال إزالة MediaSessionConnector PlayerNotificationManager
  • متوافق مع الأنظمة القديمة مع واجهات برمجة تطبيقات عميل الوسائط المتوافقة (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

واجهات برمجة تطبيقات الوسائط المراد نقلها إلى AndroidX Media3

  • ExoPlayer وإضافاته
    ويشمل ذلك جميع وحدات مشروع ExoPlayer القديم باستثناء mediasession التي تم إيقافها. تعتمد التطبيقات أو الوحدات على يمكن نقل الحِزم في com.google.android.exoplayer2 باستخدام البرنامج النصي للترحيل.
  • MediaSessionConnector (اعتمادًا على androidx.media.* حزم من androidx.media:media:1.4.3+)
    إزالة MediaSessionConnector واستخدام androidx.media3.session.MediaSession بدلاً من ذلك.
  • MediaBrowserServiceCompat (اعتمادًا على androidx.media.* حزم من androidx.media:media:1.4.3+)
    نقل الفئات الفرعية من androidx.media.MediaBrowserServiceCompat إلى androidx.media3.session.MediaLibraryService والرمز باستخدام MediaBrowserCompat.MediaItem إلى androidx.media3.common.MediaItem.
  • MediaBrowserCompat (اعتمادًا على android.support.v4.media.* حزم من androidx.media:media:1.4.3+)
    نقل رمز العميل باستخدام MediaBrowserCompat أو MediaControllerCompat لاستخدام androidx.media3.session.MediaBrowser مع androidx.media3.common.MediaItem.

المتطلّبات الأساسية

  1. التأكُّد من أنّ مشروعك خاضعًا للتحكم في المصدر

    تأكَّد من إمكانية التراجع بسهولة عن التغييرات التي تم تطبيقها من خلال أدوات نقل البيانات النصية. إذا لم يكن مشروعك ضمن التحكم في المصدر بعد، هذا هو الوقت المناسب لنبدأ بها. إذا كنت لا تريد القيام بذلك لسبب ما، نسخة احتياطية من مشروعك قبل البدء في الترحيل.

  2. تحديث تطبيقك

    • نوصي بتحديث مشروعك لاستخدام أحدث إصدار من مكتبة ExoPlayer وإزالة أي لاستدعاء طرق متوقفة. إذا كنت تنوي استخدام النص البرمجي لعملية نقل البيانات، عليك مطابقة الإصدار الذي يجري التحديث إليه بالنسخة التي يعالجها النص البرمجي.

    • يجب زيادة قيمة compileSdkVersion في تطبيقك إلى 32 على الأقل.

    • عليك ترقية أداة Gradle ومكوّن Gradle المتوافق مع "استوديو Android" إلى حزمة يعمل مع التبعيات المحدثة المذكورة أعلاه. بالنسبة مثال:

      • إصدار المكوّن الإضافي لنظام Gradle المتوافق مع Android: 7.1.0
      • إصدار Gradle: 7.4
    • استبدال جميع عبارات استيراد أحرف البدل التي تستخدم علامة النجمة (*) واستخدام عبارات الاستيراد المؤهلة بالكامل: حذف حرف البدل بيانات الاستيراد واستخدام "استوديو Android" لاستيراد البيانات العبارات (F2 - Alt/Enter، F2 - Alt/Enter، ...).

    • نقل البيانات من com.google.android.exoplayer2.PlayerView إلى com.google.android.exoplayer2.StyledPlayerView هذا الإجراء ضروري لأنه لا يوجد مكافئ com.google.android.exoplayer2.PlayerView في AndroidX Media3.

نقل بيانات ExoPlayer من خلال إتاحة النص البرمجي

يسهِّل النص البرمجي الانتقال من com.google.android.exoplayer2 إلى العلامة الجديدة. بنية الحزمة والوحدات ضمن androidx.media3. ينطبق النص البرمجي بعض فحوصات التحقق من مشروعك وتطبع التحذيرات في حالة فشل التحقق من الصحة. وبخلاف ذلك، يتم تطبيق عمليات ربط الفئات والحِزم التي تمت إعادة تسميتها في لمشروع Android gradle مكتوب بلغة Java أو Kotlin.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

استخدام النص البرمجي لنقل البيانات

  1. نزِّل النص البرمجي للنقل من علامة مشروع ExoPlayer على GitHub المتوافق مع الإصدار الذي حدّثت تطبيقك إليه:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. جعل النص البرمجي قابلاً للتنفيذ:

    chmod 744 media3-migration.sh
    
  3. شغِّل النص البرمجي باستخدام --help للاطّلاع على الخيارات.

  4. شغِّل النص البرمجي باستخدام -l لإدراج مجموعة الملفات التي تم اختيارها نقل البيانات (يمكنك استخدام -f لفرض بطاقة بيانات المتجر بدون تحذيرات):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. شغِّل النص البرمجي باستخدام -m لربط الحِزم والفئات والوحدات بـ Media3. سيؤدي تشغيل النص البرمجي باستخدام الخيار "-m" إلى تطبيق التغييرات على النص البرمجي المحدّد. الملفات.

    • التوقف عند خطأ في التحقق من الصحة بدون إجراء تغييرات
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • التنفيذ الإجباري

    وإذا عثر النص البرمجي على انتهاك للمتطلبات الأساسية، فيمكن إجراء عملية الترحيل تم فرض العلامة التجارية -f:

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

أكمِل هذه الخطوات اليدوية بعد تشغيل النص البرمجي باستخدام الخيار -m:

  1. التحقّق من الطريقة التي غيّر بها النص البرمجي رمزك: استخدِم أداة الاختلاف وأصلح المشكلة. (يمكنك الإبلاغ عن خطأ إذا كنت تعتقد أنّ النص البرمجي مشكلة عامة تم تقديمها بدون تمرير الخيار -f).
  2. إنشاء المشروع: إما استخدام ./gradlew clean build أو في Android استوديو الاختيار ملف > مزامنة المشروع مع ملفات Gradle، ثم إنشاء > "تنظيف المشروع"، ثم إنشاء > إعادة إنشاء المشروع (مراقبة الإنشاء في "إنشاء - مخرجات البنية" علامة التبويب في "استوديو Android"

خطوات المتابعة المقترَحة:

  1. حلّ الموافقة على الأخطاء المتعلّقة باستخدام واجهات برمجة التطبيقات غير الثابتة
  2. استبدال طلبات البيانات من واجهة برمجة التطبيقات المتوقّفة نهائيًا: استخدام واجهة برمجة التطبيقات البديلة المقترَحة وضع المؤشر فوق التحذير في "استوديو Android" والرجوع إلى JavaDoc للرمز الذي تم إيقافه لمعرفة ما يجب استخدامه بدلاً من استدعاء معين.
  3. ترتيب عبارات الاستيراد: افتح المشروع في "استوديو Android" ثم النقر بزر الماوس الأيمن على عقدة مجلد حزمة في عارض المشروع واختيار تحسين عمليات الاستيراد في الحِزم التي تحتوي على ملفات المصدر التي تم تغييرها

استبدال MediaSessionConnector بـ androidx.media3.session.MediaSession

في عالم MediaSessionCompat القديم، كان MediaSessionConnector المسئولة عن مزامنة حالة المشغل مع حالة الجلسة وتلقّي الأوامر من وحدات التحكم التي تحتاج إلى تفويض واللاعبين. يتم تنفيذ ذلك من خلال AndroidX Media3 بواسطة MediaSession مباشرةً بدون الحاجة إلى موصل.

  1. إزالة جميع المراجع واستخدام MediaSessionConnector: إذا كنت قد استخدمت النص البرمجي الآلي لنقل فئات وحزم ExoPlayer، ثم من المحتمل أن يكون النص البرمجي قد ترك الرمز في حالة غير قابلة للتجميع MediaSessionConnector التي لا يمكن حلها. سيتيح لك "استوديو Android" سيظهر لك الرمز المعطّل عند محاولة إنشاء التطبيق أو تشغيله.

  2. في ملف build.gradle الذي تحتفظ فيه بتبعياتك، أضِف اعتمادية التنفيذ على وحدة جلسة AndroidX Media3 وإزالة التبعية القديمة:

    implementation "androidx.media3:media3-session:1.4.0"
    
  3. استبدِل MediaSessionCompat بـ androidx.media3.session.MediaSession

  4. في موقع الرمز الإلكتروني الذي أنشأت فيه MediaSessionCompat القديم، استخدِم androidx.media3.session.MediaSession.Builder لبناء MediaSession مرر المشغِّل لإنشاء أداة إنشاء الجلسات.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. نفِّذ MySessionCallback حسب ما يتطلبه تطبيقك. وهذه خطوة اختيارية. في حال حذف فأنت تريد السماح لوحدات التحكم بإضافة عناصر الوسائط إلى المشغل، فنفذ MediaSession.Callback.onAddMediaItems() يخدم العديد من المواقع طرق واجهة برمجة التطبيقات القديمة التي تضيف ملفات الوسائط إلى المشغّل لتشغيلها في متوافقة مع الأنظمة القديمة. ويتضمن ذلك MediaController.set/addMediaItems() طريقة لوحدة تحكم Media3، باسم بالإضافة إلى TransportControls.prepareFrom*/playFrom* واجهة برمجة التطبيقات القديمة. يمكنك استخدام نموذج لتنفيذ onAddMediaItems يمكن العثور عليها في PlaybackService من التطبيق التجريبي للجلسة.

  6. إغلاق جلسة الوسائط في موقع الرمز البرمجي حيث تسببت في إتلاف جلستك قبل النقل:

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    

وظيفة "MediaSessionConnector" في Media3

يعرض الجدول التالي واجهات برمجة تطبيقات Media3 التي تعالج الوظائف تم تنفيذها سابقًا في MediaSessionConnector.

أداة MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (يُطلق على prepare() داخليًا)
QueueNavigator ForwardingPlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

نقل بيانات MediaBrowserService إلى MediaLibraryService

تقدِّم AndroidX Media3 الإصدار MediaLibraryService الذي يحل محل MediaBrowserServiceCompat مستند JavaDoc الخاص بـ MediaLibraryService وميزاته الفئة MediaSessionService توفر مقدمة جيدة لواجهة برمجة التطبيقات نموذج البرمجة غير المتزامن للخدمة.

يتوافق MediaLibraryService مع الإصدارات القديمة مع MediaBrowserService تطبيق عميل يستخدم MediaBrowserCompat أو سيستمر MediaControllerCompat في العمل بدون تغيير الرمز عند الاتصال. إلى MediaLibraryService. بالنسبة إلى العميل، يتسم التطبيق بشفافية ما إذا كان باستخدام MediaLibraryService أو MediaBrowserServiceCompat قديم.

مخطط لمكونات التطبيق مع الخدمة والنشاط والتطبيقات الخارجية.
الشكل 1: نظرة عامة على مكوِّنات تطبيق الوسائط
  1. لكي يعمل التوافق مع الأنظمة القديمة، يجب تسجيل كلتا الخدمتين. بخدمتك في AndroidManifest.xml. بهذه الطريقة يعثر العميل على خدمتك من خلال واجهة الخدمة المطلوبة:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. في ملف build.gradle الذي تحتفظ فيه بتبعياتك، أضِف اعتمادية التنفيذ على وحدة جلسة AndroidX Media3 لإزالة التبعية القديمة:

    implementation "androidx.media3:media3-session:1.4.0"
    
  3. تغيير خدمتك ليتم اكتسابها من MediaLibraryService بدلاً من MediaBrowserService كما ذكرنا سابقًا، MediaLibraryService متوافق مع الإصدار القديم. MediaBrowserService وبناءً عليه، فإن واجهة برمجة التطبيقات الأوسع التي تستخدمها الخدمة تقديمه للعملاء لا يزال كما هو. لذلك من المحتمل أن يحتفظ التطبيق معظم المنطق المطلوب لتنفيذ MediaBrowserService وتكييفه لتلائم MediaLibraryService الجديد

    الاختلافات الرئيسية مقارنةً بالنظام القديم في ما يلي MediaBrowserServiceCompat:

    • تنفيذ طرق دورة حياة الخدمة: الطرق التي يجب يتم تجاوزها في الخدمة نفسها هي onCreate/onDestroy، حيث يتم يخصّص أو يخصَّص جلسة المكتبة والمشغّل الموارد. بالإضافة إلى الطرق القياسية لدورة حياة الخدمة، يمكن يجب إلغاء onGetSession(MediaSession.ControllerInfo) لعرض MediaLibrarySession التي تم بناؤها في onCreate.

    • تنفيذ MediaLibraryService.MediaLibrarySessionCallback: الإنشاء جلسة تتطلب MediaLibraryService.MediaLibrarySessionCallback التي تنفّذ طرق واجهة برمجة تطبيقات النطاق الفعلية. لذا بدلاً من إلغاء طرق واجهة برمجة التطبيقات الخدمة القديمة، فستلغي طرق MediaLibrarySession.Callback بدلاً من ذلك.

      بعد ذلك، يتم استخدام هذه الدالة لإنشاء MediaLibrarySession:

      mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, MySessionCallback())
               .build()
      

      ابحث عن واجهة برمجة التطبيقات الكاملة لـ MediaLibrarySessionCallback في واجهة برمجة التطبيقات. التوثيق.

    • تنفيذ MediaSession.Callback.onAddMediaItems(): معاودة الاتصال عدد مرات العرض: onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) طرق مختلفة حالية وقديمة لواجهة برمجة التطبيقات تضيف ملفات الوسائط إلى المشغّل للتشغيل بطريقة متوافقة مع الأنظمة القديمة. ويتضمن ذلك MediaController.set/addMediaItems() طريقة لوحدة التحكم Media3، فضلاً عن TransportControls.prepareFrom*/playFrom* واجهة برمجة التطبيقات القديمة. يمكن لتنفيذ نموذجي لمعاودة الاتصال يمكن العثور عليها في PlaybackService من التطبيق التجريبي للجلسة.

    • يستخدم AndroidX Media3 androidx.media3.common.MediaItem بدلاً من ذلك من MediaBrowserCompat.MediaItem وMediaMetadataCompat. قطع الغيار يجب تغيير رمزك المرتبط بالفئات القديمة وفقًا لذلك أو الربط بـ Media3 MediaItem بدلاً من ذلك.

    • تم تغيير نموذج البرمجة غير المتزامنة العام إلى Futures في على عكس طريقة Result القابلة للفصل MediaBrowserServiceCompat يمكن أن يؤدي تنفيذ خدمتك إلى ظهور غير متزامن ListenableFuture بدلاً من فصل نتيجة أو إرجاع مستقبل مباشر لإرجاع قيمة مباشرةً.

إزالة PlayerNotificationManager

يتوافق "MediaLibraryService" مع إشعارات الوسائط تلقائيًا يمكن إزالة PlayerNotificationManager عند استخدام MediaLibraryService أو MediaSessionService

يمكن لأي تطبيق تخصيص الإشعار عن طريق ضبط MediaNotification.Provider في onCreate() تحل محل DefaultMediaNotificationProvider يتولى MediaLibraryService بعد ذلك بدء الخدمة في المقدمة على النحو المطلوب.

من خلال تجاوز MediaLibraryService.updateNotification()، يمكن للتطبيق تنفيذ المزيد من المهام الملكية الكاملة لنشر الإشعار وبدء/إيقاف الخدمة في المقدمة على النحو المطلوب.

نقل رمز العميل باستخدام MediaBrowser

مع AndroidX Media3، MediaBrowser ينفذ MediaController/Player من الواجهات ويمكن استخدامها للتحكم في تشغيل الوسائط إلى جانب تصفح الوسائط المكتبة. إذا كان عليك إنشاء MediaBrowserCompat MediaControllerCompat في العالم القديم، يمكنك فعل الشيء نفسه من خلال استخدام MediaBrowser في Media3.

يمكن إنشاء "MediaBrowser" وانتظار الاتصال الخدمة التي يتم إنشاؤها:

scope.launch {
    val sessionToken =
        SessionToken(context, ComponentName(context, MusicService::class.java)
    browser =
        MediaBrowser.Builder(context, sessionToken))
            .setListener(BrowserListener())
            .buildAsync()
            .await()
    // Get the library root to start browsing the library.
    root = browser.getLibraryRoot(/* params= */ null).await();
    // Add a MediaController.Listener to listen to player state events.
    browser.addListener(playerListener)
    playerView.setPlayer(browser)
}

الاطّلاع على التحكّم في التشغيل في جلسة تشغيل الوسائط لمعرفة كيفية إنشاء MediaController للتحكّم في التشغيل من خلال الخلفية.

خطوات إضافية وإخلاء

أخطاء واجهة برمجة التطبيقات غير الثابتة

بعد النقل إلى Media3، قد تظهر لك أخطاء أداة Lint حول الاستخدامات غير المستقرة لواجهة برمجة التطبيقات. يمكن استخدام واجهات برمجة التطبيقات هذه بشكل آمن، كما أن أخطاء الوبر هي منتج ثانوي يضمن التوافق الثنائي. إذا لم تكن تطلب برنامجًا ثنائيًا دقيقًا التوافق، يمكن منع هذه الأخطاء بأمان باستخدام @OptIn التعليق التوضيحي.

خلفية

لم يقدّم الإصدار 1 أو 2 من ExoPlayer ضمانات صارمة بشأن التوافق الثنائي. من المكتبة بين الإصدارات اللاحقة. تعتبر واجهة برمجة التطبيقات ExoPlayer مهمة جدًا كبيرة من حيث التصميم، للسماح للتطبيقات بتخصيص كل جانب من جوانب التشغيل. وتقدم الإصدارات اللاحقة من ExoPlayer رمزًا إعادة التسمية أو التغييرات الأخرى التي قد تؤدي إلى أعطال (مثل إضافة طرق جديدة مطلوبة على الواجهات) ضِمن وفي معظم الحالات، يتم التخفيف من حدة هذه الانقطاعات من خلال إدخال الرمز الجديد إلى جانب إيقاف الرمز القديم لبعض الإصدارات، وذلك للسماح للمطوّرين المستخدم لنقل بيانات الاستخدام، إلا أن هذا لم يكن ممكنًا دائمًا.

أدّت هذه التغييرات التي قد تؤدي إلى عطل إلى حدوث مشكلتَين لمستخدمي الإصدار الأول من ExoPlayer. ومكتبات الإصدار 2:

  1. وقد تؤدي الترقية من إصدار ExoPlayer إلى إيقاف تجميع الرمز.
  2. يشير هذا المصطلح إلى تطبيق يعتمد على ExoPlayer بشكل مباشر أو من خلال حساب متوسط. المكتبة يجب أن يتأكد من أن كلا التبعيتين كانتا نفس الإصدار، وإلا فقد يؤدي عدم التوافقات الثنائية إلى حدوث أعطال في وقت التشغيل.

التحسينات في Media3

تضمن منصة Media3 التوافق الثنائي مع مجموعة فرعية من مساحة عرض واجهة برمجة التطبيقات. تشير رسالة الأشكال البيانية يتم وضع علامة على الأجزاء التي لا تضمن التوافق الثنائي معها @UnstableApi ولتوضيح هذا التمييز، فإنّ الاستخدامات غير المستقرة تؤدي رموز واجهة برمجة التطبيقات إلى إنشاء خطأ في الوبر ما لم تتم إضافة تعليقات توضيحية إليها باستخدام @OptIn.

بعد النقل من الإصدار الثاني من ExoPlayer إلى Media3، قد تظهر لك رسائل واجهة برمجة تطبيقات غير مستقرة. الأخطاء في الوبر. قد يجعل هذا الأمر يبدو أن Media3 "أقل ثباتًا" من ExoPlayer الإصدار 2. لكن الأمر ليس كذلك. كلمة 'غير مستقرة' لا تختلف جزأين من واجهة برمجة تطبيقات Media3 مستوى الثبات ككامل مساحة واجهة برمجة التطبيقات ExoPlayer v2 لا تتوفر ضمانات لمساحة واجهة برمجة تطبيقات Media3 الثابتة في الإصدار 2 من ExoPlayer على الكل. يكمن الاختلاف في أن خطأ الوبر ينبهك الآن إلى ومستويات الاستقرار.

معالجة أخطاء أداة Lint غير الثابتة لواجهة برمجة التطبيقات

راجع قسم تحرّي الخلل وإصلاحه ضمن أخطاء الوبر هذه للحصول على تفاصيل حول كيفية إضافة تعليقات توضيحية حول استخدامات Java وKotlin لواجهات برمجة التطبيقات غير الثابتة باستخدام @OptIn

واجهات برمجة التطبيقات التي تم إيقافها نهائيًا

قد تلاحظ أن طلبات البيانات من واجهات برمجة التطبيقات المتوقّفة نهائيًا قد تم إيقافها في نظام Android. استوديو YouTube. نقترح استبدال هذه المكالمات بالبديل المناسب. مرِّر مؤشر الماوس فوق الرمز لعرض JavaDoc الذي يحدد واجهة برمجة التطبيقات التي يجب استخدامها بدلاً من ذلك.

لقطة شاشة: كيفية عرض JavaDoc مع طريقة أخرى متوقّفة نهائيًا
الشكل 3: يقترح تلميح JavaDoc في "استوديو Android" بديلاً لأي رمز تم إيقافه.

عيّنات التعليمات البرمجية والتطبيقات التجريبية

  • التطبيق التجريبي لجلسة AndroidX Media3 (الأجهزة الجوّالة وWearOS)
    • الإجراءات المخصّصة
    • إشعار واجهة مستخدم النظام، MediaButton/BT
    • التحكّم في التشغيل باستخدام "مساعد Google"
  • UAMP: Android Media Player (branch media3) (الأجهزة الجوّالة، AutomotiveOS)
    • إشعار واجهة مستخدم النظام، MediaButton/BT، استئناف التشغيل
    • عنصر التحكّم في التشغيل على نظام التشغيل Wear OS أو "مساعد Google"
    • AutomotiveOS: الطلب المخصَّص وتسجيل الدخول