建構導航應用程式

本頁說明車輛應用程式程式庫的不同功能,方便您實作即時路線導航應用程式中的功能。

在資訊清單中宣告導航支援

導航應用程式必須在 CarAppService 的意圖篩選器中宣告 androidx.car.app.category.NAVIGATION 車用應用程式類別

<application>
    ...
   <service
       ...
        android:name=".MyNavigationCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.NAVIGATION"/>
      </intent-filter>
    </service>
    ...
</application>

支援導航意圖

為支援應用程式的導航意圖 (包括透過 Google 助理使用語音查詢的導航意圖),應用程式必須處理 Session.onCreateScreenSession.onNewIntent 中的 CarContext.ACTION_NAVIGATE 意圖。

如要進一步瞭解意圖格式,請參閱有關 CarContext.startCarApp 的說明文件。

存取導航範本

導航應用程式可以存取專為導航應用程式設計的以下專用範本。這些範本都會在地圖的背景顯示介面,並在導航期間顯示即時路線指示。

如要瞭解如何運用這些範本來設計導航應用程式的使用者介面,請參閱「導航應用程式」。

如要存取導航範本,應用程式需要在 AndroidManifest.xml 檔案中聲明 androidx.car.app.NAVIGATION_TEMPLATES 權限:

<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>

繪製地圖

導航應用程式可以存取 Surface,在相關範本中繪製地圖。

如要存取 SurfaceContainer 物件,可以在 AppManager 車輛服務設定 SurfaceCallback 例項:

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

Surface 屬性有所變更時,SurfaceCallback 就會在可使用 SurfaceContainer 時提供回呼及其他回呼。

如要存取介面,應用程式必須在 AndroidManifest.xml 檔案中聲明 androidx.car.app.ACCESS_SURFACE 權限:

<uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>

地圖可見區域

主機可在地圖上層為範本繪製使用者介面元素。只要呼叫 SurfaceCallback.onVisibleAreaChanged 方法,主機即可與使用者絕對可見的開放區域通訊。此外,為了盡量減少變更,主機會透過最小矩形呼叫 SurfaceCallback.onStableAreaChanged 方法,這種矩形一律會根據目前的範本顯示。

舉例來說,如果導航應用程式使用有頂端動作列的 NavigationTemplate,在使用者有一段時間未與畫面互動後,系統就會隱藏該動作列,為地圖提供更多空間。在這種情況下,系統會使用相同矩形回呼 onStableAreaChangedonVisibleAreaChanged。當操作列隱藏時,系統只會使用較大區域呼叫 onVisibleAreaChanged。如果使用者與畫面互動,系統只會使用第一個矩形呼叫 onVisibleAreaChanged

支援深色模式

當主機判定條件可提供擔保時,導航應用程式必須使用適當的深色在 Surface 例項上重新繪製地圖,如「車用 Android 應用程式品質指南」所述。

如要判斷是否應繪製深色地圖,可以使用 CarContext.isDarkMode 方法。每當深色模式狀態有所變更,您就會收到 Session.onCarConfigurationChanged 呼叫。

導航應用程式必須向主機傳送額外的導航中繼資料。主機會使用該資訊,為車輛的車用運算主機提供資訊,並避免導航應用程式與共用資源產生衝突。

系統會透過可從 CarContext 可存取的 NavigationManager 車輛服務提供導航中繼資料:

Kotlin

val navigationManager = carContext.getCarService(NavigationManager::class.java)

Java

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);

開始、結束及停止導航

為了讓主機管理多個導航應用程式、路線規劃通知和車輛叢集資料,主機需要瞭解當前的導航狀態。使用者開始導航時,請呼叫 NavigationManager.navigationStarted。同樣地,導航結束時 (例如使用者抵達目的地或取消導航),請呼叫 NavigationManager.navigationEnded

請只在使用者完成導航時呼叫 NavigationManager.navigationEnded。舉例來說,如果中途需要重新計算路徑,請改用 Trip.Builder.setLoading(true)

