המוצרים Android Auto ו-Android Automotive OS עוזרים לכם להציג את התוכן של אפליקציית המדיה למשתמשים במכונית שלהם.
יש שתי דרכים ליצור אפליקציות מדיה לרכב:
במדריך הזה מוסבר איך להשתמש ב-
MediaBrowserService
וב-MediaSession
כדי ליצור אפליקציה שאליה Android Auto ו-Android Automotive OS יכולים להתחבר כדי להציג תצוגות של חיפוש והפעלה של מדיה שממוטבות לשימוש ברכב.אפשר גם ליצור אפליקציות מדיה באמצעות תבניות של Car App Library כדי להתאים אישית את הפורמט, את אפשרויות העיון ואת הפעולות המותאמות אישית. פרטים על ההטמעה זמינים במאמר בנושא פיתוח אפליקציית מדיה מבוססת-תבנית. אפליקציות מדיה מבוססות-תבניות נתמכות כרגע רק ב-Android Auto.
במדריך הזה מתוארים הרכיבים הנדרשים של MediaBrowserService
ושל MediaSession
שהאפליקציה צריכה כדי לפעול ב-Android Auto או ב-Android Automotive OS. אחרי שמשלימים את התשתית הבסיסית של המדיה, אפשר להוסיף תמיכה ב-Android Auto ולהוסיף תמיכה ב-Android Automotive OS לאפליקציית המדיה.
המדריך הזה מיועד למפתחים שכבר יש להם אפליקציית מדיה שמפעילה אודיו בטלפון, ושמבנה אפליקציית המדיה שלהם תואם לארכיטקטורה של אפליקציית מדיה ב-Android.
לפני שמתחילים
- מידע נוסף זמין במאמרי העזרה של 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 יוכלו לגלות את השירות ולהתחבר לאפליקציה.
קטע הקוד הבא מראה איך להצהיר על שירות דפדפן המדיה במניפסט. צריך לכלול את הקוד הזה בקובץ המניפסט של מודול 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 יכולים להשתמש בהם כדי לייצג את האפליקציה בממשק המשתמש של המערכת. חובה לציין שני סוגים של סמלים:
- סמל מרכז האפליקציות
- סמל השיוך (Attribution)
סמל מרכז האפליקציות
סמל מרכז האפליקציות מייצג את האפליקציה בממשק המשתמש של המערכת, למשל במרכז האפליקציות ובמגש הסמלים. אתם יכולים לציין שאתם רוצים להשתמש בסמל מאפליקציית הנייד שלכם כדי לייצג את אפליקציית המדיה לרכב באמצעות ההצהרה הבאה בקובץ המניפסט:
<application
...
android:icon="@mipmap/ic_launcher"
...
/>
כדי להשתמש בסמל שונה מזה של האפליקציה לנייד, צריך להגדיר את המאפיין android:icon
ברכיב <service>
של שירות דפדפן המדיה במניפסט:
<application>
...
<service
...
android:icon="@mipmap/auto_launcher"
...
/>
</application>
סמל השיוך (Attribution)

