Android Auto ו-Android Automotive OS עוזרים להעביר את התוכן של אפליקציית המדיה אל המשתמשים במכונית שלהם. אפליקציית מדיה למכוניות צריכה לספק שירות של דפדפן מדיה כך ש-Android Auto ו-Android Automotive OS, או אפליקציה אחרת עם מדיה בדפדפן, יכולים לגלות ולהציג את התוכן שלך.
במדריך הזה יוצאים מנקודת הנחה שכבר יש לך אפליקציית מדיה שמפעילה אודיו הטלפון ושאפליקציית המדיה שלך תואמת לאפליקציית המדיה ל-Android. של הארכיטקטורה.
במדריך הזה מתוארים הרכיבים הנדרשים של MediaBrowserService
MediaSession
שהאפליקציה צריכה כדי לפעול ב-Android Auto או ב-Android
Automotive OS. אחרי שתשלימו את תשתית הליבה של המדיה, תוכלו
הוספת תמיכה ב-Android Auto והוספת תמיכה עבור
Android Automotive OS למדיה
אפליקציה.
לפני שמתחילים
- כדאי לעיין במסמכי התיעוד של Android Media API.
- מעיינים במאמר יצירת אפליקציות מדיה לקבלת הנחיות לעיצוב.
- קוראים את המונחים והמושגים המרכזיים שמפורטים בקטע הזה.
מונחי מפתח ומושגים מרכזיים
- שירות של דפדפן מדיה
- שירות Android שהוטמע על ידי אפליקציית המדיה שלך עומד בדרישות של
MediaBrowserServiceCompat
API. האפליקציה שלך משתמשת בשירות הזה כדי לחשוף את התוכן שלה. - דפדפן המדיה
- ממשק API המשמש אפליקציות מדיה לגילוי שירותים ותצוגה של דפדפן מדיה את התוכן שלהם. Android Auto ו-Android Automotive OS משתמשים בדפדפן מדיה כדי למצוא את שירות דפדפן המדיה של האפליקציה.
- פריט מדיה
התוכן בדפדפן מאורגן בעץ של
MediaItem
אובייקטים. פריט מדיה יכול לכלול אחד מהסימונים הבאים, או את שניהם:FLAG_PLAYABLE
: מציין שהפריט הוא עלה בעץ התוכן. הפריט מייצג זרם אודיו אחד, כמו שיר באלבום, פרק בספר אודיו או פרק של פודקאסט.FLAG_BROWSABLE
: מציין שהפריט הוא צומת בעץ התוכן יש ילדים. לדוגמה, פריט מייצג אלבום, והצאצאים שלו את השירים שבאלבום.
פריט מדיה שניתן לעיין בו וגם להפעיל אותו, פועל כמו פלייליסט. אפשר בחר את הפריט עצמו כדי להפעיל את כל הצאצאים שלו, או שתוכל לעיין לילדים.
- מותאם לרכב
פעילות של אפליקציה ל-Android Automotive OS שעומדת בדרישות של הנחיות עיצוב ל-Android Automotive OS הממשק של הפעילויות האלה לא מצויר על ידי Android Automotive OS, כך חייבת להבטיח שהאפליקציה פועלת בהתאם להנחיות העיצוב. בדרך כלל, שכולל יעדי הקשה גדולים יותר וגופנים גדולים יותר, תמיכה במצבי יום ולילה וגם ביחסי ניגודיות גבוהים יותר.
אפשר להציג ממשקי משתמש שמותאמים לרכב רק במצב מכונית ההגבלות על חוויית המשתמש (CUXR) לא בתוקף כי ממשקים עשויים לדרוש תשומת לב נרחבת או אינטראקציה מצד המשתמשים. ספקי CUXR לא יהיו בתוקף כשהמכונית בעצירה או בחנייה, אבל תמיד פעילה כשהמכונית בתנועה.
אין צורך לעצב פעילויות עבור Android Auto, כי Android Auto שכותבת ממשק משלה שעבר אופטימיזציה לרכב באמצעות מידע שירות של דפדפן מדיה.
הגדרת קובצי המניפסט של האפליקציה
לפני שתוכל ליצור את שירות דפדפן המדיה, עליך להגדיר את את קובצי המניפסט של האפליקציה.
הצהרה על שירות דפדפן המדיה
גם Android Auto וגם Android Automotive OS מתחברים לאפליקציה דרך שירות דפדפן מדיה כדי לעיין בקובצי מדיה. להצהיר על זכויות יוצרים במדיה שירות דפדפן במניפסט כדי לאפשר ל-Android Auto ול-Android Automotive OS מגלים את השירות ומתחברים לאפליקציה.
קטע הקוד הבא מראה איך להצהיר (declare) על שירות דפדפן המדיה את המניפסט. צריך לכלול את הקוד הזה בקובץ המניפסט של המודול של 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 OS שמשמש לייצוג האפליקציה שלך בממשק המשתמש של המערכת. נדרשים שני סוגי סמלים:
- סמל מרכז האפליקציות
- סמל השיוך
סמל מרכז האפליקציות
סמל מרכז האפליקציות מייצג את האפליקציה בממשק המשתמש של המערכת, למשל במרכז האפליקציות ובמגש הסמלים. תוכל לציין שברצונך להשתמש בסמל מ- האפליקציה שלך לנייד כדי לייצג את אפליקציית המדיה לרכב באמצעות המניפסט הבא הצהרה:
<application
...
android:icon="@mipmap/ic_launcher"
...
/>
כדי להשתמש בסמל ששונה מהסמל של האפליקציה לנייד, צריך להגדיר את המאפיין android:icon
ברכיב <service>
של שירות דפדפן המדיה במניפסט:
<application>
...
<service
...
android:icon="@mipmap/auto_launcher"
...
/>
</application>
סמל השיוך
![](https://developer.android.com/static/images/training/cars/attribution-icon.png?authuser=9&hl=he)
איור 1. סמל השיוך בכרטיס המדיה.
סמל השיוך מופיע במקומות שבהם תוכן המדיה מקבל עדיפות, למשל בכרטיסי מדיה. כדאי להשתמש שוב בסמל הקטן המשמש להתראות. הסמל הזה חייב להיות מונוכרומטי. אפשר לציין סמל לייצוג באמצעות הצהרת המניפסט הבאה:
<application>
...
<meta-data
android:name="androidx.car.app.TintableAttributionIcon"
android:resource="@drawable/ic_status_icon" />
...
</application>
יצירת שירות של דפדפן מדיה
אתם יוצרים שירות של דפדפן מדיה על ידי הרחבת הקטע MediaBrowserServiceCompat
בכיתה. לאחר מכן, מערכת Android Auto ו-Android Automotive OS יוכלו להשתמש בשירות שלך
לבצע את הפעולות הבאות:
- צריך לעיין בהיררכיית התוכן של האפליקציה כדי להציג תפריט למשתמש.
- קבלת האסימון ל-
MediaSessionCompat
של האפליקציה שלך כדי לשלוט בהפעלת האודיו.
תוכלו גם להשתמש בשירות דפדפן המדיה כדי לאפשר ללקוחות אחרים גישה לתוכן מדיה מהאפליקציה. לקוחות המדיה האלה עשויים להיות אפליקציות אחרות הטלפון של המשתמש, או שהם יכולים להיות לקוחות מרוחקים אחרים.
תהליך עבודה של שירות דפדפן מדיה
בקטע זה מתואר האופן שבו Android Automotive OS ו-Android אינטראקציה אוטומטית עם שירות דפדפן המדיה במהלך תהליך עבודה של משתמש אופייני.
- המשתמש מפעיל את האפליקציה ב-Android Automotive OS או ב-Android Auto.
- Android Automotive OS או Android Auto יוצרים קשר עם דפדפן המדיה באפליקציה
השירות באמצעות
onCreate()
. בהטמעה שלonCreate()
צריך ליצור ולרשוםMediaSessionCompat
ואת אובייקט הקריאה החוזרת שלו. - תתבצע שיחה מ-Android Automotive OS או מ-Android Auto ל-
onGetRoot()
של השירות כדי לקבל את פריט המדיה הבסיסי בהיררכיית התוכן. פריט המדיה הבסיסי לא מוצגת, במקום זאת, הוא משמש לאחזור תוכן נוסף מהאפליקציה. - Android Automotive OS או Android Auto יתקשר לשירות
onLoadChildren()
כדי לקבל את הצאצאים של פריט המדיה הבסיסי. Android Automotive OS וגם פריטי המדיה האלה מוצגים ב-Android Auto כפריטי תוכן ברמה העליונה. צפייה לשינוי המבנה של תפריט הבסיס בדף הזה מידע על הציפיות של המערכת ברמה הזו. - אם המשתמש בוחר בפריט מדיה שניתן לעיין בו, המאפיין של השירות שלך
onLoadChildren()
תופעל שוב כדי לאחזר את הצאצאים של האפשרות שנבחרה בתפריט. - אם המשתמש בחר פריט מדיה שאפשר להפעיל, Android Automotive OS או Android קוראת אוטומטית לשיטת הקריאה החוזרת (callback) המתאימה של סשן המדיה כדי לבצע את הפעולה הזו.
- אם האפליקציה נתמכת, המשתמש יכול גם לחפש בתוכן שלכם. כאן
כיסוי, מתקשרים ל-Android Automotive OS או ל-Android Auto
onSearch()
.
יצירה של היררכיית תוכן
Android Auto ו-Android Automotive OS יפעילו את שירות דפדפן המדיה של האפליקציה אל
לבדוק איזה תוכן זמין. צריך ליישם שתי שיטות
שירות של דפדפן מדיה שתומך בכך: onGetRoot()
וגם
onLoadChildren()
יישום ב-GetRoot
onGetRoot()
של השירות
מחזירה מידע על הצומת הבסיסי (root) של היררכיית התוכן.
Android Auto ו-Android Automotive OS משתמשים בצומת הבסיס הזה כדי לבקש את שאר
בתוכן שלך באמצעות
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()
של השירות
method, חבילת הקריאה מעבירה פרטים מזהים לשירות שלכם. שלך
השירות יכול להשתמש במידע הזה כדי להחליט אם החבילה יכולה לגשת
תוכן. לדוגמה, אפשר להגביל את הגישה לתוכן של האפליקציה לרשימה של
חבילות שאושרו על ידי השוואה בין clientPackageName
לרשימת ההיתרים שלך
שמאמת את האישור ששימש לחתימה על ה-APK של החבילה. אם החבילה לא יכולה
אימות, החזרת null
כדי לדחות את הגישה לתוכן שלך.
כדי לספק אפליקציות מערכת, כמו Android Auto ו-Android Automotive OS,
עם גישה לתוכן שלך, השירות חייב תמיד להחזיר ערך שאינו null
BrowserRoot
כשאפליקציות המערכת האלה קוראות ל-onGetRoot()
. החתימה של אפליקציית המערכת של Android Automotive OS עשויה להשתנות בהתאם
היצרן והדגם של הרכב, כך שצריך לאפשר את החיבורים מכל
אפליקציות מערכת שתומכות ב-Android Automotive OS באופן יציב.
קטע הקוד הבא מראה איך השירות יכול לאמת חבילת השיחות היא אפליקציית מערכת:
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 Assistant
להתחבר אל MediaBrowserService
. שימו לב ש-Google Assistant כוללת
שמות נפרדים של חבילות
בטלפון, כולל Android Auto, וב-Android Automotive OS.
הטמעה של onLoadChildren()
אחרי שמקבלים את אובייקט הצומת של הרמה הבסיסית (root), מערכת Android Auto ו-Android Automotive OS
כדי ליצור תפריט ברמה עליונה באמצעות קריאה ל-onLoadChildren()
באובייקט הרמה הבסיסית (root) כדי לקבל את הצאצאים שלו. אפליקציות לקוח יוצרות תפריטי משנה לפי
קריאה לאותה שיטה באמצעות אובייקטים של צומת צאצא.
כל צומת בהיררכיית התוכן מיוצג על ידי MediaBrowserCompat.MediaItem
לאובייקט. כל אחד מפריטי המדיה האלה מזוהה באמצעות מחרוזת מזהה ייחודית. לקוח/ה
אפליקציות מתייחסים למחרוזות המזהים האלה כאל אסימונים אטומים. כשאפליקציית לקוח רוצה לגלוש
אל תפריט משנה או להפעיל פריט מדיה, הוא מעביר את האסימון. האפליקציה שלך אחראית
לצורך שיוך האסימון לפריט המדיה המתאים.
קטע הקוד הבא מציג הטמעה פשוטה של onLoadChildren()
method:
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.
קביעת המבנה של תפריט הבסיס
![](https://developer.android.com/static/images/training/cars/browse_home_tabs.png?authuser=9&hl=he)
איור 2. תוכן הבסיס מוצג ככרטיסיות ניווט.
ל-Android Auto ול-Android Automotive OS יש מגבלות ספציפיות לגבי
של תפריט הבסיס. הפרטים האלה מועברים אל MediaBrowserService
באמצעות רמזים בסיסיים (root), שניתן לקרוא אותם באמצעות הארגומנט Bundle
שמועבר
onGetRoot()
.
מעקב אחר הרמזים האלה מאפשר למערכת להציג את תוכן הבסיס באופן אופטימלי.
ככרטיסיות ניווט. אם לא פועלים לפי הרמזים האלה, חלק מהתוכן הבסיסי (root) עלול
או שהן יהיו פחות גלויות למערכת. נשלחים שני רמזים:
- הגבלה על מספר הצאצאים ברמת השורש: ברוב המקרים אפשר לצפות שהמספר יהיה ארבע. המשמעות היא לא ניתן להציג יותר מארבע כרטיסיות.
- דגלים נתמכים צאצאים בסיסיים:
אפשר לצפות שהערך הזה
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 OS.
לדוגמה, אם בדרך כלל מציגים פריט ברמה הבסיסית שניתן להפעיל, כדאי להציב אותו בתוך הטבלה
מתחת לפריט ברמה הבסיסית שניתן לעיין בו, עקב הערך של הדגלים הנתמכים
רמז.
חוץ מרמזים בסיסיים, יש עוד כמה הנחיות שצריך לפעול לפיהן כדי להבטיח שהכרטיסיות יעובדו בצורה אופטימלית:
- יש לספק סמלים מונוכרומטיים, רצוי לבן, עבור כל פריט כרטיסייה.
- כדאי להוסיף תוויות קצרות אבל בעלות משמעות לכל פריט כרטיסייה. שמירה על תוויות קצרות מפחית את הסיכוי לחיתוך המחרוזות.
הצגת גרפיקה של מדיה
יש להעביר גרפיקה של פריטי מדיה כ-URI מקומי באמצעות
ContentResolver.SCHEME_CONTENT
או ContentResolver.SCHEME_ANDROID_RESOURCE
.
ה-URI המקומי הזה חייב להתאים למפת סיביות (bitmap) או לשרטוט וקטורי
במשאבים של האפליקציה. בשביל MediaDescriptionCompat
אובייקטים שמייצגים פריטים ב-
בהיררכיית התוכן, מעבירים את ה-URI דרך setIconUri()
.
עבור MediaMetadataCompat
אובייקטים שמייצגים את הפריט שמופעל כרגע, מעבירים את הפקודה
URI דרך putString()
,
באמצעות אחד מהמפתחות הבאים:
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI
MediaMetadataCompat.METADATA_KEY_ART_URI
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI
בשלבים הבאים מוסבר איך להוריד אומנות מ-URI באינטרנט ולחשוף
באמצעות URI מקומי. כדי לקבל דוגמה מלאה יותר, אפשר לעיין
הטמעה
של openFile()
והשיטות שמסביב ב-Universal Android Music
אפליקציה לדוגמה של הנגן.
יוצרים URI של
content://
שתואם ל-URI של האינטרנט. דפדפן המדיה סשן של שירות ומדיה מעביר את ה-URI של התוכן הזה ל-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()
, צריך לבדוק אם קובץ קיים עבור ה-URI המתאים. אם לא, מורידים את קובץ התמונה ושומרים אותו במטמון. בקטע הקוד הבא נעשה שימוש ב-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 OS קוראים את החבילה הזו ומחפשים
את הקבועים האלה כדי לקבוע את הסגנון המתאים.
ניתן להשתמש בתוספות הבאות כמפתחות בחבילה:
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
: הפריטים המתאימים מוצגים בתור 'category'. פריטים ברשת. הנושאים האלה זהות לפריטי רשת רגילים, למעט שהשוליים מוחלים הפריטים כי הסמלים נראים טוב יותר כשהם קטנים. הסמלים חייב להיות פריטים ניתנים להזזה בווקטורים. יש לספק את הרמז הזה רק לפריטים שניתן לעיין בהם.
קטע הקוד הבא מראה איך להגדיר את סגנון ברירת המחדל של התוכן פריטים שניתנים לעיון במשבצות ופריטים שניתנים להפעלה לרשימות:
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
עם קבוצת משנה
הכותרת של "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")
- פריט מדיה ד' עם
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- פריט מדיה E עם
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
בגלל שהפריטי המדיה של ה'שירים' קבוצה ו'אלבומים' קבוצות לא נשמרות יחד בבלוקים רציפים, Android Auto ו-Android Automotive OS מפרש זאת כארבע הקבוצות הבאות:
- קבוצה 1 שנקראת "שירים" מכיל את פריט המדיה A
- קבוצה 2 בשם "אלבומים" מכיל את פריט המדיה B
- קבוצה 3 שנקראת 'שירים' שמכיל את פריטי המדיה C ו-D
- קבוצה 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 OS קוראות את התוספות המשויכות עם פריט מסוים ולחפש קבועים מסוימים כדי לקבוע אילו אינדיקטורים מסך. במהלך הפעלה של מדיה, Android Auto ו-Android Automotive OS קוראים את מטא-נתונים עבור סשן המדיה ומחפשים קבועים מסוימים כדי לקבוע אינדיקטורים להצגה.
![](https://developer.android.com/static/images/training/cars/metadata_indicators.png?authuser=9&hl=he)
איור 3. תצוגת הפעלה עם מטא-נתונים שמזהים את השיר ואומן וגם סמל שמציין תוכן בוטה.
![](https://developer.android.com/static/images/training/cars/progress_indicators.png?authuser=9&hl=he)
איור 4. תצוגת דפדוף עם נקודה עבור תוכן שלא הופעל ב- הפריט הראשון וסרגל התקדמות לתוכן שהופעל חלקית הפריט השני.
ניתן להשתמש בקבועים הבאים גם בתוספות תיאור של 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 OS מוצג אינדיקטור התקדמות משמעותי יותר, כמו סרגל התקדמות. אם משתמשים בתוספת הזו, עיינו בקטע שעוסק עדכון סרגל ההתקדמות בתצוגת הגלישה בזמן שהתוכן מופעל במדריך הזה תלמדו איך לשמור על העדכניות של האינדיקטור לאחר לחשיפה ראשונית.
להציג אינדיקטורים שמופיעים בזמן שהמשתמש מדפדפים בין המדיה
עץ, ליצור חבילת תוספות שכוללת אחד או יותר מהקבועים האלה
תעביר את החבילה ל-method 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 OS, האינדיקטור הזה הופך
לא מדויק במשך הזמן.
ל-Android Auto ול-Android Automotive OS
כדי שסרגל ההתקדמות מעודכן, אפשר לספק מידע נוסף
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());
הצגת תוצאות חיפוש שניתן לדפדף
![](https://developer.android.com/static/images/training/cars/search_results.png?authuser=9&hl=he)
איור 5. תצוגת הפעלה עם אפשרות 'תוצאות חיפוש' עבור הצגת פריטי מדיה שקשורים לחיפוש הקולי של המשתמש.
האפליקציה יכולה לספק תוצאות חיפוש לפי הקשר שמוצגות למשתמשים כאשר הם מתחילים שאילתת חיפוש. התוכניות Android Auto ו-Android Automotive OS את התוצאות האלה דרך ממשקי שאילתות חיפוש או דרך הצעות משתלמות בתגובה לשאילתות שנשלחו מוקדם יותר בסשן. מידע נוסף זמין במאמר הבא: הקטע תמיכה בפעולות קוליות במדריך הזה.
כדי להציג תוצאות חיפוש שניתן לדפדף ביניהן, יש לכלול את המקש הקבוע
BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED
בחבילת התוספות של onGetRoot()
השירות
מתבצע מיפוי ל-true
הבוליאני.
בקטע הקוד הבא מוסבר איך להפעיל תמיכה ב-onGetRoot()
method:
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 OS
להעביר את מונחי החיפוש של המשתמש לשיטה הזו בכל פעם שמשתמש מפעיל חיפוש
ממשק שאילתות או במחיר של 'תוצאות חיפוש'.
אפשר לארגן את החיפוש
תוצאות משיטת onSearch()
של השירות באמצעות פריטי שם פריט
כדי שיהיה קל יותר לעיין בהם. לדוגמה, אם האפליקציה מפעילה מוזיקה, ייתכן
לארגן את תוצאות החיפוש לפי אלבומים, אומנים ושירים.
קטע הקוד הבא מציג הטמעה פשוטה של onSearch()
method:
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. }
פעולות עיון בהתאמה אישית
![פעולת דפדוף אחת בהתאמה אישית.](https://developer.android.com/static/images/training/cars/custom_browse_action_single.png?authuser=9&hl=he)
איור 6. פעולת גלישה יחידה בהתאמה אישית
פעולות עיון מותאמות אישית מאפשרות להוסיף תוויות וסמלים מותאמים אישית לאפליקציה
MediaItem
אובייקטים באפליקציית המדיה של הרכב ומטפלים באינטראקציות של משתמשים עם
את הפעולות האלה. כך תוכלו להרחיב את הפונקציונליות של אפליקציית המדיה
דרכים שונות, כמו הוספת "הורדה", "הוספה לתור", "Play Radio",
'מועדפים' או 'הסרה' פעולות.
![אפשרויות נוספות של פעולות עיון בהתאמה אישית.](https://developer.android.com/static/images/training/cars/custom_browse_action_overflow.png?authuser=9&hl=he)
איור 7. אפשרויות נוספות של פעולות גלישה בהתאמה אישית
אם יש יותר פעולות מותאמות אישית ממה שיצרן ה-OEM מאפשר להציג, יוצג למשתמש תפריט אפשרויות נוספות.
איך הם פועלים?
כל פעולת גלישה בהתאמה אישית מוגדרת עם:
- מזהה פעולה (מזהה מחרוזת ייחודי)
- תווית פעולה (הטקסט שמוצג למשתמש)
- URI של סמל פעולה (קובץ וקטורי שניתן להזזה עם גוון)
אתם מגדירים באופן גלובלי רשימה של פעולות גלישה מותאמות אישית כחלק
BrowseRoot
לאחר מכן תוכלו לצרף קבוצת משנה של הפעולות האלה לכל
MediaItem.
כשמשתמש מקיים אינטראקציה עם פעולת עיון בהתאמה אישית, האפליקציה מקבלת קריאה חוזרת (callback)
ב-onCustomAction()
. לאחר מכן תוכלו לבצע את הפעולה ולעדכן את הרשימה
פעולות עבור MediaItem
במידת הצורך. האפשרות הזו שימושית לפעולות עם שמירת מצב
כמו "מועדפים" ו'הורדה'. לפעולות שאין צורך לעדכן, כמו "הפעלה
רדיו", לא צריך לעדכן את רשימת הפעולות.
![פעולות גלישה בהתאמה אישית ברמה הבסיסית (root) של צומת הגלישה.](https://developer.android.com/static/images/training/cars/custom_browse_action_toolbar.png?authuser=9&hl=he)
איור 8. סרגל הכלים של פעולת הגלישה בהתאמה אישית
אפשר גם לצרף פעולות גלישה בהתאמה אישית לרמה הבסיסית (root) של צומת הגלישה. הפעולות האלה יוצגו בסרגל הכלים המשני מתחת לסרגל הכלים הראשי.
איך מטמיעים פעולות גלישה בהתאמה אישית
כדי להוסיף לפרויקט פעולות עיון בהתאמה אישית:
- לשנות שתי שיטות ב
MediaBrowserServiceCompat
הטמעה: - ניתוח מגבלות הפעולות בזמן הריצה:
- ב
onGetRoot()
, קבלת מספר הפעולות המקסימלי המותר בכל פעםMediaItem
באמצעות המפתחBROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT
בBundle
שלrootHints
. מגבלה של 0 מציינת שהתכונה לא נתמכים על ידי המערכת.
- ב
- יצירת הרשימה הגלובלית של פעולות גלישה בהתאמה אישית:
- לכל פעולה, יוצרים אובייקט
Bundle
עם המפתחות הבאים: *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
: מזהה הפעולה *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
: תווית הפעולה *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
: ה-URI של סמל הפעולה * מוסיפים לרשימה את כל האובייקטים של הפעולה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; }
הוספת הרשימה של פעולות הגלישה בהתאמה אישית לרמה הבסיסית (root) של הדפדוף
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()
- חבילת תוצאות build
- הודעה למשתמש
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)
.
עדכון מצב הפעולה
משתמשים ב-method 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
כדי לעדכן את ממשק המשתמש.
הגישה הזו מספקת למשתמשים דרך ברורה ועקבית לנהל את פריטים מועדפים.
הדוגמאות האלה ממחישות את הגמישות של פעולות הגלישה בהתאמה אישית ואיך אפשר ולהשתמש בהם כדי ליישם מגוון פונקציות עם משוב בזמן אמת לחוויית משתמש משופרת באפליקציית המדיה ברכב.
כדי לקבל דוגמה מלאה להטמעה של התכונה הזו, אפשר לעיין
TestMediaApp
פרויקט.
הפעלה של בקרת ההפעלה
Android Auto ו-Android Automotive OS שולחים פקודות לבקרת הפעלה באמצעות
MediaSessionCompat
של השירות.
עליכם להירשם לסשן ולהטמיע את השיטות המשויכות לקריאה חוזרת (callback).
רישום סשן מדיה
ב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
לכיתה של האפליקציה. בקטע הבא מוסבר איך ליישם את האובייקט הזה.
הטמעת פקודות Play
כשמשתמש מבקש להפעיל פריט מדיה מהאפליקציה שלכם, Android Automotive
מערכת ההפעלה ו-Android Auto משתמשות בMediaSessionCompat.Callback
כיתה מה-MediaSessionCompat
של האפליקציה שלך
שהוא קיבל משירות דפדפן המדיה של האפליקציה. כשמשתמש
רוצה לשלוט בהפעלת תוכן, כמו להשהות את ההפעלה או לדלג אל
הטראק הבא, Android Auto ו-Android Automotive OS מפעילים את אחד הקריאה החוזרת
ה-methods של האובייקט.
כדי לטפל בהפעלת תוכן, האפליקציה שלך חייבת להרחיב את המופשט MediaSessionCompat.Callback
ולהטמיע את השיטות שהאפליקציה תומכת בהן.
להטמיע את כל השיטות הבאות להתקשרות חזרה, שמתאימות סוג התוכן שהאפליקציה מציעה:
onPrepare()
- מופעלת כשמקור המדיה משתנה. מערכת Android Automotive OS מפעילה גם שיטה זו מיד לאחר האתחול. צריך להטמיע את המזהה הזה באפליקציית המדיה .
onPlay()
- מופעלת אם המשתמש בוחר להפעיל בלי לבחור פריט ספציפי. שלך
חייב להפעיל את תוכן ברירת המחדל שלו, או אם ההפעלה הושהתה באמצעות
onPause()
, האפליקציה ממשיכה את ההפעלה.הערה: האפליקציה לא אמורה להתחיל להשמיע מוזיקה באופן אוטומטי כש-Android Automotive OS או Android Auto מתחברים לדפדפן המדיה לאחר השיפור. למידע נוסף, עיינו בקטע שעוסק הגדרת מצב ההפעלה הראשונית.
onPlayFromMediaId()
- מופעלת כשהמשתמש בוחר להפעיל פריט מסוים. השיטה עברה המזהה שהוקצה על ידי שירות דפדפן המדיה לפריט המדיה בהיררכיית התוכן.
onPlayFromSearch()
- מופעלת כשהמשתמש בוחר להפעיל משאילתת חיפוש. האפליקציה חייבת לבצע בחירה מתאימה על סמך מחרוזת החיפוש שהועברה.
onPause()
- מופעלת כשהמשתמש בוחר להשהות את ההפעלה.
onSkipToNext()
- מופעלת כשהמשתמש בוחר לדלג לפריט הבא.
onSkipToPrevious()
- מופעלת כשהמשתמש בוחר לדלג לפריט הקודם.
onStop()
- מופעלת כשהמשתמש בוחר להפסיק את ההפעלה.
כדי לספק את הפונקציונליות הרצויה, צריך לשנות את השיטות האלה באפליקציה. שלך
אין צורך להטמיע שיטה אם האפליקציה לא תומכת בפונקציונליות שלה. עבור
לדוגמה, אם האפליקציה מפעילה שידור חי, כגון שידור ספורט,
לא צריכים להטמיע את השיטה onSkipToNext()
. אפשר להשתמש בברירת המחדל
יישום של onSkipToNext()
במקום זאת.
לא נדרשת שום היגיון מיוחד כדי להפעיל תוכן דרך הרכב וברמקולים. כשהאפליקציה מקבלת בקשה להפעלת תוכן, היא יכולה להשמיע אודיו בדיוק כמו שהוא מציג תוכן מהרמקולים או האוזניות של המשתמש. Android Auto ו-Android Automotive OS שולח באופן אוטומטי את תוכן האודיו למערכת הרכב כדי להשמיע וברמקולים של הרכב.
למידע נוסף על הפעלת תוכן אודיו, אפשר לבקר כאן: סקירה כללית של MediaPlayer, סקירה כללית של אפליקציית האודיו, ובסקירה הכללית של ExoPlayer.
הגדרת פעולות הפעלה רגילות
ב-Android Auto וב-Android Automotive OS מוצגים פקדי ההפעלה שמבוססים על
פעולות שמופעלות בדף PlaybackStateCompat
לאובייקט.
כברירת מחדל, האפליקציה צריכה לתמוך בפעולות הבאות:
האפליקציה יכולה לתמוך גם בפעולות הבאות, אם הן רלוונטיות תוכן האפליקציה:
בנוסף, יש לך אפשרות ליצור תור הפעלה שיוצג
למשתמש, אבל זו לא חובה. כדי לעשות זאת, צריך לקרוא ל-setQueue()
ו-setQueueTitle()
methods, מפעילים את ACTION_SKIP_TO_QUEUE_ITEM
פעולה, ולהגדיר את הקריאה החוזרת onSkipToQueueItem()
.
כמו כן, הוסיפו תמיכה בסמל מה שומעים עכשיו?
מה שומעים כרגע? כדי לעשות זאת, צריך לקרוא ל-setActiveQueueItemId()
ומעבירים את המזהה של הפריט שמופעל עכשיו בתור. צריך:
לעדכן את setActiveQueueItemId()
בכל פעם שיש שינוי בתור.
לחצני התצוגה של Android Auto ו-Android Automotive OS לכל פעולה מופעלת
וגם את תור ההפעלה. כשהלחצנים
שעליהם לוחצים, המערכת מפעילה את הקריאה החוזרת התואמת
MediaSessionCompat.Callback
שמירת שטח אחסון לא בשימוש
ממשק המשתמש של Android Auto ו-Android Automotive OS ששומרים מקום
ACTION_SKIP_TO_PREVIOUS
ו-ACTION_SKIP_TO_NEXT
פעולות. אם האפליקציה
לא תומכות באחת מהפונקציות האלה, ב-Android Auto וב-Android Automotive OS
כדי להציג את כל הפעולות המותאמות אישית שאתם יוצרים.
אם אתם לא רוצים למלא את המרחבים המשותפים האלה בפעולות מותאמות אישית, אתם יכולים להזמין
כך ש-Android Auto ו-Android Automotive OS ישאירו את הרווח ריק
כשהאפליקציה לא תומכת בפונקציה המתאימה. לשם כך, התקשרו
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
כדי לציין את הערכים שלהם.
הגדרת מצב הפעלה ראשוני
בזמן ש-Android Auto ו-Android Automotive OS מתקשרים עם דפדפן המדיה
השירות, סשן המדיה שלך מעביר את הסטטוס של הפעלת התוכן באמצעות
PlaybackStateCompat
.
האפליקציה לא אמורה להתחיל להשמיע מוזיקה באופן אוטומטי כאשר Android Automotive OS
או Android Auto מתחברים לשירות דפדפן המדיה. במקום זאת, הסתמכו על Android
Auto ו-Android Automotive OS כדי להמשיך או להתחיל את ההפעלה בהתאם
או פעולות המשתמש.
כדי לעשות זאת, צריך להגדיר את PlaybackStateCompat
הראשוני
של סשן המדיה שלך
STATE_STOPPED
,
STATE_PAUSED
,
STATE_NONE
,
או STATE_ERROR
.
סשנים של מדיה ב-Android Auto וב-Android Automotive OS נמשכים רק
משך הנסיעה, כך שהמשתמשים מתחילים ומפסיקים את הסשנים האלה לעיתים קרובות. שפת תרגום
לקדם חוויה חלקה בין נסיעות, לעקוב אחר הנסיעה הקודמת של המשתמש
את מצב הסשן, כך שכשאפליקציית המדיה מקבלת
המשך הבקשה, המשתמש יכול להמשיך באופן אוטומטי מהמקום שבו עזב
כבוי - לדוגמה, פריט המדיה האחרון שהופעל, PlaybackStateCompat
,
ואת התור.
הוספת פעולות הפעלה בהתאמה אישית
אתם יכולים להוסיף פעולות הפעלה בהתאמה אישית כדי להציג פעולות נוספות שאתם
שאפליקציית המדיה תומכת בה. אם המקום מאפשר (ולא שמור מקום), Android מוסיפה את
פעולות מותאמות אישית לבקרת התעבורה. אחרת, הפעולות המותאמות אישית
יוצגו בתפריט האפשרויות הנוספות. הפעולות המותאמות אישית מוצגות לפי הסדר
הם נוספים אל PlaybackStateCompat
.
שימוש בפעולות מותאמות אישית כדי לספק התנהגות שונה מהתנהגות רגילה פעולות. אין להשתמש בהם כדי להחליף או לשכפל מודעות רגילות פעולות.
אפשר להוסיף פעולות מותאמות אישית באמצעות addCustomAction()
ב-PlaybackStateCompat.Builder
בכיתה.
קטע הקוד הבא מראה איך להוסיף ערוץ מותאם אישית מסוג 'פתיחת ערוץ רדיו' action:
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
(לקישור מתוך
בתיאור). לקבלת פרטים נוספים, תוכלו לקרוא את מאמרי העזרה של
בשדות של מטא-נתונים.
תמיכה בפעולות קוליות
אפליקציית המדיה חייבת לתמוך בפעולות קוליות כדי לספק לנהגים סביבה בטוחה נוחה ולצמצם את הסחות הדעת. לדוגמה, אם האפליקציה מפעיל פריט מדיה אחד, המשתמש יכול לומר "Play [song title]" כדי לבקש מהאפליקציה להשמיע שיר אחר בלי להסתכל על הרכב או לגעת בו מסך. המשתמשים יכולים ליזום שאילתות על ידי לחיצה על הלחצנים המתאימים להשתמש בהגה או לומר את מילות ההפעלה "OK Google".
כש-Android Auto או Android Automotive OS מזהים קול ומנתח אותו
פעולה קולית, היא מועברת לאפליקציה דרך
onPlayFromSearch()
לאחר קבלת הקריאה החוזרת, האפליקציה תמצא תוכן שתואם לquery
מחרוזת ומתחילה את ההפעלה.
המשתמשים יכולים לציין קטגוריות שונות של מונחים בשאילתה שלהם: ז'אנר, אומן,
אלבום, שם שיר, תחנת רדיו או פלייליסט, בין היתר. בזמן הבנייה
תמיכה בחיפוש, צריך להביא בחשבון את כל הקטגוריות שמתאימות לאפליקציה.
אם Android Auto או Android Automotive OS מזהים ששאילתה נתונה מתאימה
מקטגוריות מסוימות, הוא מוסיף תוספות בפרמטר extras
.
ניתן לשלוח את התוספות הבאות:
חשבון למחרוזת query
ריקה, שאותה ניתן לשלוח באמצעות
Android Auto או Android Automotive OS אם המשתמש לא מציין מונחי חיפוש.
לדוגמה, אם המשתמש אומר "אני רוצה לשמוע מוזיקה". במקרה כזה, האפליקציה עשויה
בוחרים אם להתחיל טראק שהשמעת לאחרונה או הצעה חדשה.
אם לא ניתן לעבד חיפוש במהירות, אין לחסום ב-onPlayFromSearch()
.
במקום זאת, צריך להגדיר את מצב ההפעלה ל-STATE_CONNECTING
ומבצעים את החיפוש בשרשור אסינכרוני.
לאחר שההפעלה מתחילה, כדאי לאכלס את התור של סשן המדיה תוכן קשור. לדוגמה, אם המשתמש מבקש להפעיל אלבום, האפליקציה עשויה למלא את התור ברשימת הטראקים של האלבום. כדאי גם ליישם תמיכה בתוצאות חיפוש שניתן לעיין בהן, כך שמשתמש יכול לבחור טראק אחר שתואם לשאילתה שלהם.
בנוסף ל-'play' שאילתות, Android Auto ו-Android Automotive OS
לזהות שאילתות קוליות כדי לשלוט בהפעלה כמו "pause music". וגם "הבא
שיר" ולהתאים את הפקודות האלה לקריאות החוזרות (callback) המתאימות של סשן המדיה,
כמו onPause()
ו-onSkipToNext()
.
דוגמה מפורטת להטמעה של פעולות הפעלה קוליות ב: האפליקציה שלכם, תוכלו להיעזר במאמר Google Assistant ואפליקציות מדיה.
הטמעת אמצעי הגנה מפני הסחות דעת
כי טלפון של המשתמש מחובר לרמקולים של הרכב בזמן השימוש ב-Android במצב אוטומטי, חובה לנקוט אמצעי זהירות נוספים כדי למנוע את הסחות הדעת של הנהג.
השבתת ההתראות ברכב
אסור שאפליקציות מדיה ל-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 OS
הודעה למשתמש.
לדוגמה, אם התוכן לא זמין באזור הנוכחי של המשתמש, תוכלו
להשתמש ב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 צריך לפתוח את האפליקציה לטלפון כדי לפתור שגיאה, מספקים את המידע הזה למשתמש בהודעה. לדוגמה, השגיאה שבה נתקלת יכול להיות שתופיע ההודעה "Sign in to [your app name] " במקום 'נא להיכנס'.