يساعدك Android Auto ونظام التشغيل Android Automotive في توفير محتوى تطبيقك الإعلامي للمستخدمين في سياراتهم. يجب أن يقدّم تطبيق الوسائط للسيارات خدمة تصفّح وسائط لكي يتمكّن Android Auto ونظام التشغيل Android Automotive أو تطبيق آخر يتضمّن تصفّح الوسائط من اكتشاف المحتوى الخاص بك وعرضه.
يفترض هذا الدليل أنّ لديك تطبيق وسائط لتشغيل المحتوى الصوتي على هاتف ويتوافق مع بنية تطبيق الوسائط في Android.
يصف هذا الدليل المكوّنات المطلوبة لتطبيق MediaBrowserService
وMediaSession
التي يحتاجها تطبيقك لكي يعمل على Android Auto أو Android
Automotive. بعد إكمال البنية الأساسية للوسائط، يمكنك إتاحة استخدام Android Auto وإضافة دعم
لنظام التشغيل Android Automotive إلى تطبيق الوسائط.
قبل البدء
- راجِع مستندات Android media API.
- راجِع مقالة إنشاء تطبيقات وسائط للحصول على إرشادات حول التصميم.
- راجِع المصطلحات والمفاهيم الرئيسية المدرَجة في هذا القسم.
المصطلحات والمفاهيم الرئيسية
- خدمة متصفّح الوسائط
- خدمة Android ينفذها تطبيق الوسائط الخاص بك وتتوافق مع واجهة برمجة التطبيقات
MediaBrowserServiceCompat
يستخدم تطبيقك هذه الخدمة لعرض محتواه. - متصفح الوسائط
- واجهة برمجة تطبيقات تستخدمها تطبيقات الوسائط لاكتشاف خدمات متصفّحات الوسائط وعرض المحتوى. يستخدم نظاما التشغيل Android Auto وAndroid Automotive متصفّح وسائط للعثور على خدمة متصفّح الوسائط في تطبيقك.
- ملف وسائط
ينظّم متصفّح الوسائط المحتوى في شجرة لكائنات
MediaItem
. يمكن أن يحتوي عنصر الوسائط على إحدى العلامتين التاليتين أو كلتيهما:FLAG_PLAYABLE
: يشير إلى أنّ العنصر هو عنصر فرعي في شجرة المحتوى. يمثل العنصر بثًا صوتيًا واحدًا، مثل أغنية من ألبوم أو فصل في كتاب مسموع أو حلقة من بودكاست.FLAG_BROWSABLE
: يشير إلى أنّ العنصر هو عقدة في شجرة المحتوى ويضمّ عناصر فرعية. على سبيل المثال، يمثّل العنصر ألبومًا، وعناصره الثانوية هي الأغاني المضمّنة في الألبوم.
يعمل عنصر الوسائط القابل للتصفّح والتشغيل كقائمة تشغيل. يمكنك اختيار العنصر نفسه لتشغيل كل العناصر الفرعية له، أو يمكنك تصفُّح العناصر الفرعية.
- محسّنة للمركبات
نشاط لتطبيق Android Automotive يلتزم بإرشادات تصميم نظام التشغيل Android Automotive لا يرسم نظام التشغيل Android Automotive واجهة هذه الأنشطة، لذا يجب التأكّد من أنّ تطبيقك يلتزم بإرشادات التصميم. وعادةً ما يشمل ذلك أهداف أكبر حجمًا للنقر عليها وحجم خطوط أكبر، وإمكانية استخدام الوضعَين النهاري والليلي، و ratiosشديدة التباين.
لا يُسمح بعرض واجهات المستخدم المحسّنة للمركبات إلا عندما لا تكون "قيود تجربة المستخدم في المركبات" (CUXR) سارية، لأنّ هذه الواجهات قد تتطلّب تركيزًا أو تفاعلًا مطوّلاً من المستخدم. لا تكون تجربة CUXR سارية عند وقوف السيارة أو ركنها ولكنها تكون سارية دائمًا عندما تكون السيارة تتحرك.
لست بحاجة إلى تصميم أنشطة لنظام Android Auto، لأنّه يرسم واجهته المحسّنة للمركبات باستخدام المعلومات الواردة من خدمة تصفّح الوسائط.
ضبط ملفات بيان تطبيقك
قبل أن تتمكّن من إنشاء خدمة متصفّح الوسائط، عليك ضبط ملفات البيان في تطبيقك.
الإفصاح عن خدمة متصفّح الوسائط
يتصل كل من Android Auto ونظام التشغيل Android Automotive بتطبيقك من خلال خدمة متصفّح الوسائط لتصفّح عناصر الوسائط. حدِّد خدمة تصفُّح الوسائط في البيان للسماح لنظامَي التشغيل Android Auto وAndroid Automotive باكتشاف الخدمة والربط بتطبيقك.
يوضّح المقتطف التالي من الرمز البرمجي كيفية الإفصاح عن خدمة متصفّح الوسائط فيملف البيان. أدرِج هذا الرمز في ملف بيان وحدة Android Automotive OS وفي ملف بيان تطبيق الهاتف.
<application>
...
<service android:name=".MyMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
...
</application>
تحديد رموز التطبيقات
عليك تحديد رموز التطبيقات التي يمكن لنظامَي Android Auto وAndroid Automotive استخدامها لتمثيل تطبيقك في واجهة مستخدم النظام. يجب استخدام نوعَين من الرموز:
- رمز مشغّل التطبيقات
- رمز الإحالة
رمز مشغّل التطبيقات
يمثّل رمز مشغّل التطبيقات تطبيقك في واجهة مستخدم النظام، مثل مشغّل التطبيقات ولوحة الرموز. يمكنك تحديد أنّك تريد استخدام الرمز من تطبيقك المتوافق مع الأجهزة الجوّالة لتمثيل تطبيق الوسائط في السيارة باستخدام بيان البيان التالي:
<application
...
android:icon="@mipmap/ic_launcher"
...
/>
لاستخدام رمز مختلف عن رمز تطبيقك المتوافق مع الأجهزة الجوّالة، اضبط سمة android:icon
على عنصر <service>
لخدمة متصفّح الوسائط في البيان:
<application>
...
<service
...
android:icon="@mipmap/auto_launcher"
...
/>
</application>
رمز الإحالة
يتم استخدام رمز الإسناد في الأماكن التي يكون فيها محتوى الوسائط هو الأولوية، مثل بطاقات الوسائط. ننصحك بإعادة استخدام الرمز الصغير المستخدَم للإشعارات. يجب أن يكون هذا الرمز أحادي اللون. يمكنك تحديد رمز يُستخدَم لتمثيل تطبيقك باستخدام بيان البيان التالي:
<application>
...
<meta-data
android:name="androidx.car.app.TintableAttributionIcon"
android:resource="@drawable/ic_status_icon" />
...
</application>
إنشاء خدمة متصفّح الوسائط
يمكنك إنشاء خدمة تصفّح وسائط من خلال توسيع فئة MediaBrowserServiceCompat
. يمكن لكل من نظامي التشغيل Android Auto ونظام التشغيل Android Automotive الاستعانة بخدمتك لإجراء ما يلي:
- تصفَّح التسلسل الهرمي لمحتوى تطبيقك لعرض قائمة للمستخدم.
- احصل على الرمز المميّز لعنصر
MediaSessionCompat
في تطبيقك للتحكّم في تشغيل الصوت.
يمكنك أيضًا استخدام خدمة متصفّح الوسائط للسماح للعملاء الآخرين بالوصول إلى محتوى الوسائط من تطبيقك. ويمكن أن تكون عملاء الوسائط هذه تطبيقات أخرى على هاتف المستخدم، أو يمكن أن تكون عملاء آخرين عن بُعد.
سير عمل خدمة متصفِّح الوسائط
يصف هذا القسم كيفية تفاعل نظام التشغيل Android Automotive وAndroid Auto مع خدمة متصفّح الوسائط أثناء سير عمل المستخدم المعتاد.
- يشغِّل المستخدم تطبيقك على نظام التشغيل Android Automotive أو Android Auto.
- يتواصل نظام التشغيل Android Automotive أو Android Auto مع
خدمة متصفّح الوسائط في تطبيقك باستخدام الطريقة
onCreate()
. أثناء تنفيذ الطريقةonCreate()
، يجب إنشاء كائنMediaSessionCompat
وتسجيله مع كائن الاستدعاء الخاص به. - يُطلِق نظام التشغيل Android Automotive أو Android Auto طريقة
onGetRoot()
في خدمتك للحصول على عنصر الوسائط الجذر في التسلسل الهرمي للمحتوى. لا يتم عرض ملف الوسائط الجذر، ويُستخدَم بدلاً من ذلك لاسترداد المزيد من المحتوى من تطبيقك. - يُطلِق نظام التشغيل Android Automotive أو Android Auto طريقة
onLoadChildren()
في خدمتك للحصول على العناصر الفرعية لعنصر الوسائط الجذر. يعرض نظام التشغيل Android Automotive و Android Auto عناصر الوسائط هذه على أنّها المستوى الأعلى من عناصر المحتوى. اطّلِع على تنظيم القائمة الرئيسية في هذه الصفحة للحصول على مزيد من المعلومات حول ما يتوقّعه النظام على هذا المستوى. - إذا اختار المستخدم عنصر وسائط قابلاً للتصفّح، يتم استدعاء
onLoadChildren()
طريقة خدمتك مرة أخرى لاسترداد العناصر الفرعية لعنصر القائمة المحدّد. - إذا اختار المستخدم عنصر وسائط قابل للتشغيل، يطلب نظام التشغيل Android Automotive أو Android Auto طريقة معاودة الاتصال المناسبة لجلسة الوسائط لتنفيذ هذا الإجراء.
- يمكن للمستخدم أيضًا البحث في المحتوى الخاص بك إذا كان تطبيقك يتيح استخدامه. في هذه الحالة، يُطلِق نظام التشغيل Android Automotive أو Android Auto
onSearch()
طريقة خدمتك.
إنشاء تسلسل هرمي للمحتوى
يتصل Android Auto ونظام التشغيل Android Automotive بخدمة متصفّح الوسائط في تطبيقك لتحديد المحتوى المتاح. يجب تنفيذ طريقتَين في
خدمة متصفّح الوسائط لتفعيل هذه الميزة: onGetRoot()
و
onLoadChildren()
تنفيذ onGetRoot
تُعرِض طريقة onGetRoot()
في خدمتك معلومات عن العقدة الجذر للهيكل الهرمي للمحتوى.
يستخدم Android Auto ونظام التشغيل Android Automotive العقدة الأساسية هذه لطلب بقية المحتوى باستخدام الطريقة onLoadChildren()
.
يعرض مقتطف الرمز البرمجي التالي تنفيذًا بسيطًا لطريقة
onGetRoot()
:
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? = // Verify that the specified package is allowed to access your // content. You'll need to write your own logic to do this. if (!isValid(clientPackageName, clientUid)) { // If the request comes from an untrusted package, return null. // No further calls will be made to other media browsing methods. null } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)
Java
@Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { // Verify that the specified package is allowed to access your // content. You'll need to write your own logic to do this. if (!isValid(clientPackageName, clientUid)) { // If the request comes from an untrusted package, return null. // No further calls will be made to other media browsing methods. return null; } return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null); }
للحصول على مثال أكثر تفصيلاً لهذه الطريقة، اطّلِع على طريقة onGetRoot()
في نموذج تطبيق Universal Android Music Player على GitHub.
إضافة التحقق من صحة الحزمة لـ onGetRoot()
عند إجراء مكالمة إلى onGetRoot()
طريقة خدمتك، تُرسِل حزمة الاتصال معلومات التعريف إلى خدمتك. ويمكن للخدمة استخدام هذه المعلومات لتحديد ما إذا كان بإمكان هذه الحزمة الوصول إلى المحتوى أم لا. على سبيل المثال، يمكنك حصر إمكانية الوصول إلى محتوى تطبيقك بقائمة
بالحِزم الموافَق عليها من خلال مقارنة clientPackageName
بقائمة المسموح بها
والتحقق من الشهادة المستخدَمة لتوقيع حزمة APK. إذا تعذّر
إثبات ملكية الحزمة، اضغط على null
لرفض الوصول إلى المحتوى.
لإتاحة وصول تطبيقات النظام، مثل Android Auto وAndroid Automotive،
إلى المحتوى الخاص بك، يجب أن تعرض خدمتك دائمًا علامة BrowserRoot
غير فارغة
عندما تطلب تطبيقات النظام هذه الطريقة onGetRoot()
. يمكن أن يختلف توقيع تطبيق نظام التشغيل Android Automotive استنادًا
إلى طراز السيارة وعلامتها التجارية، لذا عليك السماح بعمليات الربط من جميع
تطبيقات النظام لتتوافق مع نظام التشغيل Android Automotive بشكلٍ فعّال.
يوضّح مقتطف الرمز البرمجي التالي كيفية التحقّق من أنّ حزمة الاتصال هي تطبيق نظام:
fun isKnownCaller(
callingPackage: String,
callingUid: Int
): Boolean {
...
val isCallerKnown = when {
// If the system is making the call, allow it.
callingUid == Process.SYSTEM_UID -> true
// If the app was signed by the same certificate as the platform
// itself, also allow it.
callerSignature == platformSignature -> true
// ... more cases
}
return isCallerKnown
}
مقتطف الرمز البرمجي هذا هو مقتطف من فئة PackageValidator
في نموذج تطبيق Universal Android Music Player على GitHub. راجِع تلك الفئة للاطّلاع على مثال أكثر تفصيلاً حول كيفية تنفيذ التحقق من صحة الحزمة لطريقة onGetRoot()
الخاصة بخدمتك.
بالإضافة إلى السماح بتطبيقات النظام، يجب السماح لخدمة "مساعد Google"
بالاتصال بجهاز MediaBrowserService
. يُرجى العِلم أنّ "مساعد Google" لديه
أسماء حِزم منفصلة
للهاتف، بما في ذلك Android Auto، ونظام التشغيل Android Automotive.
تنفيذ onLoadChildren()
بعد تلقّي عنصر العقدة الجذر، ينشئ نظاما التشغيل Android Auto وAndroid Automotive
قائمة على مستوى أعلى من خلال استدعاء onLoadChildren()
على عنصر العقدة الجذر للحصول على عناصره الفرعية. تُنشئ تطبيقات العميل قوائم فرعية من خلال
استدعاء هذه الطريقة نفسها باستخدام كائنات العقد الفرعية.
يتم تمثيل كل عقدة في التدرّج الهرمي للمحتوى بعنصر MediaBrowserCompat.MediaItem
. ويتم تحديد كل عنصر من عناصر الوسائط هذه من خلال سلسلة معرّف فريدة. تتعامل التطبيقات العميلة
مع سلاسل التعريف هذه كرموز مميّزة غير شفافة. عندما يريد تطبيق العميل الانتقال
إلى قائمة فرعية أو تشغيل عنصر وسائط، يُرسِل الرمز المميّز. يتحمّل تطبيقك مسؤولية
ربط الرمز المميّز بعنصر الوسائط المناسب.
يعرض مقتطف الرمز البرمجي التالي تنفيذًا بسيطًا للإجراء onLoadChildren()
:
Kotlin
override fun onLoadChildren( parentMediaId: String, result: Result<List<MediaBrowserCompat.MediaItem>> ) { // Assume for example that the music catalog is already loaded/cached. val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf() // Check whether this is the root menu: if (MY_MEDIA_ROOT_ID == parentMediaId) { // Build the MediaItem objects for the top level // and put them in the mediaItems list. } else { // Examine the passed parentMediaId to see which submenu we're at // and put the children of that menu in the mediaItems list. } result.sendResult(mediaItems) }
Java
@Override public void onLoadChildren(final String parentMediaId, final Result<List<MediaBrowserCompat.MediaItem>> result) { // Assume for example that the music catalog is already loaded/cached. List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); // Check whether this is the root menu: if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) { // Build the MediaItem objects for the top level // and put them in the mediaItems list. } else { // Examine the passed parentMediaId to see which submenu we're at // and put the children of that menu in the mediaItems list. } result.sendResult(mediaItems); }
للحصول على مثال كامل على هذه الطريقة، يُرجى الاطّلاع على onLoadChildren()
في نموذج تطبيق Universal Android Music Player على GitHub.
تنظيم القائمة الرئيسية
يفرض كل من Android Auto ونظام التشغيل Android Automotive قيودًا محدّدة على
بنية القائمة الرئيسية. ويتم إرسال هذه المعلومات إلى MediaBrowserService
من خلال تلميحات الجذر التي يمكن قراءتها من خلال الوسيطة Bundle
التي يتم تمريرها إلى
onGetRoot()
.
من خلال اتّباع هذه النصائح، يمكن للنظام عرض المحتوى الجذر بشكلٍ مثالي
كعلامات تبويب للتنقّل. في حال عدم اتّباع هذه التلميحات، قد يتم تجاهل بعض المحتوى الجذر أو يقلّل من احتمال عثور النظام عليه. يتم إرسال تلميحتَين:
- الحدّ الأقصى لعدد العناصر الفرعية للجذر: في معظم الحالات، يكون هذا العدد أربعة. وهذا يعني أنّه لا يمكن عرض أكثر من أربع علامات تبويب.
- العلامات المسموح بها على العناصر الفرعية الجذر:
يمكنك توقّع أن تكون هذه القيمة
MediaItem#FLAG_BROWSABLE
. وهذا يعني أنّه يمكن عرض علامات التبويب للعناصر القابلة للتصفّح فقط، وليس العناصر القابلة للتشغيل.
استخدِم الرمز التالي لقراءة تلميحات الجذر ذات الصلة:
Kotlin
import androidx.media.utils.MediaConstants // Later, in your MediaBrowserServiceCompat. override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle ): BrowserRoot { val maximumRootChildLimit = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, /* defaultValue= */ 4) val supportedRootChildFlags = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ MediaItem.FLAG_BROWSABLE) // Rest of method... }
Java
import androidx.media.utils.MediaConstants; // Later, in your MediaBrowserServiceCompat. @Override public BrowserRoot onGetRoot( String clientPackageName, int clientUid, Bundle rootHints) { int maximumRootChildLimit = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, /* defaultValue= */ 4); int supportedRootChildFlags = rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ MediaItem.FLAG_BROWSABLE); // Rest of method... }
يمكنك اختيار تقسيم منطق بنية العرض الهرمي للمحتوى بناءً على قيم هذه التلميحات، لا سيما إذا كان التدرّج الهرمي يختلف بين
عمليات دمج MediaBrowser
خارج Android Auto وAndroid Automotive.
على سبيل المثال، إذا كنت تعرض عادةً عنصرًا قابلاً للتشغيل في الجذر، قد تحتاج إلى تداخله
ضمن عنصر قابل للتصفّح في الجذر بدلاً من ذلك بسبب قيمة التلميح
للرموز المتوافقة.
بالإضافة إلى إشارات الجذر، هناك إرشادات إضافية يجب اتّباعها للمساعدة في ضمان عرض علامات التبويب على النحو الأمثل:
- قدِّم رموزًا أحادية اللون، ويُفضّل أن تكون بيضاء، لكل عنصر من عناصر علامة التبويب.
- أضِف تصنيفات قصيرة ومفيدة لكل عنصر من عناصر علامة التبويب. إنّ إبقاء التصنيفات قصيرة يقلل من احتمالية اقتطاع السلاسل.
عرض العمل الفني للوسائط
يجب إرسال العمل الفني لعناصر الوسائط كمعرّف موارد منتظم محلي باستخدام إما
ContentResolver.SCHEME_CONTENT
أو ContentResolver.SCHEME_ANDROID_RESOURCE
.
يجب أن يحلّ معرّف الموارد المنتظم (URI) المحلي هذا محلّ ملف رسومات نقطية أو ملف رسومات متجهة قابلَين للرسم في موارد
التطبيق. بالنسبة إلى عناصر MediaDescriptionCompat
التي تمثّل عناصر في
التسلسل الهرمي للمحتوى، يجب تمرير معرّف الموارد المنتظم (URI) من خلال setIconUri()
.
بالنسبة إلى عناصر MediaMetadataCompat
التي تمثّل المحتوى الذي يتم تشغيله حاليًا، يجب تمرير ملف تعريف الارتباط
عبر putString()
،
باستخدام أي من المفاتيح التالية:
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI
MediaMetadataCompat.METADATA_KEY_ART_URI
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI
توضّح الخطوات التالية كيفية تنزيل العمل الفني من عنوان URL للويب وعرضه
من خلال عنوان URL محلي. للحصول على مثال أكثر اكتمالاً، اطّلِع على تنفيذ openFile()
والطُرق المحيطة به في نموذج تطبيق Universal Android Music Player.
أنشئ معرّف موارد منتظم (URI)
content://
يتوافق مع معرّف الموارد المنتظم للويب. تُرسِل خدمة متصفّح الوسائط وجلسة الوسائط عنوان URL للمحتوى هذا إلى Android Auto و نظام التشغيل Android Automotive.Kotlin
fun Uri.asAlbumArtContentURI(): Uri { return Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CONTENT_PROVIDER_AUTHORITY) .appendPath(this.getPath()) // Make sure you trust the URI .build() }
Java
public static Uri asAlbumArtContentURI(Uri webUri) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_CONTENT) .authority(CONTENT_PROVIDER_AUTHORITY) .appendPath(webUri.getPath()) // Make sure you trust the URI! .build(); }
في عملية تنفيذ
ContentProvider.openFile()
، تحقّق مما إذا كان هناك ملف موجودًا لمعرّف الموارد المنتظم المقابل. إذا لم يكن الأمر كذلك، نزِّل ملف الصورة واحفظه في ذاكرة التخزين المؤقت. يستخدم مقتطف الرمز البرمجي التالي Glide.Kotlin
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { val context = this.context ?: return null val file = File(context.cacheDir, uri.path) if (!file.exists()) { val remoteUri = Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.path) .build() val cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS) cacheFile.renameTo(file) file = cacheFile } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) }
Java
@Nullable @Override public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { Context context = this.getContext(); File file = new File(context.getCacheDir(), uri.getPath()); if (!file.exists()) { Uri remoteUri = new Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.getPath()) .build(); File cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS); cacheFile.renameTo(file); file = cacheFile; } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); }
لمزيد من التفاصيل عن مقدّمي المحتوى، يُرجى الاطّلاع على مقالة إنشاء مقدّم محتوى.
تطبيق أنماط المحتوى
بعد إنشاء التسلسل الهرمي للمحتوى باستخدام عناصر قابلة للتصفّح أو التشغيل، يمكنك تطبيق أنماط المحتوى التي تحدّد كيفية عرض هذه العناصر في السيارة.
يمكنك استخدام أنماط المحتوى التالية:
- عناصر القائمة
-
يمنح نمط المحتوى هذا الأولوية للعناوين والبيانات الوصفية على الصور.
- عناصر الشبكة
-
يمنح أسلوب المحتوى هذا الأولوية للصور على العناوين والبيانات الوصفية.
ضبط أنماط المحتوى التلقائية
يمكنك ضبط قيم تلقائية عمومية لكيفية عرض عناصر الوسائط من خلال تضمين ثوابت معيّنة في حزمة BrowserRoot
الإضافية ضمن طريقة
onGetRoot()
الخاصة بخدمتك. يقرأ Android Auto ونظام التشغيل Android Automotive هذه الحِزمة ويبحثان عن
هذه الثوابت لتحديد النمط المناسب.
يمكن استخدام الميزات الإضافية التالية كمفاتيح في الحزمة:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
: يشير إلى تلميح عرض لجميع العناصر القابلة للتصفّح ضمن شجرة التصفّح.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
: تشير إلى تلميح عرض تقديمي لجميع العناصر القابلة للتشغيل داخل شجرة التصفّح.
يمكن ربط المفاتيح بالقيم الثابتة الصحيحة التالية للتأثير في عرض هذه العناصر:
-
DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
: يتم عرض العناصر المقابلة كعناصر قائمة. DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
: يتم عرض العناصر المقابلة كعناصر شبكة.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
: يتم عرض العناصر المقابلة كعناصر قائمة "category". هذه العناصر هي مثل عناصر القائمة العادية باستثناء أنّه يتم تطبيق الهوامش حول رموز العناصر، لأنّ الرموز تبدو أفضل عندما تكون صغيرة. يجب أن تكون الأيقونات قابلة للرسم متجهات قابلة للرسم. من المتوقّع أن يتم توفير هذا التلميح فقط للعناصر القابلة للتصفّح.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
: يتم عرض العناصر المقابلة كعناصر في شبكة "الفئة". هذه العناصر هي مثل عناصر الشبكة العادية، باستثناء أنّه يتم تطبيق الهوامش حول أيقونات العناصر، لأنّها تبدو أفضل عندما تكون صغيرة. يجب أن تكون الأيقونات متّجهًا قابلاً للرسم وقابلًا للتلوين. من المتوقع تقديم هذا التلميح للعناصر القابلة للتصفح فقط.
يوضّح مقتطف الرمز التالي كيفية ضبط نمط المحتوى التلقائي لأجل عرض العناصر القابلة للتصفّح في شبكات والعناصر القابلة للتشغيل في قوائم:
Kotlin
import androidx.media.utils.MediaConstants @Nullable override fun onGetRoot( @NonNull clientPackageName: String, clientUid: Int, @Nullable rootHints: Bundle ): BrowserRoot { val extras = Bundle() extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM) return BrowserRoot(ROOT_ID, extras) }
Java
import androidx.media.utils.MediaConstants; @Nullable @Override public BrowserRoot onGetRoot( @NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { Bundle extras = new Bundle(); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM); return new BrowserRoot(ROOT_ID, extras); }
تحديد أنماط المحتوى لكل عنصر
تتيح لك Content Style API إلغاء نمط المحتوى التلقائي لأي عناصر فرعية لعنصر وسائط قابل للتصفّح، بالإضافة إلى أي عنصر وسائط نفسه.
لإلغاء الإعداد التلقائي للعناصر الفرعية لعنصر وسائط قابل للتصفّح، أنشئ ملفًا برمجيًا لتحميل
ملحقات في MediaDescription
لعنصر الوسائط وأضِف
الإشارات المذكورة سابقًا. تنطبق سمة DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
على العناصر الفرعية القابلة للتشغيل من هذا العنصر، بينما تنطبق سمة
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
على العناصر الفرعية القابلة للتصفّح من هذا العنصر.
لتجاوز الإعداد التلقائي لعنصر وسائط معيّن نفسه، وليس
لعناصره الفرعية، أنشئ حِزمة إضافات في MediaDescription
لعنصر الوسائط
وأضِف تلميحًا باستخدام المفتاح
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM
.
استخدِم القيم نفسها الموضّحة سابقًا لتحديد العرض التقديمي لهذا العنصر.
يوضّح مقتطف الرمز البرمجي التالي كيفية إنشاء MediaItem
قابل للتصفّح ويتجاوز أسلوب المحتوى التلقائي لكل من العنصر نفسه والعناصر الفرعية. ويتم تنسيقه
كعنصر قائمة فئة، وعناصره الفرعية القابلة للتصفّح كعناصر قائمة، وعناصره
القابلة للتشغيل كعناصر شبكة:
Kotlin
import androidx.media.utils.MediaConstants private fun createBrowsableMediaItem( mediaId: String, folderName: String, iconUri: Uri ): MediaBrowser.MediaItem { val mediaDescriptionBuilder = MediaDescription.Builder() mediaDescriptionBuilder.setMediaId(mediaId) mediaDescriptionBuilder.setTitle(folderName) mediaDescriptionBuilder.setIconUri(iconUri) val extras = Bundle() extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM) mediaDescriptionBuilder.setExtras(extras) return MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE) }
Java
import androidx.media.utils.MediaConstants; private MediaBrowser.MediaItem createBrowsableMediaItem( String mediaId, String folderName, Uri iconUri) { MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder(); mediaDescriptionBuilder.setMediaId(mediaId); mediaDescriptionBuilder.setTitle(folderName); mediaDescriptionBuilder.setIconUri(iconUri); Bundle extras = new Bundle(); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM); mediaDescriptionBuilder.setExtras(extras); return new MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE); }
تجميع العناصر باستخدام نصائح العناوين
لتجميع عناصر الوسائط ذات الصلة معًا، استخدِم تلميحًا لكل عنصر. يجب أن يُعلن كل عنصر وسائط
في مجموعة عن حِزمة إضافات في MediaDescription
تتضمّن تعيينًا باستخدام المفتاح
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE
وقيمة سلسلة متطابقة. قم بترجمة هذه السلسلة، والتي تُستخدم
كعنوان للمجموعة.
يوضّح مقتطف الرمز التالي كيفية إنشاء MediaItem
يتضمّن عنوانًا ل subgroup
"Songs"
:
Kotlin
import androidx.media.utils.MediaConstants private fun createMediaItem( mediaId: String, folderName: String, iconUri: Uri ): MediaBrowser.MediaItem { val mediaDescriptionBuilder = MediaDescription.Builder() mediaDescriptionBuilder.setMediaId(mediaId) mediaDescriptionBuilder.setTitle(folderName) mediaDescriptionBuilder.setIconUri(iconUri) val extras = Bundle() extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") mediaDescriptionBuilder.setExtras(extras) return MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/) }
Java
import androidx.media.utils.MediaConstants; private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) { MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder(); mediaDescriptionBuilder.setMediaId(mediaId); mediaDescriptionBuilder.setTitle(folderName); mediaDescriptionBuilder.setIconUri(iconUri); Bundle extras = new Bundle(); extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs"); mediaDescriptionBuilder.setExtras(extras); return new MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/); }
يجب أن يرسل تطبيقك جميع عناصر الوسائط التي تريد تجميعها معًا في قالب متجاور. على سبيل المثال، لنفترض أنك تريد عرض مجموعتين من عناصر الوسائط، "الأغاني" و"الألبومات"، بهذا الترتيب، وأن تطبيقك ينقل إلى خمسة عناصر وسائط بالترتيب التالي:
- ملف الوسائط "أ" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- ملف الوسائط "ب" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- ملف الوسائط "ج" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- عنصر الوسائط D مع
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- ملف الوسائط "هـ" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
بما أنّ عناصر الوسائط الخاصة بمجموعة "الأغاني" ومجموعة "الألبومات" لا يتم تجميعها معًا في مجموعات متسلسلة، يفسّر نظاما Android Auto وAndroid Automotive هذا على أنّه المجموعات الأربع التالية:
- المجموعة 1 التي تحمل اسم "الأغاني" وتتضمّن ملف الوسائط "أ"
- المجموعة 2 التي تُسمى "الألبومات" وتتضمن عنصر الوسائط "ب"
- المجموعة 3 التي تحمل اسم "الأغاني" وتتضمّن ملفَي الوسائط "ج" و"د"
- المجموعة 4 المسماة "الألبومات" والتي تتضمن عنصر الوسائط E
لعرض هذه العناصر في مجموعتين، يجب أن يمرر تطبيقك عناصر الوسائط بالترتيب التالي بدلاً من ذلك:
- عنصر الوسائط "أ" مع "
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
" - ملف الوسائط "ج" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- ملف الوسائط "د" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- ملف الوسائط "ب" الذي يتضمّن
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- عنصر الوسائط E مع
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
عرض مؤشرات البيانات الوصفية الإضافية
يمكنك تضمين مؤشرات بيانات وصفية إضافية لتوفير معلومات عن المحتوى في شجرة متصفّح الوسائط وأثناء التشغيل. ضمن شجيرة التنقّل، يقرأ Android Auto ونظام التشغيل Android Automotive الإضافات المرتبطة بعنصر معيّن ويبحثان عن ثوابت معيّنة لتحديد المؤشرات التي يجب عرضها. أثناء تشغيل الوسائط، يقرأ Android Auto ونظام التشغيل Android Automotive ملف المحتوى الوصفي لجلسة الوسائط ويبحثان عن ثوابت معيّنة لتحديد المؤشرات التي سيتم عرضها.
يمكن استخدام الثوابت التالية في كلّ من MediaItem
إضافات الوصف وMediaMetadata
الإضافات:
EXTRA_DOWNLOAD_STATUS
: يشير إلى حالة تنزيل المحتوى. استخدِم هذا الثابت كأحد المفاتيح، وتكون الثوابت الطويلة التالية هي القيم المحتمَلة:STATUS_DOWNLOADED
: تم تنزيل المحتوى بالكامل.STATUS_DOWNLOADING
: جارٍ تنزيل العنصرSTATUS_NOT_DOWNLOADED
: لم يتم تنزيل العنصر.
METADATA_KEY_IS_EXPLICIT
: تشير إلى ما إذا كان العنصر يتضمّن محتوًى فاضحًا. للإشارة إلى أنّ العنصر واضح، استخدِم هذا الثابت كمفتاح والقيمة الطويلةMETADATA_VALUE_ATTRIBUTE_PRESENT
.
يمكن استخدام الثوابت التالية فقط في إضافات الوصف MediaItem
:
-
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
: يشير إلى حالة اكتمال المحتوى الطويل، مثل حلقات البودكاست أو الكتب المسموعة. استخدِم هذا الثابت كمفتاح، وتكون الأعداد الكاملة التالية الثابتة هي القيم المحتمَلة:DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
: لم يتم تشغيل العنصر على الإطلاق.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
: تم تشغيل المحتوى جزئيًا، والموقع الحالي في مكان ما في منتصف المحتوى.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
: تم إكمال العنصر.
-
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
: يشير إلى نسبة التقدّم في إكمال المحتوى الطويل كقيمة مضاعفة تتراوح بين 0.0 و1.0، شاملة. توفّر هذه الميزة الإضافية معلومات إضافية حول حالةPARTIALLY_PLAYING
لكي يعرض Android Auto أو Android Automotive مؤشر تقدّم أكثر فائدة، مثل شريط التقدّم. إذا كنت تستخدِم هذه الميزة الإضافية، اطّلِع على القسم المعنيّ بموضوع تعديل شريط التقدم في طريقة العرض "تصفّح" أثناء تشغيل المحتوى في هذا الدليل لمعرفة كيفية إبقاء هذا المؤشر محدّثًا بعد مرّة الظهور الأولى.
لعرض المؤشرات التي تظهر أثناء تصفّح المستخدِم لشجيرة تصفّح الوسائط، أنشئ حِزمة إضافات تتضمّن واحدًا أو أكثر من هذه الثوابت
وأرسِل هذه الحِزمة إلى طريقة MediaDescription.Builder.setExtras()
.
يوضّح المقتطف التالي من الرمز البرمجي كيفية عرض مؤشرات لمحتوى وسائط فاضح قد اكتمل بنسبة% 70:
Kotlin
import androidx.media.utils.MediaConstants val extras = Bundle() extras.putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED) extras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7) val description = MediaDescriptionCompat.Builder() .setMediaId(/*...*/) .setTitle(resources.getString(/*...*/)) .setExtras(extras) .build() return MediaBrowserCompat.MediaItem(description, /* flags */)
Java
import androidx.media.utils.MediaConstants; Bundle extras = new Bundle(); extras.putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT); extras.putInt( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED); extras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7); MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId(/*...*/) .setTitle(resources.getString(/*...*/)) .setExtras(extras) .build(); return new MediaBrowserCompat.MediaItem(description, /* flags */);
لعرض مؤشرات لعنصر وسائط يتم تشغيله حاليًا، يمكنك
تحديد قيم Long
لـ METADATA_KEY_IS_EXPLICIT
أو EXTRA_DOWNLOAD_STATUS
في MediaMetadataCompat
من mediaSession
. لا يمكنك عرض المؤشّرين
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
أو
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
في طريقة عرض التشغيل.
يوضّح مقتطف الرمز البرمجي التالي كيفية الإشارة إلى أنّ الأغنية الحالية في عرض التشغيل فاضحة وتم تنزيلها:
Kotlin
import androidx.media.utils.MediaConstants mediaSession.setMetadata( MediaMetadataCompat.Builder() .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name") .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name") .putString( MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString()) .putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) .putLong( MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS, MediaDescriptionCompat.STATUS_DOWNLOADED) .build())
Java
import androidx.media.utils.MediaConstants; mediaSession.setMetadata( new MediaMetadataCompat.Builder() .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name") .putString( MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name") .putString( MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString()) .putLong( MediaConstants.METADATA_KEY_IS_EXPLICIT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) .putLong( MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS, MediaDescriptionCompat.STATUS_DOWNLOADED) .build());
تعديل شريط التقدم في طريقة العرض "التصفّح" أثناء تشغيل المحتوى
كما ذكرنا سابقًا، يمكنك استخدام الرمز DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
الإضافي لعرض شريط تقدم للمحتوى الذي تم تشغيله جزئيًا في ملفه الشخصي في ميزة
عرض التصفّح. ومع ذلك، إذا واصل المستخدم تشغيل المحتوى الذي تم تشغيله جزئيًا
من Android Auto أو نظام التشغيل Android Automotive، يصبح هذا المؤشر
غير دقيق مع مرور الوقت.
بالنسبة إلى نظامَي التشغيل Android Auto وAndroid Automotive
لإبقاء شريط التقدم محدَّثًا، يمكنك توفير معلومات إضافية في
"MediaMetadataCompat
" و"PlaybackStateCompat
" لربط المحتوى الجاري بـ "عناصر الوسائط" في طريقة عرض "التصفُّح". يجب استيفاء المتطلبات التالية لعنصر الوسائط
من أجل الحصول على شريط تقدّم يتم تحديثه تلقائيًا:
- عند إنشائه، يجب أن يُرسِل
MediaItem
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
في البيانات الإضافية بقيمة تتراوح بين 0.0 و1.0، بما في ذلك. - يجب أن يُرسِل
MediaMetadataCompat
METADATA_KEY_MEDIA_ID
بقيمة سلسلة متساوية مع معرّف الوسائط المرسَل إلىMediaItem
. - يجب أن تتضمّن
PlaybackStateCompat
عنصرًا إضافيًا مع المفتاحPLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID
الذي يرتبط بقيمة سلسلة تساوي رقم تعريف الوسائط الذي تم تمريره إلىMediaItem
.
يوضّح المقتطف التالي من الرمز كيفية الإشارة إلى أنّ المحتوى الذي يتم تشغيله حاليًا مرتبط بمحتوى في طريقة العرض "التصفّح":
Kotlin
import androidx.media.utils.MediaConstants // When the MediaItem is constructed to show in the browse view. // Suppose the item was 25% complete when the user launched the browse view. val mediaItemExtras = Bundle() mediaItemExtras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25) val description = MediaDescriptionCompat.Builder() .setMediaId("my-media-id") .setExtras(mediaItemExtras) // ...and any other setters. .build() return MediaBrowserCompat.MediaItem(description, /* flags */) // Elsewhere, when the user has selected MediaItem for playback. mediaSession.setMetadata( MediaMetadataCompat.Builder() .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id") // ...and any other setters. .build()) val playbackStateExtras = Bundle() playbackStateExtras.putString( MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id") mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setExtras(playbackStateExtras) // ...and any other setters. .build())
Java
import androidx.media.utils.MediaConstants; // When the MediaItem is constructed to show in the browse view. // Suppose the item was 25% complete when the user launched the browse view. Bundle mediaItemExtras = new Bundle(); mediaItemExtras.putDouble( MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25); MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId("my-media-id") .setExtras(mediaItemExtras) // ...and any other setters. .build(); return MediaBrowserCompat.MediaItem(description, /* flags */); // Elsewhere, when the user has selected MediaItem for playback. mediaSession.setMetadata( new MediaMetadataCompat.Builder() .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id") // ...and any other setters. .build()); Bundle playbackStateExtras = new Bundle(); playbackStateExtras.putString( MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id"); mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setExtras(playbackStateExtras) // ...and any other setters. .build());
عرض نتائج البحث القابلة للتصفّح
يمكن أن يقدّم تطبيقك نتائج بحث سياقية تظهر للمستخدمين عند بدء طلب بحث. يعرض نظاما التشغيل Android Auto وAndroid Automotive هذه النتائج من خلال واجهات طلبات البحث أو من خلال ميزات تستند إلى طلبات البحث التي تم إجراؤها في وقت سابق من الجلسة. للتعرف على مزيد من المعلومات، راجع قسم الإجراءات الصوتية لدعم في هذا الدليل.
لعرض نتائج بحث قابلة للتصفّح، أدرِج المفتاح الثابت
BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED
في حِزمة الإضافات لطريقة onGetRoot()
في خدمتك، مع الربط بالقيمة المنطقية true
.
يوضّح المقتطف البرمجي التالي كيفية تفعيل الميزة في الإجراء onGetRoot()
:
Kotlin
import androidx.media.utils.MediaConstants @Nullable fun onGetRoot( @NonNull clientPackageName: String, clientUid: Int, @Nullable rootHints: Bundle ): BrowserRoot { val extras = Bundle() extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true) return BrowserRoot(ROOT_ID, extras) }
Java
import androidx.media.utils.MediaConstants; @Nullable @Override public BrowserRoot onGetRoot( @NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { Bundle extras = new Bundle(); extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true); return new BrowserRoot(ROOT_ID, extras); }
لبدء عرض نتائج البحث، يمكنك إلغاء طريقة onSearch()
في خدمة متصفّح الوسائط. يعيد نظاما التشغيل Android Auto وAndroid Automotive توجيه عبارات بحث المستخدم إلى هذه الطريقة كلما استدعى المستخدم واجهة طلب بحث أو عنصر تحكم "نتائج البحث".
يمكنك تنظيم نتائج البحث من طريقة onSearch()
الخاصة بالخدمة باستخدام عناصر العناوين لجعلها أكثر قابلية للتصفّح. على سبيل المثال، إذا كان تطبيقك يشغِّل الموسيقى، يمكنك تنظيم نتائج البحث حسب الألبوم والفنان والأغاني.
يعرض مقتطف الرمز البرمجي التالي تنفيذًا بسيطًا للإجراء onSearch()
:
Kotlin
fun onSearch(query: String, extras: Bundle) { // Detach from results to unblock the caller (if a search is expensive). result.detach() object:AsyncTask() { internal var searchResponse:ArrayList internal var succeeded = false protected fun doInBackground(vararg params:Void):Void { searchResponse = ArrayList() if (doSearch(query, extras, searchResponse)) { succeeded = true } return null } protected fun onPostExecute(param:Void) { if (succeeded) { // Sending an empty List informs the caller that there were no results. result.sendResult(searchResponse) } else { // This invokes onError() on the search callback. result.sendResult(null) } return null } }.execute() } // Populates resultsToFill with search results. Returns true on success or false on error. private fun doSearch( query: String, extras: Bundle, resultsToFill: ArrayList ): Boolean { // Implement this method. }
Java
@Override public void onSearch(final String query, final Bundle extras, Result<List<MediaItem>> result) { // Detach from results to unblock the caller (if a search is expensive). result.detach(); new AsyncTask<Void, Void, Void>() { List<MediaItem> searchResponse; boolean succeeded = false; @Override protected Void doInBackground(Void... params) { searchResponse = new ArrayList<MediaItem>(); if (doSearch(query, extras, searchResponse)) { succeeded = true; } return null; } @Override protected void onPostExecute(Void param) { if (succeeded) { // Sending an empty List informs the caller that there were no results. result.sendResult(searchResponse); } else { // This invokes onError() on the search callback. result.sendResult(null); } } }.execute() } /** Populates resultsToFill with search results. Returns true on success or false on error. */ private boolean doSearch(String query, Bundle extras, ArrayList<MediaItem> resultsToFill) { // Implement this method. }
إجراءات التصفّح المخصّصة
تتيح لك "إجراءات التصفّح المخصّصة" إضافة رموز و تصنيفات مخصّصة إلى عناصر
MediaItem
تطبيقك في تطبيق الوسائط بالسيارة، ومعالجة تفاعلات المستخدمين مع
هذه الإجراءات. ويتيح لك ذلك توسيع وظائف تطبيق الوسائط بعدة طرق، مثل إضافة الإجراءات "تنزيل" أو "الإضافة إلى قائمة الانتظار" أو "تشغيل الراديو"
أو "الإضافة إلى المفضلة" أو "إزالة".
إذا كانت هناك إجراءات مخصّصة أكثر من تلك التي يسمح المصنّع الأصلي للجهاز بعرضها، سيتم عرض قائمة تضمّ المزيد من الخيارات للمستخدم.
كيف تعمل هذه الميزة؟
يتمّ تحديد كل إجراء تصفّح مخصّص بما يلي:
- معرّف الإجراء (معرّف سلسلة فريد)
- تصنيف إجراء (النص المعروض للمستخدم)
- معرّف موارد منتظم لرمز الإجراء (متّجه قابل للرسم يمكن تلوينه)
يمكنك تحديد قائمة بإجراءات التصفّح المخصّصة على مستوى الموقع الإلكتروني كجزء من
BrowseRoot
. يمكنك بعد ذلك إرفاق مجموعة فرعية من هذه الإجراءات بمنتجات فردية.
MediaItem.
عندما يتفاعل مستخدم مع إجراء تصفّح مخصّص، يتلقّى تطبيقك طلب استدعاء
في onCustomAction()
. يمكنك بعد ذلك معالجة الإجراء وتعديل قائمة
الإجراءات الخاصة بالMediaItem
إذا لزم الأمر. ويُعدّ ذلك مفيدًا للإجراءات التي تتضمّن حالة،
مثل "مفضّل" و "تنزيل". بالنسبة إلى الإجراءات التي لا تحتاج إلى تعديل، مثل "تشغيل
الراديو"، لن تحتاج إلى تعديل قائمة الإجراءات.
ويمكنك أيضًا إرفاق إجراءات التصفّح المخصّصة بجذر عقدة التصفّح. سيتم عرض هذه الإجراءات في شريط أدوات ثانوي أسفل شريط الأدوات الرئيسي.
كيفية تنفيذ إجراءات التصفّح المخصّصة
في ما يلي خطوات إضافة إجراءات التصفّح المخصّصة إلى مشروعك:
- يمكنك إلغاء طريقتَين في عملية تنفيذ
MediaBrowserServiceCompat
: - تحليل حدود الإجراءات أثناء التشغيل:
- في
onGetRoot()
، يمكنك الحصول على أقصى عدد مسموح به من الإجراءات لكلMediaItem
باستخدام المفتاحBROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT
فيrootHints
Bundle
. يشير الحدّ الأدنى 0 إلى أنّ الميزة غير متاحة في النظام.
- في
- إنشاء قائمة عامة بإجراءات التصفّح المخصّصة:
- لكل إجراء، أنشئ عنصرًا من النوع
Bundle
باستخدام المفاتيح التالية: *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
: معرّف الإجراء *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
: تصنيف الإجراء *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
: عنوان URL لرمز الإجراء * أضِف جميع عناصرBundle
إلى قائمة.
- لكل إجراء، أنشئ عنصرًا من النوع
- إضافة القائمة العامة إلى
BrowseRoot
:- في
BrowseRoot
الإضافاتBundle
، أضِف قائمة الإجراءات على هيئةParcelable
Arraylist
باستخدام المفتاحBROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST
.
- في
- أضِف إجراءات إلى عناصر
MediaItem
:- يمكنك إضافة إجراءات إلى عناصر
MediaItem
فردية من خلال تضمين قائمة معرّفات الإجراءات في الإضافاتMediaDescriptionCompat
باستخدام المفتاحDESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST
. يجب أن تكون هذه القائمة مجموعة فرعية من القائمة الشاملة للإجراءات التي حدّدتها فيBrowseRoot
.
- يمكنك إضافة إجراءات إلى عناصر
- معالجة الإجراءات وعرض مستوى التقدّم أو النتائج:
- في
onCustomAction
، يمكنك معالجة الإجراء استنادًا إلى رقم تعريف الإجراء وأي بيانات أخرى تحتاج إليها. يمكنك الحصول على رقم تعريفMediaItem
الذي أدى إلى تنفيذ الإجراء من الإضافات باستخدام المفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID
.. - يمكنك تعديل قائمة الإجراءات الخاصة بـ
MediaItem
عن طريق تضمين المفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
في حِزمة التقدّم أو النتائج.
- في
في ما يلي بعض التغييرات التي يمكنك إجراؤها على BrowserServiceCompat
لبدء استخدام "إجراءات التصفّح المخصّصة".
إلغاء BrowserServiceCompat
عليك إلغاء الطرق التالية في MediaBrowserServiceCompat
.
public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)
public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)
الحدّ الأقصى لعدد الإجراءات التي يتمّ تحليلها
عليك التحقّق من عدد إجراءات التصفّح المخصّصة المتاحة.
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0) }
إنشاء إجراء تصفُّح مخصّص
يجب تجميع كل إجراء في Bundle
منفصل.
- الرقم التعريفي للإجراء
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, "<ACTION_ID>")
- تصنيف الإجراء
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, "<ACTION_LABEL>")
- معرّف الموارد المنتظم (URI) لرمز الإجراء
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, "<ACTION_ICON_URI>")
إضافة إجراءات تصفّح مخصّصة إلى Parceable
ArrayList
أضِف جميع عناصر Bundle
"إجراء التصفّح المخصّص" إلى ArrayList
.
private ArrayList<Bundle> createCustomActionsList( CustomBrowseAction browseActions) { ArrayList<Bundle> browseActionsBundle = new ArrayList<>(); for (CustomBrowseAction browseAction : browseActions) { Bundle action = new Bundle(); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, browseAction.mId); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, getString(browseAction.mLabelResId)); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, browseAction.mIcon); browseActionsBundle.add(action); } return browseActionsBundle; }
إضافة قائمة إجراءات التصفّح المخصّصة إلى جذر التصفّح
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { Bundle browserRootExtras = new Bundle(); browserRootExtras.putParcelableArrayList( BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST, createCustomActionsList())); mRoot = new BrowserRoot(ROOT_ID, browserRootExtras); return mRoot; }
إضافة إجراءات إلى MediaItem
MediaDescriptionCompat buildDescription (long id, String title, String subtitle, String description, Uri iconUri, Uri mediaUri, ArrayList<String> browseActionIds) { MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder(); bob.setMediaId(id); bob.setTitle(title); bob.setSubtitle(subtitle); bob.setDescription(description); bob.setIconUri(iconUri); bob.setMediaUri(mediaUri); Bundle extras = new Bundle(); extras.putStringArrayList( DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST, browseActionIds); bob.setExtras(extras); return bob.build(); } MediaItem mediaItem = new MediaItem(buildDescription(...), flags);
إنشاء نتيجة واحدة (onCustomAction
)
- تحليل mediaId من
Bundle extras
:@Override public void onCustomAction( @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){ String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID); }
- بالنسبة إلى النتائج غير المتزامنة، احرص على فصل النتيجة.
result.detach()
- إنشاء حزمة النتائج
- رسالة إلى المستخدم
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE, mContext.getString(stringRes))
- تعديل العنصر(يمكنك استخدامه لتعديل الإجراءات في عنصر)
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
- فتح طريقة عرض "التشغيل"
//Shows user the PBV without changing the playback state mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
- تعديل عقدة التصفّح
//Change current browse node to mediaId mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
- رسالة إلى المستخدم
- إذا حدث خطأ، يُرجى الاتصال على
result.sendError(resultBundle).
. - إذا كان هناك معلومات جديدة بشأن مستوى التقدّم، يُرجى الاتصال على
result.sendProgressUpdate(resultBundle)
. - يمكنك إنهاء المحادثة من خلال الاتصال بالرقم
result.sendResult(resultBundle)
.
تعديل حالة الإجراء
باستخدام طريقة result.sendProgressUpdate(resultBundle)
مع المفتاح
EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
، يمكنك تعديل MediaItem
ليعكس الحالة الجديدة للإجراء. يتيح لك ذلك تقديم ملاحظات في الوقت الفعلي للمستخدم حول مستوى التقدّم ونتيجة الإجراء الذي اتّخذه.
مثال: إجراء التنزيل
في ما يلي مثال على كيفية استخدام هذه الميزة لتنفيذ إجراء تنزيل بثلاث حالات:
- تنزيل: هذه هي الحالة الأولية للإجراء. عندما يختار المستخدم
هذا الإجراء، يمكنك استبداله بـ "جارٍ التنزيل" واستدعاء
sendProgressUpdate
لتعديل واجهة المستخدم. - يتم التنزيل: تشير هذه الحالة إلى أنّ عملية التنزيل قيد التقدّم. يمكنك استخدام هذه الحالة لعرض شريط تقدّم أو مؤشر آخر للمستخدم.
- تم تنزيله: تشير هذه الحالة إلى اكتمال عملية التنزيل. عند انتهاء
عملية التنزيل، يمكنك تبديل "التنزيل" بـ "تم التنزيل" وطلب
sendResult
باستخدام المفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
للإشارة إلى ضرورة إعادة تحميل العنصر. بالإضافة إلى ذلك، يمكنك استخدام مفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE
لعرض رسالة نجاح للمستخدم.
يتيح لك هذا النهج تقديم ملاحظات واضحة للمستخدم حول عملية التحميل وحالته الحالية. يمكنك إضافة المزيد من التفاصيل باستخدام الرموز لعرض حالات التنزيل عند اكتمال 25% أو 50% أو 75%.
مثال: الإجراء المفضّل
مثال آخر هو إجراء مفضّل يتضمّن حالتَين:
- مفضّل: يتم عرض هذا الإجراء للعناصر التي ليست في قائمة المفضّلات لدى
المستخدم. عندما يختار المستخدم هذا الإجراء، يمكنك تبديله
بـ "المفضّلة" واستدعاء
sendResult
باستخدام مفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
لتعديل واجهة المستخدم. - المفضلة: يتم عرض هذا الإجراء للعناصر الموجودة في قائمة العناصر المفضلة للمستخدم. عندما يختار المستخدم هذا الإجراء، يمكنك تبديله برمز
"العناصر المفضّلة" واستدعاء
sendResult
باستخدام مفتاحEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
لتعديل واجهة المستخدم.
يقدّم هذا الأسلوب طريقة واضحة ومتسقة للمستخدمين لإدارة العناصر المفضّلة لديهم.
توضِّح هذه الأمثلة مرونة "إجراءات التصفّح المخصّصة" وكيفية استخدامها لتنفيذ مجموعة متنوعة من الوظائف مع الحصول على ملاحظات في الوقت الفعلي لتوفير تجربة محسّنة للمستخدم في تطبيق الوسائط بالسيارة.
للاطّلاع على مثال كامل لتنفيذ هذه الميزة، يمكنك الرجوع إلى project
TestMediaApp
.
تفعيل عناصر التحكّم في التشغيل
يرسل Android Auto ونظام التشغيل Android Automotive أوامر التحكّم في التشغيل من خلال MediaSessionCompat
الخاص بخدمتك.
يجب تسجيل جلسة وتنفيذ طرق الاستدعاء المرتبطة بها.
تسجيل جلسة وسائط
في طريقة onCreate()
لخدمة متصفّح الوسائط، أنشئ MediaSessionCompat
،
ثم سجِّل جلسة الوسائط من خلال الاتصال بـ setSessionToken()
.
يوضّح المقتطف البرمجي التالي كيفية إنشاء جلسة وسائط وتسجيلها:
Kotlin
override fun onCreate() { super.onCreate() ... // Start a new MediaSession. val session = MediaSessionCompat(this, "session tag").apply { // Set a callback object that implements MediaSession.Callback // to handle play control requests. setCallback(MyMediaSessionCallback()) } sessionToken = session.sessionToken ... }
Java
public void onCreate() { super.onCreate(); ... // Start a new MediaSession. MediaSessionCompat session = new MediaSessionCompat(this, "session tag"); setSessionToken(session.getSessionToken()); // Set a callback object that implements MediaSession.Callback // to handle play control requests. session.setCallback(new MyMediaSessionCallback()); ... }
عند إنشاء عنصر جلسة الوسائط، يمكنك ضبط عنصر استدعاء يتم استخدامه
لمعالجة طلبات التحكّم في التشغيل. يمكنك إنشاء عنصر معاودة الاتصال هذا من خلال
توفير تنفيذ لفئة MediaSessionCompat.Callback
لتطبيقك. ويتناول القسم التالي كيفية تنفيذ هذا العنصر.
تنفيذ أوامر التشغيل
عندما يطلب مستخدم تشغيل عنصر وسائط من تطبيقك، يستخدم نظام التشغيل Android Automotive
وAndroid Auto فئة MediaSessionCompat.Callback
من عنصر MediaSessionCompat
في تطبيقك الذي حصلوا عليه من خدمة متصفّح الوسائط في تطبيقك. عندما يريد مستخدم
التحكّم في تشغيل المحتوى، مثل إيقاف التشغيل مؤقتًا أو التخطّي إلى الأغنية التالية، يستدعي Android Auto ونظام التشغيل Android Automotive إحدى طُرق عنصر callback.
لتشغيل المحتوى، يجب أن ينشئ تطبيقك فئة MediaSessionCompat.Callback
مجردة وأن ينفذ الطرق التي يتوافق معها.
نفِّذ جميع طرق الاستدعاء التالية المناسبة لنوع المحتوى الذي يوفّره تطبيقك:
onPrepare()
- يتم استدعاؤه عند تغيير مصدر الوسائط. يستدعي نظام التشغيل Android Automotive هذه الطريقة أيضًا بعد التشغيل مباشرةً. يجب أن ينفذ تطبيق الوسائط هذه المحاولة.
onPlay()
- يتم تشغيله إذا اختار المستخدم تشغيل المحتوى بدون اختيار عنصر معيّن. يجب أن يشغِّل تطبيقك المحتوى التلقائي، وإلا سيتم استئناف تشغيله إذا تم إيقاف تشغيله مؤقتًا باستخدام
onPause()
.ملاحظة: يجب ألّا يبدأ تطبيقك تشغيل الموسيقى تلقائيًا عند اتصال نظام التشغيل Android Automotive أو Android Auto بخدمة متصفح الوسائط. للحصول على مزيد من المعلومات، يمكنك الاطّلاع على القسم حول ضبط حالة التشغيل المبدئية.
onPlayFromMediaId()
- يتمّ تشغيله عندما يختار المستخدِم تشغيل عنصر معيّن. يتم تمرير الطريقة مع رقم التعريف الذي حدّدته خدمة متصفّح الوسائط لعنصر الوسائط في التسلسل الهرمي للمحتوى.
onPlayFromSearch()
- يتم تشغيله عندما يختار المستخدم تشغيل المحتوى من طلب بحث. يجب أن يُجري التطبيق اختيارًا مناسبًا استنادًا إلى سلسلة البحث التي تم تمريرها.
onPause()
- يتم استدعاؤه عندما يختار المستخدم إيقاف التشغيل مؤقتًا.
onSkipToNext()
- يتمّ تشغيله عندما يختار المستخدِم التخطّي إلى العنصر التالي.
onSkipToPrevious()
- يتمّ استدعاؤه عندما يختار المستخدِم التخطّي إلى العنصر السابق.
onStop()
- يتم تشغيله عندما يختار المستخدم إيقاف التشغيل.
يمكنك تجاهُل هذه الطرق في تطبيقك لإتاحة الوظائف المطلوبة. ليس عليك تنفيذ طريقة إذا لم تكن وظائفها متاحة في تطبيقك. مثلاً، إذا كان تطبيقك يشغّل بثًا مباشرًا، مثل بث رياضي، ليس عليك تنفيذ طريقة onSkipToNext()
. يمكنك استخدام التنفيذ التلقائي
لـ onSkipToNext()
بدلاً من ذلك.
لا يحتاج تطبيقك إلى أي منطق خاص لتشغيل المحتوى من خلال مكبّرات صوت السيارة. عندما يتلقّى تطبيقك طلبًا لتشغيل محتوى، يمكنه تشغيل الصوت بنفس طريقة تشغيل المحتوى من خلال مكبّرات الصوت أو سماعات الرأس في هاتف المستخدِم. يُرسِل Android Auto ونظام التشغيل Android Automotive المحتوى الصوتي تلقائيًا إلى نظام السيارة لتشغيله من خلال مكبّرات صوت السيارة.
لمزيد من المعلومات عن تشغيل المحتوى الصوتي، اطّلِع على نظرة عامة على MediaPlayer ونظرة عامة على تطبيق الصوت ونظرة عامة على ExoPlayer.
ضبط إجراءات التشغيل العادية
يعرض Android Auto ونظام التشغيل Android Automotive عناصر التحكّم في التشغيل استنادًا إلى
الإجراءات المفعّلة في عنصر PlaybackStateCompat
.
يجب أن يتيح تطبيقك الإجراءات التالية تلقائيًا:
يمكن أن يتيح تطبيقك أيضًا الإجراءات التالية إذا كانت ذات صلة بمحتوى التطبيق:
بالإضافة إلى ذلك، يتوفر لك خيار إنشاء قائمة انتظار تشغيل يمكن عرضها للمستخدم، ولكن هذا ليس مطلوبًا. لإجراء ذلك، يمكنك استدعاء الطريقتين setQueue()
وsetQueueTitle()
وتفعيل الإجراء ACTION_SKIP_TO_QUEUE_ITEM
وتحديد عملية معاودة الاتصال onSkipToQueueItem()
.
أضِف أيضًا رمز المحتوى الذي يتم تشغيله الآن، وهو مؤشر على المحتوى الذي يتم
تشغيله حاليًا. لإجراء ذلك، يمكنك استدعاء الطريقة setActiveQueueItemId()
وتمرير معرّف العنصر قيد التشغيل حاليًا في قائمة الانتظار. يجب تعديل setActiveQueueItemId()
عند حدوث تغيير في قائمة الانتظار.
يعرض Android Auto ونظام التشغيل Android Automotive أزرارًا لكل إجراء مفعَّل، بالإضافة إلى قائمة المحتوى التالي. عند النقر على الأزرار، يستدعي النظام طلب معاودة الاتصال المقابل من
MediaSessionCompat.Callback
.
حجز المساحة غير المستخدَمة
يحتجز Android Auto ونظام التشغيل Android Automotive مساحة في واجهة المستخدم لإجراءات
ACTION_SKIP_TO_PREVIOUS
وACTION_SKIP_TO_NEXT
. إذا كان تطبيقك
لا يتيح استخدام إحدى هاتين الدالتَين، يستخدم Android Auto ونظام التشغيل Android Automotive
المساحة لعرض أي إجراءات مخصّصة تنشئها.
إذا كنت لا تريد ملء هذه المساحات بإجراءات مخصّصة، يمكنك حجز
هذه المساحات لكي يترك نظاما التشغيل Android Auto وAndroid Automotive المساحة فارغة
عندما لا يتيح تطبيقك الوظيفة المقابلة. لإجراء ذلك، يمكنك استدعاء setExtras()
باستخدام حِزمة إضافات تحتوي على ثوابت تتوافق مع الدوال المُخصّصة.
يتوافق الرمز SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
مع الرمز ACTION_SKIP_TO_NEXT
، ويتوافق الرمز
SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
مع الرمز ACTION_SKIP_TO_PREVIOUS
. استخدِم هذه الثوابت كمفاتيح في
الحزمة، واستخدِم القيمة المنطقية true
لقيم هذه الثوابت.
ضبط PlaybackState الأولي
عندما يتواصل Android Auto ونظام التشغيل Android Automotive مع خدمة متصفّح الوسائط، تُرسِل جلسة الوسائط حالة تشغيل المحتوى باستخدام PlaybackStateCompat
.
يجب ألا يبدأ تطبيقك تشغيل الموسيقى تلقائيًا عند ربط نظام التشغيل Android Automotive
أو Android Auto بخدمة متصفِّح الوسائط. بدلاً من ذلك، يمكنك الاعتماد على Android
Auto ونظام التشغيل Android Automotive لإعادة تشغيل المحتوى أو بدؤه استنادًا إلى حالة
السيارة أو إجراءات المستخدم.
ولإجراء ذلك، اضبط القيمة الأولية لـ PlaybackStateCompat
لجلسة الوسائط على
STATE_STOPPED
أو
STATE_PAUSED
أو
STATE_NONE
أو
STATE_ERROR
.
لا تستمر جلسات الوسائط في Android Auto ونظام التشغيل Android Automotive إلا مدّة الرحلة، لذا يبدأ المستخدمون هذه الجلسات ويوقفونها بشكل متكرّر. ولتحسين
تجربة سلسة بين مساحات التخزين السحابي، يجب تتبُّع حالة الجلسة السابقة للمستخدم، وذلك كي عندما يتلقّى تطبيق الوسائط طلب استئناف، يمكن للمستخدم المتابعة من حيث توقف تلقائيًا، على سبيل المثال آخر عنصر وسائط تم تشغيله وPlaybackStateCompat
وقائمة الانتظار.
إضافة إجراءات تشغيل مخصّصة
يمكنك إضافة إجراءات تشغيل مخصّصة لعرض الإجراءات الإضافية التي يتيحها تطبيق الوسائط. إذا كانت المساحة تسمح بذلك (وليست محجوزة)، يضيف Android
الإجراءات المخصّصة إلى عناصر التحكّم في النقل. وإلّا، يتم عرض الإجراءات المخصّصة
في القائمة الكاملة. تظهر الإجراءات المخصّصة بترتيب إضافتها إلى PlaybackStateCompat
.
استخدِم الإجراءات المخصّصة لتقديم سلوك مختلف عن الإجراءات العادية. لا تستخدِمها لاستبدال الخطوات العادية أو تكرارها.
يمكنك إضافة إجراءات مخصّصة باستخدام الطريقة addCustomAction()
في فئة PlaybackStateCompat.Builder
.
يعرض مقتطف الرمز التالي كيفية إضافة إجراء مخصّص "بدء قناة راديو":
Kotlin
stateBuilder.addCustomAction( PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon ).run { setExtras(customActionExtras) build() } )
Java
stateBuilder.addCustomAction( new PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon) .setExtras(customActionExtras) .build());
للحصول على مثال أكثر تفصيلاً لهذه الطريقة، يمكنك الاطّلاع على الطريقة setCustomAction()
في نموذج تطبيق Universal Android Music Player على GitHub.
بعد إنشاء الإجراء المخصّص، يمكن لجلسة الوسائط الاستجابة للإجراء
من خلال إلغاء طريقة onCustomAction()
.
يعرض مقتطف الرمز التالي كيفية استجابة تطبيقك لإجراء "بدء قناة راديو":
Kotlin
override fun onCustomAction(action: String, extras: Bundle?) { when(action) { CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> { ... } } }
Java
@Override public void onCustomAction(@NonNull String action, Bundle extras) { if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) { ... } }
للحصول على مثال أكثر تفصيلاً لهذه الطريقة، اطّلِع على طريقة onCustomAction
في نموذج تطبيق Universal Android Music Player على GitHub.
رموز الإجراءات المخصّصة
يتطلب كل إجراء مخصّص تنشئه مورد رمز. يمكن تشغيل التطبيقات في السيارات على العديد من أحجام الشاشات وكثافتها المختلفة، لذا يجب أن تكون الرموز التي تقدّمها رسومًا قابلة للتحويل إلى أشكال هندسية. يتيح لك المتجه القابل للرسم تغيير حجم الأصول دون فقدان التفاصيل. يسهّل استخدام ملف قابل للدمج باستخدام متّجهات أيضًا محاذاة الحواف والأركان مع حدود وحدات البكسل عند استخدام دقة متناهية الصغر.
إذا كان الإجراء المخصّص يعتمد على الحالة، على سبيل المثال، يشغّل إعدادًا للتشغيل أو يوقفه، يجب توفير رموز مختلفة للحالات المختلفة، حتى يتمكّن المستخدمون من ملاحظة التغيير عند اختيار الإجراء.
توفير أنماط رموز بديلة للإجراءات التي تم إيقافها
عند عدم توفُّر إجراء مخصّص للسياق الحالي، يمكنك تبديل رمز الإجراء المخصّص برمز بديل يوضّح أنّ الإجراء غير مفعَّل.
الإشارة إلى تنسيق الصوت
للإشارة إلى أنّ الوسائط التي يتم تشغيلها حاليًا تستخدم تنسيقًا صوتيًا خاصًا،
يمكنك تحديد الرموز التي يتم عرضها في السيارات التي تتيح استخدام هذه الميزة. يمكنك
ضبط
KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI
و
KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI
في حِزمة الإضافات الخاصة بعنصر الوسائط الذي يتم تشغيله حاليًا (يتم تمريرها إلى
MediaSession.setMetadata()
). احرص على ضبط كلا
الإضافات، لاستيعاب التنسيقات المختلفة.
بالإضافة إلى ذلك، يمكنك ضبط الإعداد الإضافي KEY_IMMERSIVE_AUDIO
لإعلام المصنّعين الأصليّين للسيارات بأنّ هذا محتوى صوتي محيطي، ويجب أن يكونوا حذرين جدًا عند اتخاذ قرار بشأن تطبيق تأثيرات صوتية قد تتداخل مع المحتوى المحيطي.
إضافة روابط من العنصر الذي يتم تشغيله حاليًا
يمكنك ضبط عنصر الوسائط الذي يتم تشغيله حاليًا بحيث يكون العنوان الفرعي أو الوصف أو كليهما رابطًا إلى عناصر وسائط أخرى. يتيح ذلك للمستخدم الانتقال بسرعة إلى العناصر ذات الصلة، على سبيل المثال، يمكنه الانتقال إلى أغانٍ أخرى للفنان نفسه، أو حلقات أخرى من البودكاست هذا، وما إلى ذلك. إذا كانت السيارة تتيح هذه الميزة، يمكن للمستخدمين النقر على الرابط للانتقال إلى هذا المحتوى.
لإضافة روابط، عليك ضبط البيانات الوصفية
KEY_SUBTITLE_LINK_MEDIA_ID
(لإنشاء رابط من الترجمة) أو
KEY_DESCRIPTION_LINK_MEDIA_ID
(لإنشاء رابط من
الوصف). للاطّلاع على التفاصيل، يُرجى الاطّلاع على المستندات المرجعية لحقول metadata
هذه.
إتاحة الإجراءات الصوتية
يجب أن يتيح تطبيق الوسائط الإجراءات الصوتية للمساعدة في توفير تجربة آمنة ومريحة للسائقين تقلّل من مصادر التشتيت. على سبيل المثال، إذا كان تطبيقك يشغّل ملف وسائط واحدًا، يمكن للمستخدم قول"تشغيل [عنوان الأغنية]" لطلب تشغيل أغنية مختلفة من تطبيقك بدون النظر إلى شاشة السيارة أو لمسها. يمكن للمستخدمين بدء طلبات البحث من خلال النقر على الأزرار المناسبة في عجلة القيادة أو قول الكلمات الرئيسية "Ok Google".
.عندما يرصد Android Auto أو نظام التشغيل Android Automotive أحد
الإجراءات الصوتية ويفسّره، يتم إرسال هذا الإجراء الصوتي إلى التطبيق من خلال
onPlayFromSearch()
.
عند تلقّي معاودة الاتصال هذه، يعثر التطبيق على محتوى يطابق سلسلة query
ويبدأ التشغيل.
يمكن للمستخدمين تحديد فئات مختلفة من العبارات في طلب البحث، مثل النوع أو الفنان أو
الألبوم أو اسم الأغنية أو محطة الراديو أو قائمة التشغيل، وغيرها. عند إتاحة إمكانية البحث في تطبيقك، يجب مراعاة جميع الفئات المفيدة لتطبيقك.
وإذا اكتشف Android Auto أو Android Automotive أن طلب بحث معيَّن يناسب فئات معيَّنة، سيتم إلحاق الإضافات الإضافية في معلَمة extras
. يمكن إرسال المحتوى التالي:
يجب مراعاة سلسلة query
الفارغة التي يمكن إرسالها من قِبل
Android Auto أو نظام التشغيل Android Automotive إذا لم يحدّد المستخدم عبارات البحث.
على سبيل المثال، إذا قال المستخدم "تشغيل بعض الموسيقى". في هذه الحالة، قد يختار تطبيقك
بدء تشغيل مقطع صوتي تم تشغيله مؤخرًا أو تم اقتراحه حديثًا.
إذا تعذّر معالجة طلب بحث بسرعة، لا تحظر المحتوى في onPlayFromSearch()
.
بدلاً من ذلك، يمكنك ضبط حالة التشغيل على STATE_CONNECTING
وإجراء البحث على سلسلة محادثات غير متزامنة.
بعد بدء التشغيل، ننصحك بملء قائمة انتظار جلسة الوسائط بمحتوى ذي صلة. على سبيل المثال، إذا طلب المستخدم تشغيل ألبوم، قد يملأ تطبيقك قائمة الانتظار بقائمة المقاطع الصوتية للألبوم. يمكنك أيضًا تطبيق إتاحة نتائج البحث القابلة للتصفّح كي يتمكّن المستخدم من اختيار مسار مختلف يتطابق مع طلب البحث.
بالإضافة إلى طلبات بحث "play"، يتعرّف Android Auto ونظام التشغيل Android Automotive
على طلبات البحث الصوتية للتحكّم في عمليات التشغيل، مثل "إيقاف الموسيقى مؤقتًا" و"الأغنية
التالية" ومطابقة هذه الأوامر مع استدعاءات جلسة الوسائط المناسبة،
مثل onPause()
وonSkipToNext()
.
للحصول على مثال مفصّل عن كيفية تنفيذ إجراءات التشغيل المفعَّلة بالصوت في تطبيقك، يُرجى الاطّلاع على مقالة "مساعد Google" وتطبيقات الوسائط.
تنفيذ إجراءات الوقاية من تشتيت الانتباه
نظرًا لاتصال هاتف المستخدم بمكبرات الصوت في السيارة أثناء استخدام Android Auto، يجب اتخاذ احتياطات إضافية للمساعدة في منع تشتيت السائق.
إيقاف المنبّهات في السيارة
يجب ألا تبدأ تطبيقات الوسائط في Android Auto تشغيل الصوت من خلال مكبّرات صوت السيارة ما لم يبدأ المستخدم التشغيل من خلال الضغط على زر التشغيل مثلاً. حتى المنبّه الذي يضبطه المستخدم من تطبيق الوسائط يجب ألا يبدأ في تشغيل الموسيقى من خلال مكبّرات صوت السيارة.
لاستيفاء هذا الشرط، يمكن لتطبيقك استخدام CarConnection
كإشارة قبل تشغيل أي صوت. يمكن لتطبيقك التحقّق مما إذا كان الهاتف يُعرِض المحتوى على شاشة السيارة من خلال رصد LiveData
لنوع اتصال السيارة
والتحقّق مما إذا كان يساوي
CONNECTION_TYPE_PROJECTION
.
إذا كان هاتف المستخدم يعرض محتوى، يجب أن تُجري تطبيقات الوسائط المتوافقة مع المنبّهات أحد الإجراءين التاليين:
- أوقِف المنبّه.
- شغِّل المنبّه على
STREAM_ALARM
واذكر واجهة مستخدم على شاشة الهاتف لإيقاف الإنذار.
التعامل مع إعلانات الوسائط
يعرض Android Auto تلقائيًا إشعارًا عند تغيير البيانات الوصفية للوسائط
أثناء جلسة تشغيل الصوت. عندما ينتقل تطبيق وسائط من تشغيل الموسيقى
إلى عرض إعلان، يُعدّ عرض
إشعار للمستخدم أمرًا مشتتًا للانتباه. لمنع ميزة Android Auto من عرض إشعار في هذه الحالة، يجب ضبط مفتاح البيانات الوصفية للوسائط
METADATA_KEY_IS_ADVERTISEMENT
على
METADATA_VALUE_ATTRIBUTE_PRESENT
،
كما هو موضّح في مقتطف الرمز التالي:
Kotlin
import androidx.media.utils.MediaConstants override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) { MediaMetadataCompat.Builder().apply { if (isAd(mediaId)) { putLong( MediaConstants.METADATA_KEY_IS_ADVERTISEMENT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT) } // ...add any other properties you normally would. mediaSession.setMetadata(build()) } }
Java
import androidx.media.utils.MediaConstants; @Override public void onPlayFromMediaId(String mediaId, Bundle extras) { MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(); if (isAd(mediaId)) { builder.putLong( MediaConstants.METADATA_KEY_IS_ADVERTISEMENT, MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT); } // ...add any other properties you normally would. mediaSession.setMetadata(builder.build()); }
التعامل مع الأخطاء العامة
عندما يواجه التطبيق خطأ، اضبط حالة التشغيل على STATE_ERROR
وقدِّم رسالة خطأ باستخدام الطريقة setErrorMessage()
. اطّلِع على PlaybackStateCompat
للحصول على قائمة برموز الخطأ التي يمكنك استخدامها عند ضبط رسالة الخطأ.
يجب أن تكون رسائل الخطأ موجَّهة للمستخدمين ومترجمة باللغة الحالية. بعد ذلك، يمكن أن تعرض ميزة Android Auto وAndroid Automotive رسالة الخطأ للمستخدم.
على سبيل المثال، إذا لم يكن المحتوى متاحًا في المنطقة الحالية للمستخدم، يمكنك
استخدام رمز الخطأ ERROR_CODE_NOT_AVAILABLE_IN_REGION
عند ضبط رسالة الخطأ.
Kotlin
mediaSession.setPlaybackState( PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_ERROR) .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region)) // ...and any other setters. .build())
Java
mediaSession.setPlaybackState( new PlaybackStateCompat.Builder() .setState(PlaybackStateCompat.STATE_ERROR) .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region)) // ...and any other setters. .build());
لمزيد من المعلومات عن حالات الخطأ، يُرجى الاطّلاع على مقالة استخدام جلسة وسائط: الحالات والأخطاء.
إذا كان مستخدم Android Auto بحاجة إلى فتح تطبيق هاتفك لحلّ خطأ، قدِّم هذه المعلومات للمستخدم في رسالتك. على سبيل المثال، قد تظهر رسالة الخطأ التالية: "تسجيل الدخول إلى [اسم تطبيقك]" بدلاً من "يُرجى تسجيل الدخول".