إنشاء تطبيق وسائط مستند إلى نموذج

تطبيقات موسيقى مستندة إلى نماذج متوفّرة في إصدار تجريبي
في الوقت الحالي، يمكن لأي مستخدم نشر تطبيقات وسائط قائمة على النماذج في مسارات الاختبار الداخلي والمغلق على "متجر Google Play". وسيُسمَح بالنشر على المسارات المفتوحة ومسارات الإصدار العلني في وقت لاحق.

يمكن لتطبيقات الموسيقى التي تستخدم نماذج Car App Library تخصيص تجربة تصفُّح الموسيقى وتشغيلها مع ضمان تحسين التجربة لتناسب شاشات السيارات وتقليل مصادر التشتيت أثناء القيادة.

يفترض هذا الدليل أنّ لديك تطبيق وسائط يشغّل الصوت على الهاتف وأنّ تطبيق الوسائط يتوافق مع بنية تطبيق الوسائط على Android. تتيح لك مكتبة تطبيقات السيارات استبدال التجربة داخل التطبيق بنماذج بدلاً من تلك التي تم إنشاؤها باستخدام بنية بيانات إنشاء تطبيقات وسائط للسيارات MediaBrowser. يجب أيضًا توفير MediaSession لعناصر التحكّم في التشغيل، وMediaBrowserService أو MediaLibraryService، التي تُستخدم للاقتراحات والتجارب الذكية الأخرى.

تجارب "الركن ثم القيادة" متاحة الآن في إصدار تجريبي
أصبحت تطبيقات الوسائط المستندة إلى نماذج مؤهَّلة الآن لإضافة تجربة "التبديل من وضع ركن السيارة إلى وضع القيادة" على نظام التشغيل Android Automotive. ويتيح ذلك لتطبيقك إضافة أنشطة ناتجة من رموز برمجية أصلية وقابلة للتكيّف أثناء توقّف السيارة، والانتقال بسلاسة إلى تجربة تشغيل الوسائط المستندة إلى نموذج عند قيادة السيارة.

ضبط ملف البيان الخاص بتطبيقك

بالإضافة إلى الخطوات الموضّحة في مقالة استخدام مكتبة تطبيقات Android للسيارات، يجب استيفاء المتطلبات التالية في تطبيقات الوسائط المستندة إلى نماذج:

تحديد الفئات المتوافقة في بيان التطبيق

يجب أن يعرِّف تطبيقك عن androidx.car.app.category.MEDIA فئة تطبيق السيارة في فلتر intent الخاص بـ CarAppService.

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.MEDIA"/>
      </intent-filter>
    </service>
    ...
<application>

للوصول إلى MediaPlaybackTemplate، يجب أن يوضّح تطبيقك أيضًا إذن androidx.car.app.MEDIA_TEMPLATES في ملف البيان:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.MEDIA_TEMPLATES"/>
  ...
</manifest>

ضبط الحد الأدنى لمستوى واجهة برمجة تطبيقات السيارات

لا تتوافق تطبيقات الوسائط التي تستخدم MediaPlaybackTemplate إلا مع الإصدار 8 من واجهة برمجة تطبيقات CAL والإصدارات الأحدث، لذا تأكَّد من ضبط الحد الأدنى لمستوى Car App API level على 8.

<application ...>
  ...
  <meta-data
    android:name="androidx.car.app.minCarApiLevel"
    android:value="8"/>
  ...
</application>

تقديم رمز تحديد المصدر

احرص على إضافة رمز الإسناد لتطبيقات الموسيقى التي تم إنشاؤها باستخدام مكتبة Car App Library.

الإعلان عن توافق التطبيق مع Android Auto

تأكَّد من تضمين ما يلي في بيان تطبيقك:

<application>
  ...
  <meta-data android:name="com.google.android.gms.car.application"
      android:resource="@xml/automotive_app_desc"/>
  ...
</application>

بعد ذلك، أضِف بيان النموذج إلى automotive_app_desc.xml في موارد XML. ومن المفترض أن يظهر على النحو التالي:

<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
 <uses name="media"/>
 <uses name="template"/>
</automotiveApp>

الإشارة إلى التوافق مع نظام التشغيل Android Automotive