איור 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 ו-Android Auto פועלות באינטראקציה עם שירות דפדפן המדיה במהלך תהליך עבודה אופייני של משתמש.
- המשתמש מפעיל את האפליקציה ב-Android Automotive OS או ב-Android Auto.
- מערכת Android Automotive OS או Android Auto יוצרת קשר עם שירות דפדפן המדיה של האפליקציה באמצעות השיטה
onCreate()
. בהטמעה של methodonCreate()
, צריך ליצור ולרשום אובייקטMediaSessionCompat
ואת אובייקט הקריאה החוזרת שלו. - Android Automotive OS או Android Auto קוראים לשיטת
onGetRoot()
של השירות כדי לקבל את פריט המדיה הבסיסי בהיררכיית התוכן. פריט המדיה הבסיסי לא מוצג, אלא משמש לאחזור תוכן נוסף מהאפליקציה. - Android Automotive OS או Android Auto קוראים לשיטה
onLoadChildren()
של השירות כדי לקבל את הפריטים המשניים של פריט המדיה הבסיסי. ב-Android Automotive OS וב-Android Auto, פריטי המדיה האלה מוצגים כפריטים ברמה העליונה של התוכן. בקטע מבנה תפריט השורש בדף הזה מוסבר מה המערכת מצפה ברמה הזו. - אם המשתמש בוחר פריט מדיה שאפשר לעיין בו, המערכת קוראת שוב לשיטה
onLoadChildren()
של השירות כדי לאחזר את פריטי המשנה של פריט התפריט שנבחר. - אם המשתמש בוחר פריט מדיה שאפשר להפעיל, מערכת ההפעלה Android Automotive OS או Android Auto קוראת לשיטת הקריאה החוזרת המתאימה של סשן המדיה כדי לבצע את הפעולה הזו.
- אם האפליקציה תומכת בכך, המשתמש יכול גם לחפש את התוכן שלכם. במקרה הזה, מערכת ההפעלה Android Automotive OS או Android Auto יקראו לשיטה
onSearch()
של השירות.
בניית היררכיית התוכן
מערכות Android Auto ו-Android Automotive OS קוראות לשירות דפדפן המדיה של האפליקציה כדי לגלות איזה תוכן זמין. כדי לתמוך בזה, צריך להטמיע שתי שיטות בשירות של דפדפן המדיה: onGetRoot()
ו-onLoadChildren()
.
הטמעה של onGetRoot
השיטה onGetRoot()
של השירות מחזירה מידע על צומת הבסיס בהיררכיית התוכן.
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()
של השירות, חבילת ההתקשרות מעבירה לשירות מידע מזהה. השירות יכול להשתמש במידע הזה כדי להחליט אם החבילה יכולה לגשת לתוכן שלכם. לדוגמה, אפשר להגביל את הגישה לתוכן של האפליקציה לרשימה של חבילות מאושרות על ידי השוואת clientPackageName
לרשימת ההיתרים ואימות האישור ששימש לחתימה על ה-APK של החבילה. אם אי אפשר לאמת את החבילה, צריך לחזור אל null
כדי לחסום את הגישה לתוכן.
כדי לספק לאפליקציות מערכת, כמו Android Auto ו-Android Automotive OS, גישה לתוכן שלכם, השירות שלכם צריך תמיד להחזיר BrowserRoot
שאינו null כשמערכות האפליקציות האלה קוראות לשיטת 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()
אחרי קבלת אובייקט צומת השורש, מערכות Android Auto ו-Android Automotive OS יוצרות תפריט ברמה העליונה על ידי קריאה ל-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.
מבנה תפריט השורש

