使用汽车应用库模板的媒体应用可以自定义媒体浏览和播放体验,同时确保体验针对汽车屏幕进行了优化,并最大限度地减少驾驶时的干扰。
本指南假定您已经拥有可在手机上播放音频的媒体应用
并且您的媒体应用遵循 Android 媒体应用架构。汽车应用库让您能够使用
模板替换应用内体验,而不是使用构建车载媒体应用
MediaBrowser数据结构构建的体验。您仍然必须为播放控件提供 MediaSession,以及为推荐和其他智能体验提供 MediaBrowserService 或 MediaLibraryService。
配置应用的清单
除了使用 Android for Cars 应用库中所述的步骤之外,模板化媒体应用还必须满足以下 要求:
在清单中声明类别支持
应用需要在其 CarAppService 的 intent
过滤器中声明 androidx.car.app.category.MEDIA
汽车应用类别。
<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>
提供提供方图标
请务必为使用汽车 应用库构建的媒体应用添加提供方图标。
声明 Android Auto 支持
确保应用的清单中包含以下内容:
<application>
...
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
...
</application>
然后,将 template 声明添加到 xml 资源中的 automotive_app_desc.xml。它应如下所示:
<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
<uses name="media"/>
<uses name="template"/>
</automotiveApp>
声明 Android Automotive OS 支持
您可以通过两种不同的方式在 Android Automotive OS 上分发启用了汽车应用库的媒体应用:以单个 APK 的形式分发,或以两个单独的 APK 的形式分发。如果您分发单个 APK,它将支持启用了汽车应用库主机的 Android Automotive OS 车辆,如果未启用,则会回退到 MediaBrowserService 或 MediaLibraryService 应用,即使对于较旧的 Android 版本(Android 10 - Android 13)也是如此。如果您选择分发两个单独的 APK,则可以更轻松地更新汽车应用库版本中的新添加内容,而不必担心影响应用的 MediaBrowserService 或 MediaLibraryService 版本。
分发单个 APK
在为应用的汽车应用库和 MediaBrowserService
或 MediaLibraryService 版本分发单个 APK 时,请务必将
"android:required="false".
<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>
接下来,请遵循 AAOS 的汽车应用库指南,并
引入可启动的 CarAppActivity(或 trampoline activity)。您必须在清单中将 activity 设置为 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(一个使用汽车应用库,另一个使用 MediaBrowserService 或 MediaLibraryService),请按照以下步骤操作,以确保正确指定目标车辆功能。
为应用的汽车应用库版本创建单独的 APK 时,您必须将 android.software.car.templates_host.media 设置为 android:required=true。这样可确保应用仅在经过认证支持汽车应用库主机的 Android Automotive OS build 上分发。
<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>
除了使用 android.software.car.templates_host.media 并将其设置为
android:required=true(如上所述)之外,请按照以下步骤为可启动的汽车应用库 activity 启用 Android Automotive OS
。
Play 分发
使用汽车应用库的 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 时,请在
CarAppService 中使用 MediaPlaybackManager 注册 a
MediaSession 令牌。否则,当 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),您需要在 MediaLibrarySession.Callback 中实现自定义 SessionCommand,该命令会返回会话的基础平台令牌:session.platformToken。在 CarAppService 中,将此自定义命令发送到会话。收到平台令牌后,使用
MediaSessionCompat.Token.fromToken(platformToken)将其转换为兼容令牌,并将此兼容令牌
传递给汽车应用库,以便在.registerMediaPlaybackToken()中使用。
使用模板整理媒体
如需整理媒体以供浏览(例如歌曲或专辑),我们建议使用
SectionedItemTemplate,
该模板可让您将 GridSection 和
RowSection 结合使用,以创建混合了图片列表
和文本项的布局。
图 2: 一个 SectionedItemTemplate,其中包含一个 RowSection,后跟一个 GridSection
在 TabTemplate 中使用 SectionedItemTemplate
在应用中对媒体进行分类的一种便捷方式是使用
SectionedItemTemplate,在
TabTemplate中。
val template =
SectionedItemTemplate.Builder()...build();
val tabTemplate =
TabTemplate.Builder(tabCallback)
.setTabContents(TabContents.Builder(template).build)
.setHeaderAction(Action.APP_ICON)
…
.build();
汽车应用库 1.9 组件和功能
汽车应用库 API 1.9 版引入了自定义 组件,用于提供独特的浏览功能,例如 Chip、 进度条、精简项、 互动式和展开式标头、 焦点部分和横幅。
图 3: 一个 SectionedItemTemplate,其中包含 Chips、Condensed Items、一个 Interactive Header、Grid Items 和一个 Minimized Control Panel
图 4: 两个媒体浏览界面,其中包含 Expanded Header、Spotlight Sections 和 Progress Bars
如需详细了解如何使用这些 模板设计媒体应用的界面,请参阅媒体应用。
导航到播放控件
浏览媒体时,用户能够以最小的干扰快速
导航到 MediaPlaybackTemplate 非常重要。为了满足
MFT-1质量要求,您的应用必须提供一种从所有媒体浏览界面访问
MediaPlaybackTemplate的方式。
如果您使用的是 SectionedItemTemplate,则可以通过添加一个操作按钮来实现此目的,该按钮可将您导航到媒体播放界面。使用标准汽车应用库 Action.MEDIA_PLAYBACK 操作。媒体应用会将此操作显示为“最小化控制面板”,如果您使用的是汽车应用库 API 1.9 或更高版本,则必须满足 MFT-1 质量要求。对于其他模板,标头操作是实现此目的的另一种方式。
处理系统媒体播放 intent
当应用从系统播放媒体界面(例如媒体卡片)启动时,必须将用户定向到 MediaPlaybackTemplate。我们要求媒体应用处理此 Intent Action,以便
为用户提供顺畅的体验。
将 androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK 操作添加到
汽车应用库组件(CarAppActivity 或
trampoline Activity)的 intent 过滤器。
确保您的 activity 使用 singleTask 或 singleTop 的 launchMode,以便调用 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 activity,请在 onCreate() 中检查 intent 操作。在调用 finish() 之前,将此操作传递给 CarAppActivity 创建 intent。
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();
}
}