هناك طريقتان مختلفتان لتوزيع تطبيق وسائط متوافق مع Car App Library على نظام التشغيل Android Automotive: كحزمة APK واحدة أو كحزمتَي APK منفصلتَين. إذا كنت توزّع حزمة APK واحدة، ستكون متوافقة مع المركبات التي تم تفعيل نظام التشغيل Android Automotive فيها باستخدام مضيف &quot;مكتبة تطبيقات السيارات&quot;، وستعود إلى تطبيق MediaBrowserService أو MediaLibraryService في حال عدم تفعيل النظام، حتى في إصدارات Android القديمة (من Android 10 إلى Android 13). إذا اخترت توزيع حزمتَي APK منفصلتَين، يمكنك تحديث الإضافات الجديدة إلى إصدار Car App Library بدون الخوف من التأثير في إصدار MediaBrowserService أو MediaLibraryService من تطبيقك.

توزيع حزمة APK واحدة

عند توزيع حزمة APK واحدة لمكتبة تطبيقات السيارة وإصدارات MediaBrowserService أو MediaLibraryService من تطبيقك، من المهم ضبط android.software.car.templates_host.media على android:required="false".

<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>

بعد ذلك، اتّبِع إرشادات مكتبة تطبيقات السيارات لنظام التشغيل AAOS وأضِف CarAppActivity قابلاً للتشغيل (أو نشاطًا انتقاليًا). يجب ضبط النشاط على android:enabled="false" في ملف البيان. بعد ذلك، أضِف علامة بيانات وصفية إلى بيان MediaBrowserService تشير إلى أنّ المكوّن CarAppActivity هو البديل. اطّلِع على مثال البيان التالي:

<service android:name=".media.MyMediaService"
    android:exported="true"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
    </intent-filter>

    <!-- Link to Car App Library Activity -->
    <meta-data
        android:name="androidx.car.app.media.CalMediaActivityComponent"
        android:value="com.example.mediaapp.LaunchableTrampoline"/>
</service>

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false"> <!-- Set to false -->

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

توزيع Google Play

يجب تفعيل ملف APK الذي يتضمّن مكتبة Car App Library وMediaBrowserService أو MediaLibraryService باستخدام رمز إصدار أعلى و minSdk يستهدف Android 14 (34).

التوزيع باستخدام حزمتَي APK

لتوزيع حزمتَي APK منفصلتَين، إحداهما تستخدم مكتبة تطبيقات المركبات والأخرى تستخدم MediaBrowserService أو MediaLibraryService، اتّبِع الخطوات التالية لضمان استهداف إمكانات المركبة الصحيحة بشكل صحيح.

عند إنشاء حزمة APK منفصلة لإصدار تطبيقك المتوافق مع مكتبة Car App Library، يجب ضبط android.software.car.templates_host.media على android:required=true. يضمن ذلك توزيع التطبيق فقط على إصدارات نظام التشغيل Android Automotive المعتمَدة والمتوافقة مع مضيف "مكتبة تطبيقات السيارات".

<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>

إلى جانب استخدام android.software.car.templates_host.media وضبطه على android:required=true، اتّبِع هذه الخطوات لتفعيل نظام التشغيل Android Automotive لنشاط "مكتبة تطبيقات السيارات" القابل للتشغيل.

Play Distribution

يجب توزيع ملف APK الذي يستخدم مكتبة Car App Library في قناة الإصدار المحدود المخصّصة لنظام التشغيل Automotive.

إتاحة الإجراءات الصوتية

يمكنك تفعيل ميزة "التحكّم الصوتي" في تطبيقك للسماح للمستخدمين بإكمال الإجراءات الشائعة بدون لمس الجهاز. لمزيد من التفاصيل حول تعليمات التنفيذ، يُرجى الاطّلاع على إتاحة الإجراءات الصوتية للوسائط. في حال استخدام تطبيق وسائط مستند إلى نموذج، لن تحتاج إلى تعديل MediaBrowserService أو MediaLibraryService باستخدام نتائج البحث عند تلقّي طلب صوتي. بدلاً من ذلك، يمكنك إضافة إجراء في نموذج تشغيل الوسائط للسماح للمستخدم بالعثور على المزيد من المحتوى استنادًا إلى طلب البحث أو التشغيل هذا. يجب إتاحة الطلبات الصوتية لاستيفاء إرشادات الجودة VC-1.