איור 2. תוכן הבסיס מוצג ככרטיסיות ניווט.
יש מגבלות ספציפיות לגבי המבנה של תפריט השורש ב-Android Auto וב-Android Automotive OS. המידע הזה מועבר אל 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... }
אתם יכולים לבחור לפצל את הלוגיקה של מבנה היררכיית התוכן על סמך הערכים של הרמזים האלה, במיוחד אם ההיררכיה משתנה בין שילובים מחוץ ל-Android Auto ול-Android Automotive OS.MediaBrowser
לדוגמה, אם בדרך כלל מוצג פריט שניתן להפעלה ברמת הבסיס, יכול להיות שתרצו להציג אותו כפריט שניתן לעיון ברמת הבסיס במקום זאת, בהתאם לערך של רמז הדגלים הנתמכים.
בנוסף לרמזים לגבי שורש, יש עוד כמה הנחיות שכדאי לפעול לפיהן כדי לוודא שהכרטיסיות מוצגות בצורה אופטימלית:
- צריך לספק סמלים חד-צבעיים, רצוי לבנים, לכל פריט בכרטיסייה.
- חשוב לספק תוויות קצרות אבל משמעותיות לכל פריט בכרטיסייה. אם התוויות קצרות, יש פחות סיכויים שהמחרוזות ייחתכו.
הצגת יצירות אומנות של מדיה
צריך להעביר את יצירות האומנות של פריטי המדיה כמזהה משאבים אחיד (URI) מקומי באמצעות ContentResolver.SCHEME_CONTENT
או ContentResolver.SCHEME_ANDROID_RESOURCE
.
ה-URI המקומי הזה חייב להפנות ל-bitmap או ל-vector drawable במשאבים של האפליקציה. לאובייקטים מסוג 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 Player.
יוצרים
content://
URI שמתאים ל-URI של האינטרנט. שירות דפדפן המדיה ומסד נתוני המדיה מעבירים את ה-URI של התוכן הזה אל Android Auto ואל Android Automotive OS.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
: הפריטים התואמים מוצגים כפריטים ברשימה 'קטגוריה'. הם זהים לפריטים רגילים ברשימה, אלא שהשוליים מוחלים סביב הסמלים של הפריטים, כי הסמלים נראים טוב יותר כשהם קטנים. הסמלים צריכים להיות פריטים גרפיים וקטוריים שניתן לצבוע אותם. ההצעה הזו אמורה להופיע רק לפריטים שאפשר לעיין בהם. -
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
עם כותרת של קבוצת משנה "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")
- קובץ מדיה C עם
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- קובץ מדיה D עם
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 בשם 'שירים' שמכילה את פריט המדיה א'
- קבוצה 2 בשם 'אלבומים' שמכילה את פריט המדיה ב'
- קבוצה 3 בשם 'שירים' שמכילה את פריטי המדיה C ו-D
- קבוצה 4 בשם 'אלבומים' שמכילה את פריט המדיה E
כדי להציג את הפריטים האלה בשתי קבוצות, האפליקציה צריכה להעביר את פריטי המדיה בסדר הבא:
- פריט מדיה א' עם
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- קובץ מדיה C עם
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")
- קובץ מדיה E עם
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
הצגת אינדיקטורים נוספים של מטא-נתונים
אתם יכולים לכלול עוד אינדיקטורים של מטא-נתונים כדי לספק מידע במבט חטוף על התוכן בעץ של דפדפן המדיה ובמהלך ההפעלה. בעץ הדפדוף, מערכות Android Auto ו-Android Automotive OS קוראות את התוספים שמשויכים לפריט ומחפשות קבועים מסוימים כדי לקבוע אילו אינדיקטורים להציג. במהלך הפעלה של מדיה, מערכות Android Auto ו-Android Automotive OS קוראות את המטא-נתונים של סשן המדיה ומחפשות קבועים מסוימים כדי לקבוע אילו אינדיקטורים להציג.

איור 3. תצוגת הפעלה עם מטא-נתונים שמזהים את השיר ואת האומן, וגם סמל שמציין תוכן בוטה.

איור 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
: מציין את מידת ההתקדמות בצפייה בתוכן ארוך כערך מסוג double בין 0.0 ל-1.0, כולל. התוספת הזו מספקת מידע נוסף עלPARTIALLY_PLAYING
המצב, כדי שמערכות Android Auto או Android Automotive OS יוכלו להציג אינדיקטור התקדמות משמעותי יותר, כמו סרגל התקדמות. אם אתם משתמשים בתוסף הזה, כדאי לעיין בקטע בנושא עדכון סרגל ההתקדמות בתצוגת העיון בזמן שהתוכן מוצג במדריך הזה כדי ללמוד איך לעדכן את האינדיקטור הזה אחרי ההצגה הראשונית שלו.
כדי להציג אינדיקטורים שמופיעים בזמן שהמשתמש מעיין בעץ של דפדפן המדיה, צריך ליצור חבילת extras שכוללת קבוע אחד או יותר מהקבועים האלה ולהעביר את החבילה הזו לשיטה 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
או EXTRA_DOWNLOAD_STATUS
ב-MediaMetadataCompat
של mediaSession
.METADATA_KEY_IS_EXPLICIT
אי אפשר להציג את האינדיקטורים 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());
עדכון סרגל ההתקדמות בתצוגת הגלישה בזמן שהתוכן מושמע
כפי שצוין קודם, אפשר להשתמש ב-extra של
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());
הצגת תוצאות חיפוש שאפשר לעיין בהן

איור 5. תצוגת הפעלה עם האפשרות 'תוצאות חיפוש' לצפייה בפריטי מדיה שקשורים לחיפוש הקולי של המשתמש.
האפליקציה יכולה לספק תוצאות חיפוש הקשריות שמוצגות למשתמשים כשהם מזינים שאילתת חיפוש. מערכות ההפעלה 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. }
פעולות מותאמות אישית בדפדפן

