車輛專用 Android App Library 可讓您使用導航、搜尋點和物聯網 (IoT) 應用程式傳送到車上 我們提供一系列專用範本,協助駕駛人分心 也會妥善保護車輛螢幕等細節 以及輸入模式
本指南將概略說明程式庫的主要功能與概念,以及 會逐步引導您設定基本應用程式。
事前準備
- 查看行車使用者體驗設計指南
涵蓋 Car App Library 的頁面
- 導航應用程式 和其他與駕駛相關的應用程式 類別總覽
- 「使用範本建構應用程式」總覽
- 構成元素 涵蓋範本和範本元件
- 流程範例 呈現常見的使用者體驗模式
- 範本應用程式規定
- 請參閱下文的重要詞彙及概念 專區。
- 熟悉 Android Auto 系統 使用者介面 和 Android Automotive OS 設計
- 查看版本資訊。
- 查看範例。
重要詞彙與概念
- 模型和範本
- 使用者介面以模型物件圖表表示 依照範本允許,以不同方式排列在一起 。範本是模型的子集,可在這些模型中做為根層級 圖表。模型會包含要在 文字、圖片形式,以及設定 這類資訊的視覺外觀 (例如文字顏色或圖片) 大小。主機將模型轉換為符合 駕駛人分心等級的標準,並會妥善處理各項細節,例如 例如車輛螢幕因素和輸入模式
- 舉辦派對
- 主機是後端元件,負責實作所列功能 ,以便您的應用程式可在車上執行。 主機的職責包括發掘應用程式及管理 也就是將模型轉換為檢視畫面,並通知應用程式 以及使用者的互動方式在行動裝置上,這個主機是由 Android 實作 自動、在 Android Automotive OS 中,這個主機已安裝為系統應用程式。
- 範本限制
- 不同的範本會強制限制模型的內容。適用對象 例如,清單範本對可儲存的項目數量設有限制 都顯示給使用者範本也設有限制 形成任務的流程舉例來說,應用程式只能推送 最多可在畫面堆疊中加入五個範本詳情請見 範本限制。
Screen
Screen
是由 應用程式實作程式庫,管理呈現給 內容。Screen
具有 生命週期,並為應用程式提供 傳送要在螢幕顯示時顯示的範本。 您也能推送Screen
個執行個體 並彈出Screen
堆疊 確保遵循 範本流程限制。CarAppService
CarAppService
是 應用程式的抽象Service
類別 必須實作並匯出,才能由主機尋找及管理。 應用程式的CarAppService
為 會負責驗證主機連線是否可信任createHostValidator
然後提供Session
每個連線的執行個體數量onCreateSession
。Session
Session
是一種抽象類別, 您的應用程式必須使用CarAppService.onCreateSession
。 這可做為在車輛螢幕上顯示資訊的進入點。這項服務 設有生命週期 應用程式目前在車輛螢幕上的狀態,例如應用程式 已隱藏或隱藏。當
Session
啟動時,例如 應用程式初次啟動時,初始的主機要求Screen
,以使用onCreateScreen
敬上 方法。
安裝 Car App Library
查看 Jetpack 程式庫 發布頁面 瞭解如何將程式庫新增至應用程式。
設定應用程式的資訊清單檔案
建立車用應用程式前,請先設定應用程式的 資訊清單檔案,如下所示。
宣告您的 CarAppService
主機會透過
CarAppService
實作。個人中心
在資訊清單中宣告這項服務,讓主機探索並連線
導入您的應用程式
您也必須在
應用程式的 <category>
元素
意圖篩選器請參閱
支援的應用程式類別 (用來指定
這個元素
下列程式碼片段說明如何為 一些興趣應用程式:
<application>
...
<service
...
android:name=".MyCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.POI"/>
</intent-filter>
</service>
...
<application>
支援的應用程式類別
新增下列一或多個類別以宣告您的應用程式類別
您依上述說明宣告 CarAppService
時,意圖篩選器中的值
在上一節中:
androidx.car.app.category.NAVIGATION
:提供即時路線導航功能的應用程式 導航路線 請參閱「建構車輛專用導航應用程式」一文 ,進一步瞭解這個類別的其他文件。androidx.car.app.category.POI
:提供相關功能的應用程式 例如停車位、充電站和 加油站。退房日 建構車輛專用搜尋點應用程式 更多與這個類別相關的說明文件androidx.car.app.category.IOT
:應用程式可讓使用者根據相關主題 在車內對已連結裝置上的動作執行動作。退房日 建構車輛專用物聯網應用程式 更多與這個類別相關的說明文件
請參閱「車用 Android 應用程式品質指南」 各類別及其所屬應用程式的詳細說明。
指定應用程式名稱和圖示
您必須指定應用程式名稱和圖示,讓主機能用來代表 開始定義應用程式
您可以使用以下項目,指定要用於代表應用程式的應用程式名稱和圖示:
label
和
您的 icon
屬性
CarAppService
:
...
<service
android:name=".MyCarAppService"
android:exported="true"
android:label="@string/my_app_name"
android:icon="@drawable/my_app_icon">
...
</service>
...
如未在
<service>
元素,主機
回復為指定值
<application>
元素。
設定自訂主題
如要設定車用應用程式的自訂主題,請新增
<meta-data>
元素
資訊清單檔案如下所示:
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
然後宣告樣式資源 為您的自訂車輛應用程式主題設定下列屬性:
<resources> <style name="MyCarAppTheme"> <item name="carColorPrimary">@layout/my_primary_car_color</item> <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item> <item name="carColorSecondary">@layout/my_secondary_car_color</item> <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
車輛應用程式 API 級別
Car App Library 會定義自己的 API 級別,您可以知道
車上的範本主機支援程式庫功能。
如要擷取主機支援的最高 Car App API 級別,請使用
getCarAppApiLevel()
敬上
方法。
在您的應用程式中,宣告應用程式支援的最低 Car App API 級別
AndroidManifest.xml
檔案:
<manifest ...>
<application ...>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>
</application>
</manifest>
詳情請參閱
RequiresCarApi
敬上
註解,說明如何維持回溯相容性及宣告
使用某項功能所需的最低 API 級別。如要瞭解
需要等級,才能使用 Car App Library 的特定功能,請查看
參考文件
CarAppApiLevels
。
建立 CarAppService 和工作階段
您的應用程式需要擴充
CarAppService
類別並實作
其
onCreateSession
。
方法,此方法會傳回 Session
目前與主機連線相對應的執行個體:
Kotlin
class HelloWorldService : CarAppService() { ... override fun onCreateSession(): Session { return HelloWorldSession() } ... }
Java
public final class HelloWorldService extends CarAppService { ... @Override @NonNull public Session onCreateSession() { return new HelloWorldSession(); } ... }
Session
執行個體負責
傳回 Screen
例項,藉此使用
。
Kotlin
class HelloWorldSession : Session() { ... override fun onCreateScreen(intent: Intent): Screen { return HelloWorldScreen(carContext) } ... }
Java
public final class HelloWorldSession extends Session { ... @Override @NonNull public Screen onCreateScreen(@NonNull Intent intent) { return new HelloWorldScreen(getCarContext()); } ... }
處理車輛應用程式需要從非螢幕啟動的情況
那麼應用程式的首頁或到達畫面 (例如處理深層連結) 即可
預先種子的螢幕返回堆疊
ScreenManager.push
敬上
之前,從
onCreateScreen
。
預先播映功能可讓使用者從第一個畫面返回上一個畫面
顯示您的應用程式。
建立開始畫面
您可以定義
Screen
類別並實作其類別
onGetTemplate
方法,以便傳回
Template
執行個體代表的
要在車輛螢幕上顯示的 UI 狀態。
下列程式碼片段說明如何宣告
Screen
採用
將「PaneTemplate
」範本套用至
顯示簡單的「Hello World!」字串:
Kotlin
class HelloWorldScreen(carContext: CarContext) : Screen(carContext) { override fun onGetTemplate(): Template { val row = Row.Builder().setTitle("Hello world!").build() val pane = Pane.Builder().addRow(row).build() return PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build() } }
Java
public class HelloWorldScreen extends Screen { @NonNull @Override public Template onGetTemplate() { Row row = new Row.Builder().setTitle("Hello world!").build(); Pane pane = new Pane.Builder().addRow(row).build(); return new PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build(); } }
CarContext 類別
CarContext
類別是
ContextWrapper
子類別
您的 Session
和
Screen
執行個體。這項服務提供存取權
例如
ScreenManager
用於管理
畫面堆疊;這個
AppManager
(適用於一般應用程式相關)
功能,例如存取繪圖地圖的 Surface
物件;
和 NavigationManager
即時路況導航應用程式用來傳達導航資訊
中繼資料和其他
導航相關
活動
。
請參閱存取導覽 範本 導航應用程式可用的程式庫功能完整清單。
CarContext
還提供其他
例如讓您透過設定載入可繪項目資源
使用意圖在車內啟動應用程式
並信號指出應用程式是否應以深色主題顯示地圖。
實作畫面導覽
應用程式通常會顯示多種不同的畫面,且每個畫面可能都使用 使用者可以在互動時瀏覽的不同範本 在畫面上顯示的介面
ScreenManager
類別提供
螢幕堆疊,用於推送可自動彈出的畫面
使用者在車輛螢幕上選取返回按鈕,或使用硬體背面時
按鈕。
下列程式碼片段說明如何在訊息範本中新增返回動作: 以及會促使使用者選取新畫面的動作:
Kotlin
val template = MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( Action.Builder() .setTitle("Next screen") .setOnClickListener { screenManager.push(NextScreen(carContext)) } .build()) .build()
Java
MessageTemplate template = new MessageTemplate.Builder("Hello world!") .setHeaderAction(Action.BACK) .addAction( new Action.Builder() .setTitle("Next screen") .setOnClickListener( () -> getScreenManager().push(new NextScreen(getCarContext()))) .build()) .build();
Action.BACK
物件是
標準 Action
,系統會自動
叫用 ScreenManager.pop
。
您可以使用
OnBackPressedDispatcher
敬上
執行個體
CarContext
。
為確保在行車期間安全使用應用程式,畫面堆疊的 最高 與五台螢幕結合請參閱範本限制 一節。
重新整理範本內容
應用程式可以要求
Screen
,以呼叫
Screen.invalidate
方法。
主機隨後又呼叫了應用程式的
Screen.onGetTemplate
敬上
方法,擷取含有新內容的範本。
重新整理 Screen
時,
請務必瞭解範本中可更新的具體內容
因此主機不會將新範本計入範本配額。
詳情請參閱「範本限制」一節。
建議你妥善規劃畫面,確保畫面呈現單一畫面
對應的 Screen
和類型
並透過 onGetTemplate
實作傳回的範本
繪製地圖
使用以下範本的導航和搜尋點 (POI) 應用程式,可以
透過存取 Surface
繪製地圖:
Template | 範本權限 | 類別指南 |
---|---|---|
NavigationTemplate |
androidx.car.app.NAVIGATION_TEMPLATES |
導覽 |
MapWithContentTemplate |
androidx.car.app.NAVIGATION_TEMPLATES 或 androidx.car.app.MAP_TEMPLATES |
導航、搜尋點 |
MapTemplate (已淘汰) |
androidx.car.app.NAVIGATION_TEMPLATES |
導覽 |
PlaceListNavigationTemplate (已淘汰) |
androidx.car.app.NAVIGATION_TEMPLATES |
導覽 |
RoutePreviewNavigationTemplate (已淘汰) |
androidx.car.app.NAVIGATION_TEMPLATES |
導覽 |
宣告表面權限
除了應用程式使用範本所需的權限外,
應用程式必須在應用程式內宣告 androidx.car.app.ACCESS_SURFACE
權限
AndroidManifest.xml
檔案可存取介面:
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
存取介面
如要存取主機提供的 Surface
,您必須
SurfaceCallback
,並提供
新增至 AppManager
汽車維修服務。目前的Surface
會傳遞至
SurfaceCallback
中的 SurfaceContainer
參數
onSurfaceAvailable()
和 onSurfaceDestroyed()
回呼。
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
瞭解平面的可見區域
主機可在地圖上層為範本繪製使用者介面元素。主機會傳遞不保證表面區域的區域
透過呼叫
SurfaceCallback.onVisibleAreaChanged
敬上
方法。此外,為了盡量減少變更,主機會透過最小矩形呼叫 SurfaceCallback.onStableAreaChanged
方法,這種矩形一律會根據目前的範本顯示。
舉例來說,如果導航應用程式使用
NavigationTemplate
敬上
在畫面頂端有動作列,即可隱藏動作列
當使用者有一段時間未與畫面互動來請求其他報表時
也就是地圖空間在此情況下,系統會回呼 onStableAreaChanged
並
具有相同矩形的 onVisibleAreaChanged
。操作列隱藏時
只有較大的區域呼叫 onVisibleAreaChanged
。如果使用者
與螢幕互動,然後再次呼叫 onVisibleAreaChanged
第一個矩形
支援深色主題
應用程式必須在 Surface
例項中,以適當的深色重新繪製地圖
如下所述:
車用 Android 應用程式品質。
如要判斷是否應繪製深色地圖,可以使用 CarContext.isDarkMode
方法。每當深色主題狀態變更時,您就會收到
Session.onCarConfigurationChanged
。
允許使用者與地圖互動
使用以下範本時,您可以新增使用者支援服務 例如讓您可以瀏覽地圖的不同部分 以及縮放和平移功能
Template | 開始支援互動功能的 Car App API 級別 |
---|---|
NavigationTemplate |
2 |
PlaceListNavigationTemplate (已淘汰) |
4 |
RoutePreviewNavigationTemplate (已淘汰) |
4 |
MapTemplate (已淘汰) |
5 (導入範本) |
MapWithContentTemplate |
7 (導入範本) |
實作互動回呼
SurfaceCallback
介面
有多種回呼方法可實作為建構的地圖添加互動性
換成上一節的範本:
互動 | SurfaceCallback 方法 |
開始支援的 Car App API 級別 |
---|---|---|
輕觸 | onClick |
5 |
雙指撥動縮放 | onScale |
2 |
單點觸控拖曳 | onScroll |
2 |
單點觸控滑動 | onFling |
2 |
輕觸兩下 | onScale (由範本主機決定縮放比例係數) |
2 |
平移模式的旋轉自動提醒 | onScroll (由範本主機決定距離係數) |
2 |
新增地圖動作區域
這些範本可包含地圖操作列,用於地圖相關動作,例如: 縮放畫面、重新置中、顯示指南針等 決定要顯示的內容地圖動作列最多可有四個純圖示按鈕 就能在不影響工作深度的情況下重新整理。處於閒置狀態時隱藏起來 就會重新顯示為啟用狀態
如要接收地圖互動回呼,
「必須」在地圖動作列中加入 Action.PAN
按鈕。當使用者
按下平移按鈕,主機會進入平移模式,如下所述
專區。
如果應用程式忽略地圖動作列中的 Action.PAN
的按鈕,就無法從 SurfaceCallback
方法收到使用者輸入內容,而且主機會結束先前啟用的平移模式。
如果是觸控螢幕,畫面上不會顯示平移按鈕。
瞭解平移模式
在平移模式下,使用者透過非觸控輸入裝置 (例如旋轉控制器和觸控板) 輸入的內容,會由範本主機轉譯成適當的 SurfaceCallback
方法。使用 NavigationTemplate.Builder
中的 setPanModeListener
方法,可在使用者進入或結束平移模式時做出反應。使用者處於平移模式時,主機可隱藏範本中的其他 UI 元件。
與使用者互動
您的應用程式可以透過與行動應用程式類似的模式與使用者互動。
處理使用者輸入內容
應用程式可以藉由傳遞適當的事件監聽器給
支援生成式 AI 模型下列程式碼片段說明如何建立
Action
模型,可設定
符合此條件的 OnClickListener
會傳回應用程式程式碼定義的方法:
Kotlin
val action = Action.Builder() .setTitle("Navigate") .setOnClickListener(::onClickNavigate) .build()
Java
Action action = new Action.Builder() .setTitle("Navigate") .setOnClickListener(this::onClickNavigate) .build();
接著,onClickNavigate
方法可以啟動
預設車用導航應用程式
方法是使用
CarContext.startCarApp
。
方法:
Kotlin
private fun onClickNavigate() { val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)) carContext.startCarApp(intent) }
Java
private void onClickNavigate() { Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address)); getCarContext().startCarApp(intent); }
如要進一步瞭解如何啟動應用程式,包括
ACTION_NAVIGATE
意圖,請參閱「使用意圖啟動車輛應用程式」
專區。
請引導使用者繼續操作,
的行動裝置互動方式。
您可以使用
ParkedOnlyOnClickListener
敬上
實際執行這些動作如果不停車,主辦人會顯示
告知使用者在此情況下,該動作不允許該動作。如果車輛
才能正常執行下列程式碼片段說明如何
使用ParkedOnlyOnClickListener
如何在行動裝置上開啟設定畫面:
Kotlin
val row = Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone)) .build()
Java
Row row = new Row.Builder() .setTitle("Open Settings") .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)) .build();
顯示通知
傳送到行動裝置的通知只會顯示在符合以下條件的情況下,於車輛螢幕上顯示
擴充功能會使用
CarAppExtender
。
部分通知屬性,例如內容標題、文字、圖示和動作
可在 CarAppExtender
中設定,並覆寫通知屬性
。
下列程式碼片段說明如何傳送通知到有 顯示的標題與行動裝置上顯示的標題不同:
Kotlin
val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(titleOnThePhone) .extend( CarAppExtender.Builder() .setContentTitle(titleOnTheCar) ... .build()) .build()
Java
Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(titleOnThePhone) .extend( new CarAppExtender.Builder() .setContentTitle(titleOnTheCar) ... .build()) .build();
通知可能會影響使用者介面的以下部分:
- 系統可能會向使用者顯示抬頭通知 (HUN)。
- 您可在通知中心新增一個項目,並視需要顯示標記 就可以在邊欄看到
- 如果是導航應用程式,通知可能會在邊欄小工具中顯示為: 描述 即時路線導航通知。
您可以選擇如何設定應用程式通知,對不同的
和這些使用者介面元素搭配使用
的
CarAppExtender
敬上
說明文件。
如果
NotificationCompat.Builder.setOnlyAlertOnce
敬上
系統會使用 true
的值呼叫,而高優先順序的通知會顯示為
只能執行一次。
如要進一步瞭解如何設計車用應用程式的通知,請參閱 Google Design for Driver 指南 通知:
顯示浮動式訊息
您的應用程式可以使用以下功能顯示浮動式訊息:
CarToast
,如以下程式碼片段所示:
Kotlin
CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()
Java
CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();
要求權限
如果您的應用程式需要存取受限資料或動作 (例如:
位置—Android 標準規則
權限
或如要申請權限,請使用
CarContext.requestPermissions()
敬上
方法。
使用
CarContext.requestPermissions()
,而非
標準 Android API
因此您不必自行啟動 Activity
建立權限對話方塊此外,您可以在
不必建立 Android Auto 和 Android Automotive OS
平台導向的流程
設定 Android Auto 權限對話方塊的樣式
Android Auto 會在手機上顯示使用者的權限對話方塊。
根據預設,對話方塊後不會有任何背景。如何設定自訂的
請在背景中宣告車用應用程式主題
AndroidManifest.xml
檔案並設定 carPermissionActivityLayout
屬性
打造最適合你的車用應用程式主題
<meta-data android:name="androidx.car.app.theme" android:resource="@style/MyCarAppTheme />
然後,設定車輛應用程式主題的 carPermissionActivityLayout
屬性:
<resources> <style name="MyCarAppTheme"> <item name="carPermissionActivityLayout">@layout/my_custom_background</item> </style> </resources>
使用意圖啟動車用應用程式
您可以呼叫
CarContext.startCarApp
敬上
方法執行下列任一動作:
- 開啟撥號程式即可撥打電話。
- 使用 預設車用導航應用程式。
- 使用意圖啟動自己的應用程式。
以下範例說明如何建立含有
會開啟應用程式,並顯示預訂停車位詳細資料的畫面。
您可以利用包含
PendingIntent
包裝
吸引使用者註意
Kotlin
val notification = notificationBuilder ... .extend( CarAppExtender.Builder() .setContentIntent( PendingIntent.getBroadcast( context, ACTION_VIEW_PARKING_RESERVATION.hashCode(), Intent(ACTION_VIEW_PARKING_RESERVATION) .setComponent(ComponentName(context, MyNotificationReceiver::class.java)), 0)) .build())
Java
Notification notification = notificationBuilder ... .extend( new CarAppExtender.Builder() .setContentIntent( PendingIntent.getBroadcast( context, ACTION_VIEW_PARKING_RESERVATION.hashCode(), new Intent(ACTION_VIEW_PARKING_RESERVATION) .setComponent(new ComponentName(context, MyNotificationReceiver.class)), 0)) .build());
此外,應用程式也必須宣告
BroadcastReceiver
,即
當使用者在
通知介面和叫用
CarContext.startCarApp
。
具有包含資料 URI 的意圖:
Kotlin
class MyNotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val intentAction = intent.action if (ACTION_VIEW_PARKING_RESERVATION == intentAction) { CarContext.startCarApp( intent, Intent(Intent.ACTION_VIEW) .setComponent(ComponentName(context, MyCarAppService::class.java)) .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction))) } } }
Java
public class MyNotificationReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String intentAction = intent.getAction(); if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) { CarContext.startCarApp( intent, new Intent(Intent.ACTION_VIEW) .setComponent(new ComponentName(context, MyCarAppService.class)) .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction))); } } }
最後,
Session.onNewIntent
敬上
方法,藉由推送停車預訂畫面來處理這項意圖
位置 (如果不是位於最上層):
Kotlin
override fun onNewIntent(intent: Intent) { val screenManager = carContext.getCarService(ScreenManager::class.java) val uri = intent.data if (uri != null && MY_URI_SCHEME == uri.scheme && MY_URI_HOST == uri.schemeSpecificPart && ACTION_VIEW_PARKING_RESERVATION == uri.fragment ) { val top = screenManager.top if (top !is ParkingReservationScreen) { screenManager.push(ParkingReservationScreen(carContext)) } } }
Java
@Override public void onNewIntent(@NonNull Intent intent) { ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class); Uri uri = intent.getData(); if (uri != null && MY_URI_SCHEME.equals(uri.getScheme()) && MY_URI_HOST.equals(uri.getSchemeSpecificPart()) && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment()) ) { Screen top = screenManager.getTop(); if (!(top instanceof ParkingReservationScreen)) { screenManager.push(new ParkingReservationScreen(getCarContext())); } } }
詳情請參閱「顯示通知」一節 瞭解如何處理車用應用程式通知。
範本限制
主機將指定工作可顯示的範本數量限制為上限 ,且最後一個範本必須是下列其中一種類型:
請注意,這項限制適用於範本數量,不適用於
堆疊中的 Screen
執行個體。適用對象
例如,如果應用程式在畫面 A 中傳送兩個範本,然後推送螢幕
B,現在可以再傳送三個範本。或者,如果每個畫面是結構化的
傳送單一範本,應用程式就能將五個畫面執行個體推送到
ScreenManager
堆疊。
這些限制有一些特殊情況:範本重新整理、返回和 重設作業
範本重新整理作業
某些內容更新不會計入範本限制。一般來說
應用程式推送相同類型的新範本,且包含
與前一個範本的主要內容相同,新範本並不是
也會計入配額例如,更新某個資料列中某列的切換狀態
ListTemplate
不會計入
依據配額詳情請參閱個別範本的說明文件
哪類內容更新可視為重新整理。
返回作業
如要在工作中啟用子流程,主機會偵測應用程式何時彈出
來自 ScreenManager
堆疊和更新的 Screen
根據應用程式即將使用的範本數量
計算剩餘的配額
反向排序
舉例來說,如果應用程式在畫面 A 中傳送兩個範本, 畫面 B 並另外傳送兩個範本,但應用程式只剩一項配額。如果 應用程式回到畫面 A 後,主機會將配額重設為 3,因為 應用程式會回溯使用兩個範本
請注意,當您彈出至螢幕時,應用程式必須傳送 以及該畫面最後傳送的相同類型傳送任何其他郵件 範本類型會導致錯誤。不過,只要型別仍 在返回作業期間相同,應用程式可以自由修改 而且不會影響配額
重設作業
某些範本具有特殊語意,代表工作的結尾。適用對象
舉例來說,
NavigationTemplate
敬上
應該會持續顯示在畫面上,而且會重新整理新的檢視畫面
方便使用者掌握使用情況的交通路線達到以下任一情況時
主機會重設範本配額,
是新任務的第一步這可讓應用程式開始新的工作。
請參閱個別範本的說明文件,瞭解哪些範本會觸發重設作業
。
如果主機收到透過通知動作啟動應用程式的意圖,或 也會重設配額。這個機制可讓應用程式 應用程式必須從通知啟動新的工作流程,即使應用程式 進入前景並鎖定位置。
詳情請參閱「顯示通知」一節 如何在車輛螢幕中顯示應用程式通知。詳情請參閱 「使用意圖啟動車用應用程式」一節將說明如何操作 透過通知動作啟動應用程式
連線 API
您可以判斷應用程式是否在 Android Auto 或 Android 上執行
使用
CarConnection
API 可
在執行階段擷取連線資訊
例如,在車用應用程式的 Session
中初始化 CarConnection
,並
訂閱 LiveData
的更新:
Kotlin
CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)
Java
new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);
在觀察器中,您可以回應連線狀態的變更:
Kotlin
fun onConnectionStateUpdated(connectionState: Int) { val message = when(connectionState) { CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit" CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS" CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto" else -> "Unknown car connection type" } CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show() }
Java
private void onConnectionStateUpdated(int connectionState) { String message; switch(connectionState) { case CarConnection.CONNECTION_TYPE_NOT_CONNECTED: message = "Not connected to a head unit"; break; case CarConnection.CONNECTION_TYPE_NATIVE: message = "Connected to Android Automotive OS"; break; case CarConnection.CONNECTION_TYPE_PROJECTION: message = "Connected to Android Auto"; break; default: message = "Unknown car connection type"; break; } CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show(); }
限制 API
不同車輛可能會允許使用不同數量
Item
執行個體
指定時間使用
ConstraintManager
敬上
在執行階段檢查內容限制,並設定合適的項目數量
首先,從 CarContext
取得 ConstraintManager
:
Kotlin
val manager = carContext.getCarService(ConstraintManager::class.java)
Java
ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
接著,您可以查詢已擷取的 ConstraintManager
物件來取得相關
內容限制。舉例來說,如要取得可在
格線、呼叫
getContentLimit
敬上
同時
CONTENT_LIMIT_TYPE_GRID
:
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
新增登入流程
如果您的應用程式為使用者提供登入體驗,您可以使用下列範本:
SignInTemplate
和 LongMessageTemplate
使用 Car App API 級別 2 以上,即可處理在
車用運算主機
如要建立 SignInTemplate
,請定義 SignInMethod
。車輛
應用程式程式庫目前支援下列登入方式:
InputSignInMethod
敬上 用於登入使用者名稱/密碼。PinSignInMethod
敬上 登入 PIN 碼,使用者會從手機連結自己的帳戶 輸入車用運算主機上顯示的 PIN 碼ProviderSignInMethod
敬上 供供應商登入,例如 Google 登入 和 One Tap。QRCodeSignInMethod
敬上 登入 QR code,使用者只要掃描 QR code 即可完成登入 他們的手機。適用於 Car API 級別 4 及以上級別。
舉例來說,如要實作收集使用者密碼的範本,首先應從
建立InputCallback
來處理及驗證使用者輸入內容:
Kotlin
val callback = object : InputCallback { override fun onInputSubmitted(text: String) { // You will receive this callback when the user presses Enter on the keyboard. } override fun onInputTextChanged(text: String) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } }
Java
InputCallback callback = new InputCallback() { @Override public void onInputSubmitted(@NonNull String text) { // You will receive this callback when the user presses Enter on the keyboard. } @Override public void onInputTextChanged(@NonNull String text) { // You will receive this callback as the user is typing. The update // frequency is determined by the host. } };
必須為 InputSignInMethod
Builder
設定 InputCallback
。
Kotlin
val passwordInput = InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build()
Java
InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback) .setHint("Password") .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD) ... .build();
最後,使用新的 InputSignInMethod
建立 SignInTemplate
。
Kotlin
SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build()
Java
new SignInTemplate.Builder(passwordInput) .setTitle("Sign in with username and password") .setInstructions("Enter your password") .setHeaderAction(Action.BACK) ... .build();
使用 AccountManager
設有驗證機制的 Android Automotive OS 應用程式 AccountManager 的原因如下:
- 提供更優質的使用者體驗和簡便的帳戶管理功能:使用者可輕鬆管理所有應用程式 透過系統設定的帳戶選單 (包括登入相關帳戶) 存取帳戶 然後再登出
- 「訪客」體驗:由於車輛是共用裝置,因此原始設備製造商 (OEM) 可以 訪客體驗,無法新增帳戶。
新增文字字串變化版本
不同大小的車輛螢幕顯示的文字量可能不同。使用 Car App API
第 2 級以上,您可以指定多個文字字串變化版本,以達到最佳效果
配合螢幕大小如要瞭解文字變化版本的適用地區,請查看範本並
採用 CarText
的元件
您可以使用以下內容,在 CarText
中新增文字字串變化版本:
CarText.Builder.addVariant()
方法:
Kotlin
val itemTitle = CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build()
Java
CarText itemTitle = new CarText.Builder("This is a very long string") .addVariant("Shorter string") ... .build();
接著,您可以使用這個 CarText
,例如
GridItem
。
Kotlin
GridItem.Builder() .addTitle(itemTitle) ... .build()
Java
new GridItem.Builder() .addTitle(itemTitle) ... build();
按照偏好由高到低的順序新增字串,例如從最長到 最短。主機會根據 以及車輛螢幕的可用空間
新增資料列的內嵌 CarIcons
您可以加入文字內嵌圖示,使用
CarIconSpan
。
詳情請參閱
CarIconSpan.create
敬上
,進一步瞭解如何建立這些時距。詳情請見
Spantastic
使用 Spans 建立文字樣式,概略瞭解 Span 的文字樣式運作方式。
Kotlin
val rating = SpannableString("Rating: 4.5 stars") rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val row = Row.Builder() ... .addText(rating) .build()
Java
SpannableString rating = new SpannableString("Rating: 4.5 stars"); rating.setSpan( CarIconSpan.create( // Create a CarIcon with an image of four and a half stars new CarIcon.Builder(...).build(), // Align the CarIcon to the baseline of the text CarIconSpan.ALIGN_BASELINE ), // The start index of the span (index of the character '4') 8, // The end index of the span (index of the last 's' in "stars") 16, Spanned.SPAN_INCLUSIVE_INCLUSIVE ); Row row = new Row.Builder() ... .addText(rating) .build();
車用硬體 API
從 Car App API 級別 3 開始,車輛應用程式程式庫會提供您熟悉的 API 存取車輛屬性和感應器
需求條件
如要搭配使用 API 與 Android Auto,請先在以下位置新增依附元件:
將 androidx.car.app:app-projected
新增至 Android 的 build.gradle
檔案
自動模組。如果是 Android Automotive OS,請新增依附元件
將 androidx.car.app:app-automotive
新增至 Android 的 build.gradle
檔案
Automotive OS 模組。
此外,在 AndroidManifest.xml
檔案中,您還需要
宣告相關權限
要求要使用的車輛資料。請注意,這些權限也必須
使用者授予您的權限。您可以使用
Android Auto 和 Android Automotive OS 上的相同程式碼,而非
不必建立獨立於平台的流程不過,您必須具備必要權限
有些則不同
汽車資訊
下表說明
CarInfo
API 和
如要使用這些元件,您需要具備下列權限:
方法 | 屬性 | Android Auto 權限 | Android Automotive OS 權限 | 開始支援的 Car App API 級別 |
---|---|---|---|---|
fetchModel |
廠牌、型式、年份 | android.car.permission.CAR_INFO |
3 | |
fetchEnergyProfile |
電動車連接器類型、燃料類型 | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
3 |
fetchExteriorDimensions
這項資料僅適用於部分 Android Automotive OS 車輛 搭載 API 30 以上版本 |
外觀尺寸 | 無 | android.car.permission.CAR_INFO |
7 |
addTollListener
removeTollListener |
付費卡狀態、收費卡類型 | 3 | ||
addEnergyLevelListener
removeEnergyLevelListener |
電池電量, 油量, 油量低, 剩餘電量 | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_ENERGY ,android.car.permission.CAR_ENERGY_PORTS ,android.car.permission.READ_CAR_DISPLAY_UNITS
|
3 |
addSpeedListener
removeSpeedListener |
原始速度、顯示速度 (顯示在車輛的儀表板螢幕上) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED ,android.car.permission.READ_CAR_DISPLAY_UNITS |
3 |
addMileageListener
removeMileageListener |
里程表距離 | com.google.android.gms.permission.CAR_MILEAGE |
Android Automotive OS 不會針對透過 Play 商店安裝的應用程式提供這項資料。 | 3 |
舉例來說,如要取得剩餘的範圍,請將
CarInfo
物件,然後
建立並註冊 OnCarDataAvailableListener
:
Kotlin
val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo val listener = OnCarDataAvailableListener<EnergyLevel> { data -> if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) { val rangeRemaining = data.rangeRemainingMeters.value } else { // Handle error } } carInfo.addEnergyLevelListener(carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener)
Java
CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo(); OnCarDataAvailableListener<EnergyLevel> listener = (data) -> { if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) { float rangeRemaining = data.getRangeRemainingMeters().getValue(); } else { // Handle error } }; carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carInfo.removeEnergyLevelListener(listener);
請勿假設車輛隨時都能取得資料。
如果收到錯誤訊息,請檢查
狀態:
供您進一步瞭解自己要求的資料為何可能
未擷取。詳情請參閱
參考文件
完整的 CarInfo
類別定義
汽車感測器
CarSensors
類別
存取車輛的加速計、陀螺儀、指南針和
位置資料。這些值的可用性可能取決於
原始設備製造商 (OEM)。加速計、陀螺儀和指南針的資料格式是
就如同
SensorManager
API:例如:
檢查車輛方向:
Kotlin
val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors val listener = OnCarDataAvailableListener<Compass> { data -> if (data.orientations.status == CarValue.STATUS_SUCCESS) { val orientation = data.orientations.value } else { // Data not available, handle error } } carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener) … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener)
Java
CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors(); OnCarDataAvailableListener<Compass> listener = (data) -> { if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) { List<Float> orientations = data.getOrientations().getValue(); } else { // Data not available, handle error } }; carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(), listener); … // Unregister the listener when you no longer need updates carSensors.removeCompassListener(listener);
如要存取車輛的位置資料,您還需要宣告並要求
android.permission.ACCESS_FINE_LOCATION
權限。
測試
如要在 Android Auto 上測試時模擬感應器資料,請參閱 感應器與感應器 「設定」部分 電腦版車用運算主機指南。在 Android 上測試時模擬感應器資料 Automotive OS,請參閱「Emulate 硬體 Android 上的狀態 Automotive OS 模擬器指南。
CarAppService、工作階段和螢幕生命週期
Session
和
Screen
類別會導入
LifecycleOwner
介面。阿斯
使用者與應用程式互動,以及 Session
和 Screen
物件的生命週期
叫用回呼,如下圖所示。
CarAppService 和工作階段的生命週期
如需完整詳細資料,請參閱
Session.getLifecycle
敬上
方法。
畫面的生命週期
如需完整詳細資料,請參閱
Screen.getLifecycle
方法,增加圍繞地圖邊緣的邊框間距。
透過汽車麥克風錄影
使用應用程式的
CarAppService
和
CarAudioRecord
API、
您可以授權應用程式存取使用者的車用麥克風。使用者必須授予
應用程式權限。您的應用程式可以錄製並
並在應用程式內處理使用者的輸入內容。
錄音權限
錄製任何音訊前,您必須先宣告其在
AndroidManifest.xml
,並要求使用者授予權限。
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
...
</manifest>
你必須取得在執行階段錄製的權限。請參閱要求 權限一節,進一步瞭解如何要求 授予這項權限。
錄音
使用者授予錄音權限後,你就能錄下音訊和處理程序 像是從錄音中移除
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) carAudioRecord.startRecording() val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording()
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); carAudioRecord.startRecording(); byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE]; while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) { // Use data array // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech } carAudioRecord.stopRecording();
音訊焦點
從汽車麥克風錄音時,請先取得音訊 將重點放在 確保播放中的媒體已停止。萬一你失去音訊焦點 停止錄製。
以下範例說明如何取得音訊焦點:
Kotlin
val carAudioRecord = CarAudioRecord.create(carContext) // Take audio focus so that user's media is not recorded val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build() val audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener { state: Int -> if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording() } } .build() if (carContext.getSystemService(AudioManager::class.java) .requestAudioFocus(audioFocusRequest) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED ) { // Don't record if the focus isn't granted return } carAudioRecord.startRecording() // Process the audio and abandon the AudioFocusRequest when done
Java
CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext()); // Take audio focus so that user's media is not recorded AudioAttributes audioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // Use the most appropriate usage type for your use case .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) .build(); AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) .setAudioAttributes(audioAttributes) .setOnAudioFocusChangeListener(state -> { if (state == AudioManager.AUDIOFOCUS_LOSS) { // Stop recording if audio focus is lost carAudioRecord.stopRecording(); } }) .build(); if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest) != AUDIOFOCUS_REQUEST_GRANTED) { // Don't record if the focus isn't granted return; } carAudioRecord.startRecording(); // Process the audio and abandon the AudioFocusRequest when done
測試程式庫
車輛專用 Android 測試
程式庫
可用來在測試環境中驗證應用程式行為的類別。
舉例來說,
SessionController
敬上
可讓您模擬與主機的連線
Screen
和
Template
。
詳情請參閱 範例 查看使用範例
回報車輛專用 Android App Library 問題
如果發現資料庫有問題,請使用 Google Issue Tracker: 在問題範本中,請務必填寫所有必要資訊。
提交新問題之前,請確認該問題是否列在媒體庫版本中 通知或問題清單您可以透過以下方式訂閱問題並進行投票: 在追蹤程式中按一下問題的星號。詳情請參閱訂閱問題一文。