主機有時會需要應用程式停止導航,然後透過 NavigationManager.setNavigationManagerCallback 在應用程式提供的 NavigationManagerCallback 物件中呼叫 onStopNavigation。然後應用程式必須在叢集顯示、導航通知和語音導引時,停止發送下一個轉彎的資訊。

更新行程資訊

在導航期間,請呼叫 NavigationManager.updateTrip。此呼叫提供的資訊可用於車輛的叢集和看路提醒顯示畫面。視使用者駕駛的車輛而定,使用者並不會看到所有資訊。舉例來說,電腦版車用運算主機 (DHU) 會顯示加入 TripStep,但不會顯示 Destination 資訊。

繪製至儀表板螢幕

為了提供最身歷其境的使用者體驗,您可能會希望在車輛的儀表板螢幕上顯示基本中繼資料以外的資訊。從 Car App API 級別 6 開始,導航應用程式可以選擇在儀表板螢幕上直接顯示自身的內容 (適用於支援的車輛),但有下列限制:

  • 儀表板螢幕 API 不支援輸入控制項
  • 儀表板螢幕應只顯示地圖圖塊。您可以選擇在這些圖塊上顯示使用中的導航路線。
  • 儀表板螢幕 API 僅支援使用 NavigationTemplate
    • 與主螢幕不同,儀表板螢幕不一定每次都會顯示所有 NavigationTemplate UI 元素,例如即時路線指示、預計到達時間資訊卡和動作。地圖圖塊是唯一會持續顯示的 UI 元素。

宣告儀表板支援

為了讓主機應用程式知道您的應用程式支援在儀表板螢幕上顯示內容,您必須在 CarAppService<intent-filter> 中新增 androidx.car.app.category.FEATURE_CLUSTER <category> 元素,如以下程式碼片段所示:

<application>
    ...
   <service
       ...
        android:name=".MyNavigationCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.NAVIGATION"/>
        <category android:name="androidx.car.app.category.FEATURE_CLUSTER"/>
      </intent-filter>
    </service>
    ...
</application>

生命週期與狀態管理

從 API 級別 6 開始,車輛應用程式的生命週期流程保持不變,但現在 CarAppService::onCreateSession 會採用 SessionInfo 類型的參數,提供有關所建立 Session 的額外資訊 (也就是顯示類型和支援的範本組合)。

應用程式可以選擇使用相同的 Session 類別來處理儀表板和主螢幕,也可以建立特定螢幕專用的 Sessions 來自訂各螢幕的行為 (如以下程式碼片段所示)。

Kotlin

override fun onCreateSession(sessionInfo: SessionInfo): Session {
  return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) {
    ClusterSession()
  } else {
    MainDisplaySession()
  }
}

Java

@Override
@NonNull
public Session onCreateSession(@NonNull SessionInfo sessionInfo) {
  if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) {
    return new ClusterSession();
  } else {
    return new MainDisplaySession();
  }
}

系統無法保證何時 (或是否) 會提供儀表板螢幕,儀表板 Session 也有可能是唯一的 Session。舉例來說,使用者在應用程式導航期間將主要螢幕切換為另一個應用程式。我們的「標準」共識是,只有在呼叫 NavigationManager::navigationStarted 後,應用程式才會取得儀表板螢幕的控制權。但是,系統仍有可能在沒有導航作業發生時為應用程式提供儀表板螢幕,也可能一律不提供儀表板螢幕。您的應用程式可以藉由顯示地圖圖塊的閒置狀態,自行處理這些情況。

主機會為每個 Session 建立獨立的繫結機制和 CarContext 執行個體。也就是說,在您使用 ScreenManager::pushScreen::invalidate 等方法時,只有用於呼叫這些方法的 Session 會受到影響。如果需要跨 Session 通訊,應用程式應在這些執行個體之間建立自己的通訊管道 (例如使用廣播訊息、共用的單例模式等方法)。

