אפליקציות מדיה שמשתמשות בתבניות של Car App Library יכולות להתאים אישית את חוויית הגלישה וההפעלה של המדיה, וגם לוודא שהחוויה מותאמת למסכי רכב ומצמצמת את הסחות הדעת בזמן הנהיגה.
המדריך הזה מיועד למשתמשים שכבר יש להם אפליקציית מדיה שמפעילה אודיו בטלפון, ושמבנה אפליקציית המדיה שלהם תואם לארכיטקטורת אפליקציות המדיה של Android. ספריית האפליקציות לרכב מאפשרת להחליף את חוויית השימוש באפליקציה בתבניות במקום בתבניות שנוצרו באמצעות מבנה הנתונים של יצירת אפליקציות מדיה לרכב
MediaBrowser. עדיין צריך לספק MediaSession
לרכיבי ה-UI להפעלת סרטונים, וגם MediaBrowserService או MediaLibraryService,
שמשמשים להמלצות ולחוויות חכמות אחרות.
הגדרת המניפסט של האפליקציה
בנוסף לשלבים שמתוארים במאמר בנושא שימוש בספריית האפליקציות של Android למכוניות, אפליקציות מדיה שמבוססות על תבניות צריכות לעמוד בדרישות הבאות:
הצהרה על תמיכה בקטגוריה במניפסט
באפליקציה צריך להצהיר על androidx.car.app.category.MEDIA
קטגוריית אפליקציות לרכב במסנן ה-Intent של CarAppService.
<application>
...
<service
...
android:name=".MyCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.MEDIA"/>
</intent-filter>
</service>
...
<application>
כדי לקבל גישה אל MediaPlaybackTemplate, האפליקציה צריכה גם להצהיר על ההרשאה androidx.car.app.MEDIA_TEMPLATES בקובץ המניפסט שלה:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.MEDIA_TEMPLATES"/>
...
</manifest>
הגדרת רמת ה-API המינימלית של אפליקציית הרכב
אפליקציות מדיה שמשתמשות ב-MediaPlaybackTemplate נתמכות רק ב-CAL API 8 ומעלה. חשוב לוודא שערך המינימום של Car App API level מוגדר ל-8.
<application ...>
...
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="8"/>
...
</application>
הוספת סמל ייחוס
חשוב להוסיף סמל שיוך לאפליקציות מדיה שנוצרו באמצעות ספריית Car App.
הצהרה על תמיכה ב-Android Auto
חשוב לוודא שהרכיבים הבאים כלולים במניפסט של האפליקציה:
<application>
...
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
...
</application>
לאחר מכן, מוסיפים את הצהרת התבנית אל automotive_app_desc.xml במשאבי ה-XML. הוא אמור להיראות כך:
<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
<uses name="media"/>
<uses name="template"/>
</automotiveApp>
הצהרה על תמיכה ב-Android Automotive OS
יש שתי דרכים שונות להפצת אפליקציית מדיה שמופעלת באמצעות Car App Library ב-Android Automotive OS: כקובץ APK יחיד או כשני קובצי APK נפרדים. אם אתם מפיצים קובץ APK יחיד, הוא יתמוך בכלי רכב שמופעלת בהם מערכת Android Automotive OS עם מארח ספריית האפליקציות לרכב, ויחזור לאפליקציית MediaBrowserService או MediaLibraryService אם לא, גם בגרסאות Android ישנות יותר (Android 10 עד Android 13). אם תבחרו להפיץ שתי חבילות APK נפרדות, תוכלו לעדכן בקלות רבה יותר את התוספות החדשות לגרסה של Car App Library בלי לחשוש מהשפעה על גרסת MediaBrowserService או MediaLibraryService של האפליקציה.
הפצה של קובץ APK יחיד
כשמפיצים קובץ APK יחיד לגרסאות MediaBrowserService
או MediaLibraryService של האפליקציה, חשוב להגדיר את הערך של android:required="false".
<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>
לאחר מכן, פועלים לפי ההנחיות של ספריית האפליקציות לרכב ל-AAOS ומוסיפים CarAppActivity שאפשר להפעיל (או פעילות טרמפולינה). צריך להגדיר את הפעילות ל-android:enabled="false" במניפסט. לאחר מכן, מוסיפים תג מטא-נתונים להצהרה MediaBrowserService שמציין את רכיב CarAppActivity כתחליף. לדוגמה:
<service android:name=".media.MyMediaService"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
</intent-filter>
<!-- Link to Car App Library Activity -->
<meta-data
android:name="androidx.car.app.media.CalMediaActivityComponent"
android:value="com.example.mediaapp.LaunchableTrampoline"/>
</service>
<activity
android:name=".LaunchableTrampoline"
android:exported="true"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:launchMode="singleTask"
android:label="@string/app_name_cal"
android:enabled="false"> <!-- Set to false -->
<meta-data android:name="distractionOptimized" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
הפצה ב-Play
חבילת ה-APK עם ספריית האפליקציות לרכב ו-MediaBrowserService
או MediaLibraryService צריכה להיות מופעלת עם קוד גרסה גבוה יותר ו-minSdk שמטרגט את Android 14 (34).
הפצה באמצעות שני קובצי APK
כדי להפיץ שני קובצי APK נפרדים, אחד באמצעות Car App Library ואחד באמצעות MediaBrowserService או MediaLibraryService, צריך לבצע את השלבים הבאים כדי לוודא שהיכולות הנכונות של הרכב מוגדרות בצורה נכונה.
כשיוצרים קובץ APK נפרד לגרסה של האפליקציה בספריית האפליקציות לרכב, צריך להגדיר את android.software.car.templates_host.media לערך android:required=true. כך מוודאים שהאפליקציה מופצת רק בגרסאות build של Android Automotive OS שאושרו עם תמיכה במארח של ספריית האפליקציות לרכב.
<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>
בנוסף לשימוש ב-android.software.car.templates_host.media ולהגדרתו ל-android:required=true כמו שמופיע למעלה, צריך לבצע את השלבים הבאים כדי להפעיל את Android Automotive OS לפעילות של ספריית Car App שאפשר להפעיל.
Play Distribution
קובץ ה-APK שמשתמש בספריית האפליקציות לרכב צריך להיות מופץ במסלול הייעודי ל-Automotive OS.
תמיכה בפעולות קוליות
אפשר להוסיף לאפליקציה תמיכה בפקודות קוליות כדי לאפשר למשתמשים לבצע פעולות נפוצות בלי להשתמש בידיים.
במאמר תמיכה בפעולות קוליות למדיה מפורטות הוראות להטמעה. אם יש לכם אפליקציית מדיה שמבוססת על תבנית ואתם מקבלים פקודה קולית, אתם לא צריכים לעדכן את MediaBrowserService או את MediaLibraryService בתוצאות חיפוש. במקום זאת, כדאי להוסיף פעולה לתבנית של הפעלת המדיה כדי לאפשר למשתמש למצוא תוכן נוסף על סמך ההפעלה או שאילתת החיפוש. תמיכה בפקודות קוליות נדרשת כדי לעמוד בהנחיות האיכות של VC-1.
יצירת תבנית ההפעלה
התבנית MediaPlaybackTemplate מציגה מידע על הפעלת מדיה באפליקציית המדיה בספריית האפליקציות לרכב. התבנית הזו מאפשרת להגדיר כותרת עם שם ופעולות שניתנות להתאמה אישית, בזמן שהמידע על המדיה ואמצעי הבקרה להפעלת המדיה מאוכלסים על ידי המארח על סמך המצב של MediaSession באפליקציה.
איור 1:
MediaPlaybackTemplate עם פעולת כותרת לפתיחת התור
בחלק העליון.
בדוגמת הקוד הזו מוצג אופן יצירת תבנית הפעלה לדוגמה שמגדירה פעולת כותרת שמאפשרת למשתמש לעבור למסך עם רשימת השירים.
val playbackTemplate = MediaPlaybackTemplate.Builder()
.setHeader(
Header.Builder()
.setStartHeaderAction(Action.BACK)
.addEndHeaderAction(
Action.Builder()
.setTitle(model.context.getString(R.string.queue_button_title))
.setIcon(
CarIcon.Builder(
IconCompat.createWithResource(
model.context,
R.drawable.gs_queue_music_vd_theme_24,
))
.build())
.setOnClickListener(showQueueScreen())
.build())
.setTitle(model.context.getString(R.string.media_playback_view_title))
.build())
.build()
כשמשתמשים ב-MediaPlaybackTemplate, צריך לרשום טוקן MediaSession באמצעות MediaPlaybackManager ב-CarAppService. אם לא עושים את זה, מוצגת שגיאה כששולחים MediaPlaybackTemplate למארח.
import androidx.car.app.media.MediaPlaybackManager
…
override fun onCreateSession(sessionInfo: SessionInfo): Session {
return object : Session() {
…
init {
lifecycle.addObserver(
LifecycleEventObserver { _, event ->
if (event == ON_CREATE) {
val token = ... // MediaSessionCompat.Token
(carContext.getCarService(CarContext.MEDIA_PLAYBACK_SERVICE) as MediaPlaybackManager)
.registerMediaPlaybackToken(token)
}
...
}
)
}
}
}
.registerMediaPlaybackToken נדרש כדי לחשוף את פרטי ההפעלה של המדיה ואת אמצעי הבקרה שלה ב-Android Auto. הוא חשוב גם כדי שהמארח יוכל ליצור התראות ספציפיות למדיה.
באפליקציות שמשתמשות בספריית Media3, שבה נעשה שימוש ב-PlatformToken במקום ב-MediaSessionCompat.Token רגיל, צריך להטמיע SessionCommand מותאם אישית ב-MediaLibrarySession.Callback שמחזיר את טוקן הפלטפורמה הבסיסי של הסשן: session.platformToken. ב-CarAppService שולחים את הפקודה המותאמת אישית הזו לסשן. אחרי שמקבלים את אסימון הפלטפורמה, ממירים אותו באמצעות MediaSessionCompat.Token.fromToken(platformToken) ומעבירים את אסימון התאימות הזה אל Car App Library ב-.registerMediaPlaybackToken().
ארגון מדיה באמצעות תבניות
כדי לארגן מדיה לצפייה, כמו שירים או אלבומים, מומלץ להשתמש בתג SectionedItemTemplate, שמאפשר להשתמש בתגים GridSection ו-RowSection יחד כדי ליצור פריסות שמשלבות רשימות של תמונות ופריטי טקסט.
איור 2: רכיב SectionedItemTemplate שמכיל רכיב RowSection ואחריו רכיב GridSection
שימוש ב-SectionedItemTemplate בתוך TabTemplate
דרך נוחה לסווג מדיה באפליקציה היא באמצעות התג
SectionedItemTemplate בתוך התג
TabTemplate.
val template =
SectionedItemTemplate.Builder()...build();
val tabTemplate =
TabTemplate.Builder(tabCallback)
.setTabContents(TabContents.Builder(template).build)
.setHeaderAction(Action.APP_ICON)
…
.build();
רכיבים ותכונות של ספריית אפליקציות לרכב 1.9
Car App Library API Version 1.9 כולל רכיבים מותאמים אישית ליכולות גלישה ייחודיות, כמו Chips, Progress Bars, Condensed Items, Interactive and Expanded Header, Spotlight Sections ו-Banners.
איור 3: צורה A
SectionedItemTemplate שמכילה Chips,
Condensed Items, Interactive Header,
Grid Items וMinimized Control Panel
איור 4: שני מסכים של דפדוף במדיה עם הסמלים Expanded Header, Spotlight Sections ו-Progress Bars
מידע נוסף על עיצוב ממשק המשתמש של אפליקציית המדיה באמצעות התבניות האלה זמין במאמר בנושא אפליקציות מדיה.
איך מגיעים לרכיבי ה-UI להפעלה
כשמשתמשים מעיינים בתוכן מדיה, חשוב שהם יוכלו לנווט במהירות אל MediaPlaybackTemplate עם הסחות דעת מינימליות.כדי לעמוד בדרישת האיכות MFT-1, באפליקציה שלכם צריכה להיות דרך לגשת אל MediaPlaybackTemplate מכל מסכי העיון בתוכן מדיה.
אם אתם משתמשים ב-SectionedItemTemplate, תוכלו להוסיף לחצן פעולה שיעביר אתכם למסך הפעלת המדיה. משתמשים בפעולה Action.MEDIA_PLAYBACK הרגילה של ספריית האפליקציות לרכב. אפליקציית מדיה תציג את הפעולה הזו כלוח בקרה ממוזער, שנדרש כדי לעמוד בדרישת האיכות MFT-1 אם משתמשים ב-Car App Library API בגרסה 1.9 ואילך. בתבניות אחרות, אפשר להשתמש בפעולת כותרת כדי להשיג את אותה תוצאה.
טיפול ב-Intents של הפעלת מדיה במערכת
כשמפעילים אפליקציה מממשק מערכת להפעלת מדיה, כמו כרטיס מדיה, צריך להפנות את המשתמש אל MediaPlaybackTemplate. אנחנו דורשים מאפליקציות מדיה לטפל בזה Intent Action בסדר הזה
כדי לספק למשתמשים חוויה חלקה.
מוסיפים את פעולת androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK ל-intent-filter של רכיב Car App Library (או CarAppActivity או Activity).
צריך לוודא שהפעילות משתמשת ב-launchMode של singleTask או singleTop כדי להפעיל את onNewIntent().
<activity
android:name=".LaunchableTrampoline"
android:exported="true"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:launchMode="singleTask"
android:label="@string/app_name_cal"
android:enabled="false">
<meta-data android:name="distractionOptimized" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
במחלקת Session, מחליפים את onNewIntent() כדי לנתח את ה-Intent הנכנס.
אם פעולת ה-Intent הנכנס תואמת ל-SHOW_MEDIA_PLAYBACK, מעבירים את המשתמש למסך 'הפעלת תוכן'.
@Override
public void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent);
if (SHOW_MEDIA_PLAYBACK.equals(intent.getAction())) {
ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
// Avoid redundant navigation if already on the playing screen
if (screenManager.getTop() instanceof MyMediaPlayScreen) {
return;
}
screenManager.push(MyMediaPlayScreen.createScreenFromPlaying(
getCarContext(), mMediaSessionController));
}
}
אם אתם משתמשים בפעילות מסוג trampoline, צריך לבדוק את פעולת ה-Intent בתוך onCreate(). מעבירים את הפעולה הזו אל ה-Intent ליצירת CarAppActivity לפני שקוראים ל-finish().
public class LaunchableTrampoline extends AppCompatActivity {
private static final String SHOW_MEDIA_PLAYBACK = "androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent receivedIntent = getIntent();
String action;
if (SHOW_MEDIA_PLAYBACK.equals(receivedIntent.getAction())) {
action = SHOW_MEDIA_PLAYBACK;
} else {
action = Intent.ACTION_MAIN;
}
Intent intent = new Intent(action);
intent.setClassName(getPackageName(), "androidx.car.app.activity.CarAppActivity");
startActivity(intent);
finish();
}
}