构建导航应用

本部分详细介绍您可以使用库的哪些不同功能来实现精细导航应用的功能。

在清单中声明导航支持

导航应用需要在其 CarAppService 的 intent 过滤器中声明 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>

支持导航 intent

为了支持发送到应用的导航 intent(包括来自 Google 助理的语音查询导航 intent),应用需要在其 Session.onCreateScreenSession.onNewIntent 中处理 CarContext.ACTION_NAVIGATE intent。

如需详细了解该 intent 的格式,请参阅 CarContext.startCarApp 的文档。

访问导航模板

导航应用可以访问专为导航应用设计的以下模板。所有这些模板都会在后台显示一个 Surface,应用可以访问该 Surface 以便绘制地图,这些模板还会显示应用提供的其他信息,这些信息因模板而异。

如需详细了解如何使用这些模板设计导航应用的界面,请参阅 Android for Cars 应用库设计准则

为了能够访问导航模板,应用需要在其 AndroidManifest.xml 中声明 androidx.car.app.NAVIGATION_TEMPLATES 权限:

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

绘制地图

导航应用可以访问 Surface,以在相关模板上绘制地图。

然后,可以通过将 SurfaceCallback 实例设置为 AppManager 汽车服务来访问 SurfaceContainer 对象:

Kotlin

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

Java

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

SurfaceContainer 可用时,SurfaceCallback 会提供一个回调,当 Surface 的属性发生更改时,它还会提供其他回调。

为了能够访问 Surface,应用需要在其 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 Auto 应用质量准则中所述。

为了决定是否应绘制深色地图,您可以使用 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.setListener 提供的 NavigationManagerListener 对象中调用 stopNavigation。然后,应用必须停止在仪表板屏幕、导航通知和语音导航中发出下一个转弯的信息。

行程信息

在有效导航期间,应用应调用 NavigationManager.updateTrip。此调用中提供的信息将用于车辆的仪表板和平视显示仪。并非所有信息都可以显示给用户,具体取决于驾驶的特定车辆。例如,桌面车机会显示添加到 TripStep,但不会显示 Destination 信息。

为了测试信息是否到达仪表板,可以配置桌面车机 (DHU) 工具以显示简单的仪表板屏幕。创建一个包含以下内容的 cluster.ini 文件:

[general]
instrumentcluster = true

然后,您可以使用一个额外的命令行参数调用 DHU:

dhu -c cluster.ini

精细导航通知

精细导航 (TBT) 指示可以通过频繁更新的导航通知来提供。为了在车载显示屏中被视为导航通知,通知的构建器必须执行以下操作:

  1. 使用 NotificationCompat.Builder.setOngoing 方法将通知标记为持续性通知。
  2. 将通知的类别设置为 Notification.CATEGORY_NAVIGATION
  3. 使用 CarAppExtender 扩展通知。

导航通知将显示在车载显示屏底部的侧边栏微件中。如果通知的重要性级别设置为 IMPORTANCE_HIGH,它也会显示为浮动通知 (HUN)。如果未使用 CarAppExtender.Builder.setImportance 方法设置重要性,将采用通知渠道的重要性

应用可以在 CarAppExtender 中设置 PendingIntent,当用户点按 HUN 或侧边栏微件时,会将其发送到应用。

如果调用 NotificationCompat.Builder.setOnlyAlertOnce 且将值设置为 true,则高重要性通知将只以 HUN 的形式提醒一次。

以下代码段展示了如何构建导航通知:

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();

精细导航通知的准则

导航应用应随着距离的变化定期更新 TBT 通知,这样会更新侧边栏微件,并且只将通知显示为 HUN。应用可使用 CarAppExtender.Builder.setImportance 方法设置通知的重要性,由此控制 HUN 行为。将重要性设置为 IMPORTANCE_HIGH 将显示 HUN,将其设置为其他任何值都将只更新侧边栏微件。

语音导航

如需通过汽车扬声器播放导航指导,应用必须请求音频焦点。作为 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 中,默认汽车导航应用为用户最近启动的导航应用。例如,当用户通过助理调用导航命令或其他应用发送 intent 以开始导航时,正是该应用会接收导航 intent

允许用户与您的地图互动

从汽车应用 API 级别 2 开始,您可以在 NavigationTemplate 中为地图添加缩放和平移功能,让用户可以查看地图的不同部分。

SurfaceCallback 方法

SurfaceCallback 提供了以下三个回调方法供您向 NavigationTemplate 添加地图互动功能:onScaleonScrollonFling。如需了解这些回调如何与用户互动关联,请参阅下表。

互动 SurfaceCallback 方法
双指张合(缩放) onScale
单点触控拖动 onScroll
单点触控快速滑动 onFling
点按两次 onScale(缩放比例由模板主机决定)
平移模式下的旋转轻推 onScroll(距离系数由模板主机决定)

地图操作栏

NavigationTemplate 可以为地图相关操作(例如放大和缩小、重新居中、显示罗盘或您的应用可以选择显示的任何其他操作)提供地图操作栏。地图操作栏最多可包含四个仅显示图标的按钮,这些按钮可在不影响任务深度的情况下刷新。与操作栏类似,地图操作栏会在空闲状态下隐藏,并在活跃状态下重新显示。

如需接收地图互动回调,您必须在地图操作栏中添加一个 Action.PAN 按钮。如果应用的地图操作栏中没有 Action.PAN 按钮,您将无法从 SurfaceCallback 方法接收用户输入,并且主机会退出先前启用的任何平移模式。当用户按平移按钮时,主机会进入平移模式。触摸屏上不会显示平移按钮。

平移模式

在平移模式下,模板主机会将来自非触控输入设备(例如旋控器和触控板)的用户输入在转换后传递给相应的 SurfaceCallback 方法。系统会使用 NavigationTemplate Builder 中的 setPanModeListener 方法响应用户操作来进入或退出平移模式。当用户处于平移模式时,主机可能会隐藏模板中的其他界面组件。

稳定区域

稳定区域会在状态于空闲和活跃之间切换时更新。您的应用应根据稳定区域的大小来绘制速度、速度限制或道路警告等与驾驶相关的信息,以便地图操作栏不会遮蔽地图上的重要信息。