測試儀表板支援

無論是在 Android Auto 還是 Android Automotive OS 上,您都可以測試導入作業。Android Auto 適用的方法是設定電腦版車用運算主機來模擬次要儀表板螢幕。如果是 Android Automotive OS,則會由 API 級別 30 以上的通用系統映像檔模擬儀表板螢幕。

使用文字或圖示自訂 TravelEstimate

如要利用文字和/或圖示自訂預估交通時間,請使用 TravelEstimate.Builder 類別的 setTripIconsetTripText 方法。NavigationTemplate 使用 TravelEstimate 選擇性設定文字和圖示,用來搭配或取代預計到達時間、剩餘時間和剩餘距離。

圖 1. 行程預估時間與自訂圖示和文字。

下列程式碼片段使用 setTripIconsetTripText 自訂行程預估時間:

Kotlin

TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...))
      ...
      .setTripIcon(CarIcon.Builder(...).build())
      .setTripText(CarText.create(...))
      .build()

Java

new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...))
      ...
      .setTripIcon(CarIcon.Builder(...).build())
      .setTripText(CarText.create(...))
      .build();

提供即時路線通知

請使用經常更新的導航通知提供即時路線 (TBT) 導航指示。為了在汽車螢幕上順利顯示導航通知,通知建構工具必須執行下列操作:

  1. 使用 NotificationCompat.Builder.setOngoing 方法,將通知標示為進行中。
  2. 將通知的類別設為 Notification.CATEGORY_NAVIGATION
  3. 使用 CarAppExtender 擴充通知。

導航通知會在車輛螢幕底部的邊欄小工具中顯示。如果通知的重要性等級設為 IMPORTANCE_HIGH,則也會顯示為看路提醒通知 (HUN)。如果並未使用 CarAppExtender.Builder.setImportance 方法設定重要性,系統就會使用通知管道的重要性

應用程式可在 CarAppExtender 中設定 PendingIntent,並在使用者輕觸抬頭通知或邊欄小工具時,將其傳送至應用程式。

如果使用 true 值呼叫 NotificationCompat.Builder.setOnlyAlertOnce,則重要性高的通知只會在抬頭通知中顯示一次。

下列程式碼片段說明如何建構導航通知:

Kotlin

NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    Intent(ACTION_OPEN_APP).setComponent(
                        ComponentName(context, MyNotificationReceiver::class.java)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build()

Java

new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    new Intent(ACTION_OPEN_APP).setComponent(
                        new ComponentName(context, MyNotificationReceiver.class)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build();

請根據距離變化定期更新即時路線通知 (邊欄小工具也會隨之更新),並且僅以抬頭通知的形式顯示通知。您可以使用 CarAppExtender.Builder.setImportance 設定通知的重要性,藉此控制抬頭通知的行為。將重要性設為 IMPORTANCE_HIGH,就會顯示抬頭通知;如果設為任何其他值,則只會更新邊欄小工具。

重新整理 PlaceListNavigationTemplate 內容

您可以允許駕駛人在瀏覽使用 PlaceListNavigationTemplate 建構的地點清單時,輕觸按鈕重新整理內容。如要啟用清單重新整理功能,請實作 OnContentRefreshListener 介面的 onContentRefreshRequested 方法,並使用 PlaceListNavigationTemplate.Builder.setOnContentRefreshListener 為範本設定事件監聽器。

下列程式碼片段說明如何設定範本的事件監聽器:

Kotlin

PlaceListNavigationTemplate.Builder()
    ...
    .setOnContentRefreshListener {
        // Execute any desired logic
        ...
        // Then call invalidate() so onGetTemplate() is called again
        invalidate()
    }
    .build()

Java

new PlaceListNavigationTemplate.Builder()
        ...
        .setOnContentRefreshListener(() -> {
            // Execute any desired logic
            ...
            // Then call invalidate() so onGetTemplate() is called again
            invalidate();
        })
        .build();

只有在事件監聽器含有值時,重新整理按鈕才會顯示在 PlaceListNavigationTemplate 的標頭中。

駕駛人按一下重新整理按鈕時,系統會呼叫 OnContentRefreshListener 實作的 onContentRefreshRequested 方法。請在 onContentRefreshRequested 中,呼叫 Screen.invalidate 方法。主機隨後會呼叫應用程式的 Screen.onGetTemplate 方法,使用經過重新整理的內容擷取範本。如要進一步瞭解如何重新整理範本,請參閱「重新整理範本內容」。只要 onGetTemplate 傳回的下一個範本類型相同,系統就會計為重新整理,而不會計入範本配額。

提供語音導引

如要使用車上音響播放導航指引,應用程式必須申請音訊焦點權限。請在 AudioFocusRequest 中,將用途設為 AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE。此外,請將焦點增益設為 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

模擬導航

為了將導航功能提交至 Google Play 商店並進行驗證,應用程式必須實作 NavigationManagerCallback.onAutoDriveEnabled 回呼。呼叫此回呼時,應用程式必須在使用者開始導航時,模擬前往所選目的地的導航過程。只要當前 Session 的生命週期達到 Lifecycle.Event.ON_DESTROY 狀態,應用程式即可退出此模式。

您可以在指令列中執行以下操作,測試系統是否呼叫 onAutoDriveEnabled 的導入:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

例如:

adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE

預設車用導航應用程式

在 Android Auto 中,使用者最後啟動的導航程式會成為預設的車輛導航應用程式。當使用者透過 Google 助理叫用導航指令,或其他應用程式傳送啟動導航的意圖時,預設應用程式會接收導航意圖

允許使用者與地圖互動

您可以新增使用者與地圖互動的支援功能,例如讓使用者縮放及平移地圖,查看地圖的不同部分。每個範本的車輛應用程式最低 API 級別規定不盡相同。請參閱下表,瞭解您想實作的範本所需的最低級別。

Template開始支援互動功能的 Car App API 級別
NavigationTemplate2
PlaceListNavigationTemplate4
RoutePreviewNavigationTemplate4
MapTemplate5

SurfaceCallback 方法

SurfaceCallback 介面提供多種回呼方法,可讓您為使用 NavigationTemplatePlaceListNavigationTemplateRoutePreviewNavigationTemplateMapTemplate 範本建立的地圖新增互動功能:onClickonScrollonScaleonFling。如要瞭解這些回呼與使用者互動之間的關係,請參閱下表。

互動技術 SurfaceCallback 方法 開始支援的 Car App API 級別
輕觸 onClick 5
雙指撥動即可縮放 onScale 2
單點觸控拖曳 onScroll 2
單點觸控滑動 onFling 2
輕觸兩下 onScale (由範本主機決定縮放比例係數) 2
平移模式的旋轉自動提醒 onScroll (由範本主機決定距離係數) 2

地圖動作區域

NavigationTemplatePlaceListNavigationTemplateRoutePreviewNavigationTemplateMapTemplate 範本可以有地圖操作列,供地圖相關的操作使用,例如縮放和重新置中、顯示指南針,以及其他您選擇顯示的動作。地圖操作列最多可有四個純圖示按鈕,這些按鈕可重新整理,並且不影響工作深度。處於閒置狀態時,這個動作列會隱藏,待回到啟用狀態才會再顯示。

如要接收地圖互動回呼,您必須在地圖動作列中新增 Action.PAN 按鈕。使用者按下平移按鈕時,主機會進入平移模式,如下一節所述。

如果應用程式忽略地圖動作列中的 Action.PAN 的按鈕,就無法從 SurfaceCallback 方法收到使用者輸入內容,而且主機會結束先前啟用的平移模式。

如果是觸控螢幕,畫面上不會顯示平移按鈕。

平移模式

在平移模式下,使用者透過非觸控輸入裝置 (例如旋轉控制器和觸控板) 輸入的內容,會由範本主機轉譯成適當的 SurfaceCallback 方法。使用 NavigationTemplate.Builder 中的 setPanModeListener 方法,可在使用者進入或結束平移模式時做出反應。使用者處於平移模式時,主機可隱藏範本中的其他 UI 元件。

穩定區域

穩定區域會在閒置和啟用狀態之間進行更新。請根據穩定區域的大小,決定是否要繪製行車相關資訊,例如速度、速限或道路警告,避免地圖動作列擋住地圖上的重要資訊。

在導航畫面顯示快訊

Alert 會向駕駛人顯示重要資訊和選用動作,無須離開導航畫面。為了讓駕駛人享有最佳體驗,Alert 會在 NavigationTemplate 中運作,避免遮擋導航路徑,並盡量減少駕駛人分心的情況。

Alert 僅適用於 NavigationTemplate。如要通知 NavigationTemplate 以外的使用者,請考慮使用抬頭通知 (HUN),詳情請參閱「顯示通知」。

舉例來說,使用 Alert 可執行以下動作:

  • 告知駕駛人與目前導航相關的更新內容,例如路況變更。
  • 詢問駕駛人與目前導航相關的更新,例如移動式測速照相機。
  • 提出即將到來的任務,並詢問駕駛人是否要接受,例如駕駛人是否願意順路接送乘客。

Alert 的基本格式中包含標題和 Alert 時間長度。時間長度會以進度列顯示。您可以視需要新增副標題、圖示和最多兩個 Action 物件。

圖 2. 導航畫面中的快訊。

Alert 顯示後,如果駕駛人互動導致離開 NavigationTemplate,快訊並不會沿用至其他範本。快訊會一直停留在原始的 NavigationTemplate 中,直到 Alert 逾時、使用者採取動作或應用程式關閉 Alert

建立快訊

請使用 Alert.Builder 建立 Alert 例項:

Kotlin

Alert.Builder(
        /*alertId*/ 1,
        /*title*/ CarText.create("Hello"),
        /*durationMillis*/ 5000
    )
    // The fields below are optional
    .addAction(firstAction)
    .addAction(secondAction)
    .setSubtitle(CarText.create(...))
    .setIcon(CarIcon.APP_ICON)
    .setCallback(...)
    .build()

Java

new Alert.Builder(
        /*alertId*/ 1,
        /*title*/ CarText.create("Hello"),
        /*durationMillis*/ 5000
    )
    // The fields below are optional
    .addAction(firstAction)
    .addAction(secondAction)
    .setSubtitle(CarText.create(...))
    .setIcon(CarIcon.APP_ICON)
    .setCallback(...)
    .build();

如要監聽 Alert 的取消或關閉作業,請建立 AlertCallback 介面實作方式。AlertCallback 呼叫路徑如下:

設定快訊時長

選擇符合應用程式需求的 Alert 持續時長。導航 Alert 的建議時間長度為 10 秒。詳情請參閱「導航快訊」一文。

顯示快訊

如要顯示 Alert,呼叫 AppManager.showAlert,此方法可透過應用程式上的 CarContext 取得。

// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
  • 使用 Alert 呼叫 showAlert 時,如果 alertId 與目前顯示的 Alert ID 相同,系統不會執行任何操作。Alert 不會更新。如要更新 Alert,必須使用新的 alertId 重新建立快訊。
  • 使用 Alert 呼叫 showAlert 時,如果 alertId 與目前顯示的 Alert 不同,系統會關閉目前顯示的 Alert

關閉快訊

雖然 Alert 會因逾時或駕駛人互動而自動關閉,但在快訊資訊過時等時機,您也可以手動關閉 Alert。如要關閉 Alert,請使用 AlertalertId 呼叫 dismissAlert 方法。

// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())

如果使用與目前顯示的 Alert 不相符的 alertId 呼叫 dismissAlert,系統不會執行任何動作,也不會擲回例外狀況。