איור 6. פעולת עיון מותאמת אישית יחידה
פעולות מותאמות אישית בדפדפן מאפשרות לכם להוסיף סמלים ותוויות מותאמים אישית לאובייקטים של האפליקציה שלכם באפליקציית המדיה של הרכב, ולטפל באינטראקציות של המשתמשים עם הפעולות האלה.MediaItem
כך תוכלו להרחיב את הפונקציונליות של אפליקציית המדיה במגוון דרכים, כמו הוספת הפעולות 'הורדה', 'הוספה לתור', 'הפעלת רדיו', 'הוספה למועדפים' או 'הסרה'.

איור 7. תפריט האפשרויות הנוספות של פעולת עיון בהתאמה אישית
אם יש יותר פעולות מותאמות אישית מכמות הפעולות שיצרן הציוד המקורי מאפשר להציג, יוצג למשתמש תפריט overflow.
איך הם פועלים?
כל פעולת גלישה בהתאמה אישית מוגדרת באמצעות:
- מזהה פעולה (מחרוזת מזהה ייחודית)
- תווית פעולה (הטקסט שמוצג למשתמש)
- URI של סמל פעולה (פריט גרפי וקטורי שניתן לצביעה)
אתם מגדירים רשימה של פעולות גלישה מותאמות אישית באופן גלובלי כחלק מBrowseRoot
. אחר כך תוכלו לצרף קבוצת משנה של הפעולות האלה לכל אחד מהכפתורים במכשיר MediaItem.
כשמשתמש מקיים אינטראקציה עם פעולת גלישה מותאמת אישית, האפליקציה מקבלת קריאה חוזרת (callback) ב-onCustomAction()
. לאחר מכן תוכלו לטפל בפעולה ולעדכן את רשימת הפעולות של MediaItem
אם צריך. זה שימושי לפעולות עם שמירת מצב כמו 'הוספה למועדפים' ו'הורדה'. לגבי פעולות שלא צריך לעדכן, כמו 'הפעלת רדיו', אין צורך לעדכן את רשימת הפעולות.