إنشاء "نموذج التشغيل"

يعرض MediaPlaybackTemplate معلومات تشغيل الوسائط في تطبيق الوسائط في Car App Library. يتيح هذا النموذج ضبط عنوان رئيسي مع عنوان وإجراءات قابلة للتخصيص، بينما تتم تعبئة معلومات الوسائط وعناصر التحكّم في التشغيل من خلال المضيف استنادًا إلى حالة MediaSession في تطبيقك.

مشغّل موسيقى يعرض أغنية &quot;أصوات الربيع&quot; (Sounds of Spring) من أداء &quot;سمر فيلدينغ&quot; (Summer Fielding) مع
     صورة مربّعة لامرأة تعزف على الغيتار

الشكل 1: MediaPlaybackTemplate مع إجراء في العنوان لفتح قائمة المحتوى التالي في أعلى الشاشة

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

val playbackTemplate = MediaPlaybackTemplate.Builder()
      .setHeader(
        Header.Builder()
          .setStartHeaderAction(Action.BACK)
          .addEndHeaderAction(
                Action.Builder()
                  .setTitle(model.context.getString(R.string.queue_button_title))
                  .setIcon(
                    CarIcon.Builder(
                        IconCompat.createWithResource(
                          model.context,
                          R.drawable.gs_queue_music_vd_theme_24,
                        ))
                      .build())
                  .setOnClickListener(showQueueScreen())
                  .build())
          .setTitle(model.context.getString(R.string.media_playback_view_title))
          .build())
      .build()

عند استخدام MediaPlaybackTemplate، سجِّل رمزًا مميزًا لـ MediaSession باستخدام MediaPlaybackManager في CarAppService. ويؤدي عدم إجراء ذلك إلى ظهور خطأ عند إرسال MediaPlaybackTemplate إلى المضيف.

import androidx.car.app.media.MediaPlaybackManager
…

override fun onCreateSession(sessionInfo: SessionInfo): Session {
    return object : Session() {
        …

        init {
          lifecycle.addObserver(
            LifecycleEventObserver { _, event ->
              if (event == ON_CREATE) {
                val token = ... // MediaSessionCompat.Token
                (carContext.getCarService(CarContext.MEDIA_PLAYBACK_SERVICE) as MediaPlaybackManager)
                  .registerMediaPlaybackToken(token)
              }
              ...
            }
          )
        }
    }
}

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

بالنسبة إلى التطبيقات التي تستخدم مكتبة Media3، والتي تستخدم PlatformToken بدلاً من MediaSessionCompat.Token العادي، عليك تنفيذ SessionCommand مخصّص في MediaLibrarySession.Callback يعرض الرمز المميّز الأساسي للمنصة في الجلسة: session.platformToken. في CarAppService، أرسِل هذا الأمر المخصّص إلى الجلسة. بعد تلقّي الرمز المميّز للمنصة، يجب تحويله باستخدام MediaSessionCompat.Token.fromToken(platformToken) وتمرير هذا الرمز المميّز المتوافق إلى مكتبة تطبيقات السيارات في .registerMediaPlaybackToken().

تنظيم الوسائط باستخدام النماذج

لتنظيم الوسائط من أجل تصفّحها، مثل الأغاني أو الألبومات، ننصح باستخدام SectionedItemTemplate، الذي يتيح لك استخدام GridSection وRowSection معًا لإنشاء تنسيقات تجمع بين قوائم الصور والعناصر النصية.

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

الشكل 2: SectionedItemTemplate يحتوي على RowSection يليه GridSection

استخدام SectionedItemTemplate داخل TabTemplate

من الطرق السهلة لتصنيف الوسائط داخل تطبيقك استخدام SectionedItemTemplate داخل TabTemplate.

val template =
      SectionedItemTemplate.Builder()...build();
val tabTemplate =
      TabTemplate.Builder(tabCallback)
          .setTabContents(TabContents.Builder(template).build)
          .setHeaderAction(Action.APP_ICON)
          …
          .build();