איור 8. סרגל כלים של פעולות מותאמות אישית בדפדפן
אפשר גם לצרף פעולות גלישה בהתאמה אישית לשורש של צומת גלישה. הפעולות האלה יוצגו בסרגל כלים משני מתחת לסרגל הכלים הראשי.
איך מטמיעים פעולות גלישה בהתאמה אישית
כך מוסיפים פעולות מותאמות אישית של גלישה לפרויקט:
- מחליפים שתי שיטות בהטמעה של
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
: ה-URI של סמל הפעולה * מוסיפים את כל אובייקטי הפעולהBundle
לרשימה.
- לכל פעולה, יוצרים אובייקט
- מוסיפים את הרשימה הגלובלית אל
BrowseRoot
:- בקטע
BrowseRoot
extrasBundle
, מוסיפים את רשימת הפעולות כ-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
כדי להתחיל להשתמש בפעולות גלישה בהתאמה אישית.
Override 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
כדי לעדכן את ממשק המשתמש.
הגישה הזו מספקת למשתמשים דרך ברורה ועקבית לנהל את הפריטים המועדפים שלהם.
הדוגמאות האלה ממחישות את הגמישות של פעולות גלישה בהתאמה אישית, ומראות איך אפשר להשתמש בהן כדי להטמיע מגוון פונקציות עם משוב בזמן אמת, כדי לשפר את חוויית המשתמש באפליקציית המדיה במכונית.
דוגמה מלאה להטמעה של התכונה הזו מופיעה בTestMediaApp
פרויקט.
הפעלת כפתורי ההפעלה
Android Auto ו-Android Automotive OS שולחים פקודות לשליטה בהפעלה דרך 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()); ... }
כשיוצרים את אובייקט סשן המדיה, מגדירים אובייקט של קריאה חוזרת שמשמש לטיפול בבקשות של בקרת ההפעלה. כדי ליצור את אובייקט הקריאה החוזרת (callback), צריך לספק הטמעה של המחלקה MediaSessionCompat.Callback
באפליקציה. בסעיף הבא מוסבר איך להטמיע את האובייקט הזה.
הטמעה של פקודות הפעלה
כשמשתמש מבקש להפעיל פריט מדיה מהאפליקציה שלכם, מערכת Android Automotive OS ו-Android Auto משתמשות במחלקה MediaSessionCompat.Callback
מאובייקט MediaSessionCompat
של האפליקציה, שהן קיבלו משירות דפדפן המדיה של האפליקציה. כשמשתמש רוצה לשלוט בהפעלת תוכן, כמו השהיית ההפעלה או דילוג לטראק הבא, מערכות Android Auto ו-Android Automotive OS מפעילות אחת מהשיטות של אובייקט הקריאה החוזרת.
כדי לטפל בהפעלת תוכן, האפליקציה צריכה להרחיב את המחלקה המופשטת MediaSessionCompat.Callback
ולהטמיע את השיטות שהאפליקציה תומכת בהן.
צריך להטמיע את כל שיטות הקריאה החוזרת הבאות שמתאימות לסוג התוכן שהאפליקציה מציעה:
onPrepare()
- מופעל כשמקור המדיה משתנה. מערכת Android Automotive OS מפעילה את השיטה הזו מיד אחרי האתחול. אפליקציית המדיה שלכם צריכה להטמיע את השיטה הזו.
onPlay()
- מופעל אם המשתמש בוחר להפעיל את התוכן בלי לבחור פריט ספציפי. האפליקציה צריכה להפעיל את תוכן ברירת המחדל שלה, או אם ההפעלה הופסקה באמצעות
onPause()
, האפליקציה צריכה להמשיך את ההפעלה.הערה: האפליקציה לא אמורה להתחיל להפעיל מוזיקה באופן אוטומטי כש-Android Automotive OS או Android Auto מתחברים לשירות של דפדפן המדיה. מידע נוסף זמין בקטע בנושא הגדרת מצב ההפעלה הראשוני.
onPlayFromMediaId()
- מופעל כשמשתמש בוחר להפעיל פריט ספציפי. השיטה מעבירה את המזהה שהוקצה על ידי שירות הדפדפן של המדיה לפריט המדיה בהיררכיית התוכן.
onPlayFromSearch()
- מופעל כשמשתמש בוחר להפעיל תוכן מתוך שאילתת חיפוש. האפליקציה צריכה לבחור את האפשרות המתאימה על סמך מחרוזת החיפוש שהועברה.
onPause()
- Invoked when the user chooses to pause playback.
onSkipToNext()
- מופעל כשהמשתמש בוחר לדלג לפריט הבא.
onSkipToPrevious()
- מופעל כשמשתמש בוחר לדלג לפריט הקודם.
onStop()
- Invoked when the user chooses to stop playback.
אפשר להגדיר באפליקציה שיטות חלופיות לשיטות האלה כדי לספק את הפונקציונליות הרצויה. אין צורך להטמיע שיטה אם הפונקציונליות שלה לא נתמכת באפליקציה. לדוגמה, אם האפליקציה מפעילה סטרימינג בשידור חי, כמו שידור של אירוע ספורט, אין צורך להטמיע את השיטה onSkipToNext()
. אפשר להשתמש במקום זאת בהטמעה של onSkipToNext()
שמוגדרת כברירת מחדל.
לא נדרשת באפליקציה לוגיקה מיוחדת כדי להפעיל תוכן דרך הרמקולים של הרכב. כשהאפליקציה מקבלת בקשה להפעיל תוכן, היא יכולה להפעיל אודיו באותו אופן שבו היא מפעילה תוכן דרך הרמקולים או האוזניות של הטלפון של המשתמש. Android Auto ו-Android Automotive OS שולחות אוטומטית את תוכן האודיו למערכת של הרכב כדי להפעיל אותו דרך הרמקולים של הרכב.
מידע נוסף על הפעלת תוכן אודיו זמין במאמרים סקירה כללית על MediaPlayer, סקירה כללית על אפליקציית האודיו וסקירה כללית על ExoPlayer.
הגדרת פעולות הפעלה רגילות
מערכות Android Auto ו-Android Automotive OS מציגות אמצעי בקרה להפעלה על סמך הפעולות שמופעלות באובייקט PlaybackStateCompat
.
כברירת מחדל, האפליקציה צריכה לתמוך בפעולות הבאות:
בנוסף, האפליקציה יכולה לתמוך בפעולות הבאות אם הן רלוונטיות לתוכן של האפליקציה:
בנוסף, יש לך אפשרות ליצור תור השמעה שיוצג למשתמש, אבל זו לא חובה. כדי לעשות זאת, שולחים קריאה לרכיבי ה-method setQueue()
ו-setQueueTitle()
, מפעילים את פעולת ACTION_SKIP_TO_QUEUE_ITEM
ומגדירים את הקריאה החוזרת onSkipToQueueItem()
.
בנוסף, הוספנו תמיכה בסמל מה שומעים עכשיו, שמציין מה מושמע כרגע. כדי לעשות את זה, מבצעים קריאה ל-method 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()
עם חבילת extras שמכילה קבועים שתואמים לפונקציות השמורות.
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 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
.
בקטע הקוד הבא מוצג איך להוסיף פעולה מותאמת אישית של 'הפעלת תחנת רדיו':
Kotlin
val customActionExtras = Bundle() customActionExtras.putInt( androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, androidx.media3.session.CommandButton.ICON_RADIO) stateBuilder.addCustomAction( PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon // or R.drawable.media3_icon_radio ).run { setExtras(customActionExtras) build() } )
Java
Bundle customActionExtras = new Bundle(); customActionExtras.putInt( androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, androidx.media3.session.CommandButton.ICON_RADIO); stateBuilder.addCustomAction( new PlaybackStateCompat.CustomAction.Builder( CUSTOM_ACTION_START_RADIO_FROM_MEDIA, resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon) // or R.drawable.media3_icon_radio .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.
סמלים לפעולות מותאמות אישית
לכל פעולה מותאמת אישית שיוצרים צריך להוסיף סמל.
אם התיאור של הסמל תואם לאחד מהקבועים CommandButton.ICON_
, צריך להגדיר את ערך המספר השלם הזה למפתח EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT
של התוספים של הפעולה המותאמת אישית. במערכות נתמכות, הפעולה הזו תבטל את משאב הסמל שהועבר אל CustomAction.Builder
, ותאפשר לרכיבי המערכת לעבד את הפעולה ואת פעולות ההפעלה האחרות בסגנון עקבי.
צריך לציין גם משאב של סמל. אפליקציות במכוניות יכולות לפעול במסכים בגדלים ובצפיפויות שונים, ולכן הסמלים שאתם מספקים צריכים להיות vector drawables. A vector drawable מאפשר לכם לשנות את הגודל של נכסים בלי לאבד את הפרטים. בנוסף, קל ליישר קצוות ופינות של תמונות וקטוריות לגבולות פיקסלים ברזולוציות קטנות יותר.
אם פעולה מותאמת אישית היא בעלת מצב – למשל, היא מחליפה מצב של הגדרת הפעלה בין מופעל למושבת – צריך לספק סמלים שונים למצבים השונים, כדי שהמשתמשים יוכלו לראות שינוי כשהם בוחרים את הפעולה.
הוספת סגנונות סמלים חלופיים לפעולות מושבתות
אם פעולה בהתאמה אישית לא זמינה בהקשר הנוכחי, צריך להחליף את הסמל של הפעולה בהתאמה אישית בסמל חלופי שמראה שהפעולה מושבתת.
ציון פורמט האודיו
כדי לציין שהמדיה שמופעלת כרגע משתמשת בפורמט אודיו מיוחד, אתם יכולים לציין סמלים שמוצגים במכוניות שתומכות בתכונה הזו. אפשר להגדיר את KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI
ואת KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI
בחבילת התוספים של פריט המדיה שמופעל כרגע (מועבר אל MediaSession.setMetadata()
). חשוב להגדיר את שני התוספים האלה כדי להתאים לפריסות שונות.
בנוסף, אפשר להגדיר את KEY_IMMERSIVE_AUDIO
extra כדי להודיע ליצרני רכב שמדובר באודיו היקפי, ושהם צריכים להיות זהירים מאוד כשהם מחליטים אם להחיל אפקטים של אודיו שעשויים להפריע לתוכן ההיקפי.
הוספת קישורים מפריט שמופעל כרגע
אפשר להגדיר את פריט המדיה שמופעל כרגע כך שהכתוביות, התיאור או שניהם יהיו קישורים לפריטי מדיה אחרים. כך המשתמש יכול לעבור במהירות לפריטים קשורים. לדוגמה, הוא יכול לעבור לשירים אחרים של אותו אמן, לפרקים אחרים של אותו פודקאסט וכו'. אם המכונית תומכת בתכונה הזו, המשתמשים יכולים להקיש על הקישור כדי לעיין בתוכן הזה.
כדי להוסיף קישורים, צריך להגדיר את המטא-נתונים KEY_SUBTITLE_LINK_MEDIA_ID
(כדי לקשר מהכתובית) או KEY_DESCRIPTION_LINK_MEDIA_ID
(כדי לקשר מהתיאור). פרטים נוספים מופיעים במאמרי העזרה בנושא שדות המטא-נתונים האלה.
תמיכה בפעולות קוליות
אפליקציית המדיה שלכם צריכה לתמוך בפעולות קוליות כדי לספק לנהגים חוויה בטוחה ונוחה שממזערת את ההסחות. לדוגמה, אם האפליקציה מפעילה פריט מדיה אחד, המשתמש יכול להגיד Play [song title] כדי להפעיל שיר אחר בלי להסתכל על המסך של הרכב או לגעת בו. המשתמשים יכולים להפעיל שאילתות בלחיצה על הלחצנים המתאימים בהגה או באמירת מילות ההפעלה Ok Google.
כשמערכת Android Auto או Android Automotive OS מזהה ומפרשת פעולה קולית, הפעולה הקולית מועברת לאפליקציה דרך onPlayFromSearch()
.
כשמתקבלת קריאה חוזרת (callback) כזו, האפליקציה מוצאת תוכן שתואם למחרוזת query
ומתחילה להפעיל אותו.
המשתמשים יכולים לציין בשאילתה שלהם קטגוריות שונות של מונחים: ז'אנר, אומן, אלבום, שם השיר, תחנת רדיו או פלייליסט, ועוד. כשמפתחים תמיכה בחיפוש, צריך להתייחס לכל הקטגוריות שרלוונטיות לאפליקציה. אם מערכת Android Auto או Android Automotive OS מזהה ששאילתה מסוימת מתאימה לקטגוריות מסוימות, היא מוסיפה תוספים בפרמטר extras
. אפשר לשלוח את התוספים הבאים:
צריך להביא בחשבון מחרוזת ריקה query
, שיכולה להישלח על ידי Android Auto או Android Automotive OS אם המשתמש לא מציין מונחי חיפוש.
לדוגמה, אם המשתמש אומר "Play some music". במקרה כזה, יכול להיות שהאפליקציה תתחיל להשמיע טראק שהושמע לאחרונה או טראק חדש שהוצע.
אם אי אפשר לעבד חיפוש במהירות, אל תחסמו אותו ב-onPlayFromSearch()
.
במקום זאת, מגדירים את מצב ההפעלה ל-STATE_CONNECTING
ומבצעים את החיפוש בשרשור אסינכרוני.
אחרי שההפעלה מתחילה, כדאי לאכלס את התור של סשן המדיה בתוכן שקשור אליו. לדוגמה, אם המשתמש מבקש להפעיל אלבום, האפליקציה שלכם יכולה למלא את התור ברשימת הרצועות של האלבום. כדאי גם להטמיע תמיכה בתוצאות חיפוש שניתן לעיין בהן, כדי שהמשתמש יוכל לבחור טראק אחר שתואם לשאילתה שלו.
בנוסף לשאילתות מסוג play, מערכות Android Auto ו-Android Automotive OS מזהות שאילתות קוליות לשליטה בהפעלה, כמו pause music ו-next song, ומתאימות את הפקודות האלה לקריאות החוזרות המתאימות של סשן המדיה, כמו onPause()
ו-onSkipToNext()
.
דוגמה מפורטת להטמעה של פעולות הפעלה שמופעלות באמצעות קול באפליקציה זמינה במאמר Google Assistant ואפליקציות מדיה.
הטמעת אמצעי הגנה מפני הסחות דעת
בזמן השימוש ב-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 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 צריך לפתוח את אפליקציית הטלפון כדי לפתור שגיאה, צריך לספק את המידע הזה למשתמש בהודעה. לדוגמה, יכול להיות שבהודעת השגיאה יופיע "כניסה אל [שם האפליקציה שלך]" במקום "עליך להיכנס לחשבון".