مكوّنات وميزات الإصدار 1.9 من مكتبة تطبيقات السيارات

يقدّم الإصدار 1.9 من واجهة برمجة التطبيقات Car App Library مكوّنات مخصّصة لإمكانات تصفّح فريدة، مثل الشرائح وأشرطة التقدم والعناصر المكثّفة والعناوين التفاعلية والموسّعة وأقسام Spotlight وإعلانات البانر.

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

الشكل 3: SectionedItemTemplate يحتوي على Chips وCondensed Items وInteractive Header وGrid Items وMinimized Control Panel

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

الشكل 4: شاشتان لتصفّح الوسائط تتضمّنان Expanded Header وSpotlight Sections وProgress Bars

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

عند تصفّح الوسائط، من المهم أن يتمكّن المستخدم من الانتقال بسرعة إلى MediaPlaybackTemplate بأقل قدر من التشتيت.لتلبية متطلبات الجودة MFT-1، يجب أن يوفّر تطبيقك طريقة للوصول إلى MediaPlaybackTemplate من جميع شاشات تصفّح الوسائط.

إذا كنت تستخدم SectionedItemTemplate، يمكنك تحقيق ذلك من خلال إضافة زر إجراء ينقلك إلى شاشة تشغيل الوسائط. استخدِم الإجراء Action.MEDIA_PLAYBACK العادي في "مكتبة تطبيقات السيارات". سيعرض تطبيق الموسيقى هذا الإجراء على شكل لوحة تحكّم مصغّرة، وهو أمر مطلوب لاستيفاء متطلبات الجودة MFT-1 إذا كنت تستخدم الإصدار 1.9 أو إصدارًا أحدث من واجهة برمجة التطبيقات Car App Library. بالنسبة إلى النماذج الأخرى، يُعدّ إجراء العنوان طريقة أخرى لتحقيق ذلك.

التعامل مع طلبات تشغيل الوسائط في النظام

يجب توجيه المستخدم إلى MediaPlaybackTemplate عند تشغيل تطبيق من مساحة تشغيل وسائط في النظام، مثل بطاقة وسائط. نطلب من تطبيقات الوسائط معالجة هذا Intent Action من أجل توفير تجربة سلسة للمستخدمين.

أضِف الإجراء androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK إلى فلتر الأهداف لمكوّن مكتبة تطبيقات السيارات (إما CarAppActivity أو الترامبولين Activity).

تأكَّد من أنّ نشاطك يستخدم launchMode من singleTask أو singleTop ليتم استدعاء onNewIntent().

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false">

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

في فئة Session، يمكنك إلغاء onNewIntent() لتحليل الغرض الوارد. إذا كان إجراء intent الوارد يتطابق مع SHOW_MEDIA_PLAYBACK، يمكنك توجيه المستخدم إلى شاشة "التعرّف التلقائي على الموسيقى".

@Override
public void onNewIntent(@NonNull Intent intent) {
    super.onNewIntent(intent);
    if (SHOW_MEDIA_PLAYBACK.equals(intent.getAction())) {
        ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
        // Avoid redundant navigation if already on the playing screen
        if (screenManager.getTop() instanceof MyMediaPlayScreen) {
            return;
        }
        screenManager.push(MyMediaPlayScreen.createScreenFromPlaying(
                getCarContext(), mMediaSessionController));
    }
}

إذا كنت تستخدم نشاطًا انتقاليًا، تحقَّق من إجراء intent ضمن onCreate(). يجب تمرير هذا الإجراء إلى نية إنشاء CarAppActivity قبل استدعاء finish().

public class LaunchableTrampoline extends AppCompatActivity {
    private static final String SHOW_MEDIA_PLAYBACK = "androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent receivedIntent = getIntent();
        String action;

        if (SHOW_MEDIA_PLAYBACK.equals(receivedIntent.getAction())) {
            action = SHOW_MEDIA_PLAYBACK;
        } else {
            action = Intent.ACTION_MAIN;
        }

        Intent intent = new Intent(action);
        intent.setClassName(getPackageName(), "androidx.car.app.activity.CarAppActivity");
        startActivity(intent);
        finish();
    }
}