내비게이션 앱 빌드

이 섹션에서는 세부 경로 안내 내비게이션 앱의 기능을 구현하는 데 활용할 수 있는 라이브러리의 다양한 기능을 자세하게 설명합니다.

매니페스트에서 내비게이션 지원 선언

내비게이션 앱은 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 문서를 참고하세요.

내비게이션 템플릿에 액세스

내비게이션 앱은 내비게이션 앱용으로 특별히 설계된 다음 템플릿에 액세스할 수 있습니다. 이러한 템플릿은 모두 앱에서 지도를 그리기 위해 액세스할 수 있는 백그라운드의 노출 영역을 표시하며, 앱에서 제공하는 기타 정보도 표시합니다(템플릿별로 다름).

  • NavigationTemplate: 내비게이션이 진행 중일 때 선택적 정보 메시지나 경로 안내 및 예상 이동 시간과 함께 지도를 표시합니다.
  • MapTemplate: 지도 옆에 목록(ListTemplate) 또는 창(PaneTemplate의 경우와 같이 상세한 정보와 가시도가 높은 작업)의 간략한 버전을 표시합니다.
  • PlaceListNavigationTemplate: 상응하는 마커를 지도에 그릴 수 있는 장소 목록을 표시합니다.
  • RoutePreviewNavigationTemplate: 하나를 선택하여 지도에서 강조표시할 수 있는 경로 목록을 표시합니다.

이러한 템플릿을 사용하여 내비게이션 앱의 사용자 인터페이스를 디자인하는 방법에 관한 자세한 내용은 자동차용 Android 앱 라이브러리 디자인 가이드라인을 참고하세요.

내비게이션 템플릿에 액세스하려면 앱은 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)

자바

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

SurfaceCallbackSurface의 속성이 변경될 때 다른 콜백과 함께 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만 호출됩니다.

어두운 모드

내비게이션 애플리케이션은 Android Auto 앱 품질 가이드라인에 설명된 대로 호스트가 조건이 충족된다고 판단하면 적절한 어두운 색상으로 Surface 인스턴스에 지도를 다시 그려야 합니다.

어두운 지도를 그려야 하는지 판단하려면 CarContext.isDarkMode 메서드를 사용하면 됩니다. 어두운 모드 상태가 변경될 때마다 Session.onCarConfigurationChanged 호출을 수신합니다.

내비게이션 애플리케이션은 추가 내비게이션 메타데이터를 호스트에 전달해야 합니다. 호스트는 이 정보를 사용하여 차량 헤드 단위에 정보를 제공하고 내비게이션 애플리케이션이 공유 리소스를 두고 충돌하는 것을 방지합니다.

내비게이션 메타데이터는 CarContext에서 액세스할 수 있는 NavigationManager 자동차 서비스를 통해 제공됩니다.

Kotlin

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

자바

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

내비게이션 시작, 종료, 중지

호스트에서 여러 내비게이션 앱, 경로 알림, 차량 계기판 데이터를 관리하려면 현재 내비게이션 상태를 알고 있어야 합니다. 사용자가 내비게이션을 시작하면 앱은 NavigationManager.navigationStarted를 호출해야 합니다. 마찬가지로 내비게이션이 종료되면(예: 사용자가 목적지에 도착하거나 사용자가 내비게이션을 취소할 때) 앱은 NavigationManager.navigationEnded를 호출해야 합니다.

사용자가 내비게이션을 완료한 경우에만 NavigationManager.navigationEnded를 호출해야 합니다. 예를 들어 이동 중에 경로를 다시 계산해야 한다면 Trip.Builder.setLoading(true)를 대신 사용하세요.

호스트에서 앱의 내비게이션 중지가 필요하여 NavigationManager.setListener를 통해 앱이 제공하는 NavigationManagerListener 객체에서 stopNavigation을 호출하는 경우도 있습니다. 그러면 앱은 계기판 디스플레이, 내비게이션 알림, 음성 안내에서 다음 회전 정보 발행을 중지해야 합니다.

경로 정보

내비게이션이 진행 중일 때 앱은 NavigationManager.updateTrip을 호출해야 합니다. 이 호출에서 제공되는 정보는 차량의 계기판과 헤드업 디스플레이에 사용됩니다. 운전하는 특정 차량에 따라 사용자에게 일부 정보가 표시되지 않을 수도 있습니다. 예를 들어 데스크톱 헤드 단위에는 Trip에 추가된 Step이 표시되지만 Destination 정보는 표시되지 않습니다.

정보가 계기판에 도달하는지 테스트하려면 간단한 계기판 디스플레이를 보여 주도록 데스크톱 헤드 단위(DHU) 도구를 구성하면 됩니다. 다음 콘텐츠로 cluster.ini 파일을 만듭니다.

[general]
instrumentcluster = true

그런 다음 추가 명령줄 매개변수를 사용하여 DHU를 호출할 수 있습니다.

dhu -c cluster.ini

텍스트 또는 아이콘을 사용하여 TravelEstimate 맞춤설정

텍스트 또는 아이콘으로 예상 이동 시간을 맞춤설정하려면 TravelEstimate.BuildersetTripIcon 또는 setTripText 메서드를 사용하세요. NavigationTemplateTravelEstimate를 사용하여 도착 예정 시간, 남은 시간, 남은 거리와 함께(또는 그 대신에) 선택적으로 텍스트와 아이콘을 설정합니다.

그림 1. 맞춤 아이콘과 텍스트가 포함된 예상 이동 시간

다음 스니펫에서는 TravelEstimate.BuildersetTripIconsetTripText 메서드를 사용하여 예상 이동 시간을 맞춤설정합니다.

Kotlin

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

자바

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 메서드를 사용하여 설정되지 않으면 알림 채널의 중요도가 사용됩니다.

앱은 사용자가 HUN이나 레일 위젯을 탭할 때 앱에 전송될 PendingIntentCarAppExtender에서 설정할 수 있습니다.

NotificationCompat.Builder.setOnlyAlertOncetrue 값으로 호출하면 중요도가 높은 알림이 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()

자바

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이 표시되고 그 외 다른 값으로 설정하면 레일 위젯만 업데이트됩니다.

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

자바

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에서 기본 내비게이션 자동차 앱은 사용자가 마지막으로 실행한 내비게이션 앱에 해당합니다. 예를 들어, 사용자가 어시스턴트를 통해 내비게이션 명령어를 호출하거나 다른 앱에서 내비게이션을 시작하는 인텐트를 전송하는 경우 기본 내비게이션 자동차 앱에서 내비게이션 인텐트를 수신합니다.

사용자가 지도와 상호작용할 수 있도록 허용

사용자가 지도의 확대/축소, 화면 이동과 같이 지도와 상호작용할 수 있는 지원을 추가하여 사용자가 지도의 여러 부분을 보도록 할 수 있습니다. 템플릿마다 최소 자동차 앱 API 수준 요구사항이 다릅니다. 구현하려는 템플릿의 최소 수준은 아래 표를 참고하세요.

템플릿자동차 앱 API 수준 이후 지원되는 상호작용
NavigationTemplate2
PlaceListNavigationTemplate4
RoutePreviewNavigationTemplate4
MapTemplate5

SurfaceCallback 메서드

SurfaceCallback에는 NavigationTemplate 또는 PlaceListNavigationTemplate, RoutePreviewNavigationTemplate, MapTemplate 템플릿으로 빌드된 지도에 지도 상호작용을 추가할 수 있는 콜백 메서드가 여러 개 있습니다(예: onClick, onScroll, onScale, onFling). 이러한 콜백이 사용자 상호작용과 어떠한 관련이 있는지 알아보려면 아래의 표를 참고하세요.

상호작용 SurfaceCallback 메서드 자동차 앱 API 수준 이후 지원됨
onClick 5
손가락 모으기(확대/축소) onScale 2
원터치 드래그 onScroll 2
원터치 플링 onFling 2
두 번 탭 onScale(템플릿 호스트에서 결정하는 배율 사용) 2
화면 이동 모드에서 로터리 이동 onScroll(템플릿 호스트에서 결정하는 거리 요소 사용) 2

지도 작업 스트립

NavigationTemplate, PlaceListNavigationTemplate, RoutePreviewNavigationTemplate, MapTemplate 템플릿은 확대/축소, 중심 재설정, 나침반 표시 또는 앱에서 표시하도록 선택할 수 있는 다른 작업과 같은 지도 관련 작업을 위해 지도 작업 스트립을 가질 수 있습니다. 지도 작업 스트립은 작업 깊이에 영향을 주지 않고 새로고침할 수 있는 아이콘 전용 버튼을 최대 네 개까지 가질 수 있습니다. 작업 스트립과 마찬가지로 지도 작업 스트립은 유휴 상태 시 숨겨지고 활성 상태에 다시 표시됩니다.

지도 상호작용 콜백을 수신하려면 지도 작업 스트립에 Action.PAN 버튼을 추가해야 합니다. 앱이 지도 작업 스트립에서 Action.PAN 버튼을 생략하면 SurfaceCallback 메서드로부터 사용자 입력을 수신하지 않게 되고 호스트는 이전에 활성화된 화면 이동 모드를 종료합니다. 사용자가 화면 이동 버튼을 누르면 호스트가 화면 이동 모드로 전환됩니다. 터치스크린에는 화면 이동 버튼이 표시되지 않습니다.

화면 이동 모드

화면 이동 모드에서는 템플릿 호스트가 터치 입력을 사용하지 않는 기기(예: 로터리 컨트롤러, 터치패드)의 사용자 입력을 적절한 SurfaceCallback 메서드로 변환합니다. 사용자 작업에 관한 응답으로 NavigationTemplate BuildersetPanModeListener 메서드를 사용하여 화면 이동 모드에 진입하거나 화면 이동 모드를 종료합니다. 사용자가 화면 이동 모드에 있는 동안 호스트는 템플릿에서 다른 UI 구성요소를 숨길 수 있습니다.

안정화된 영역

안정화된 영역은 유휴 상태와 활성 상태 사이에 업데이트됩니다. 앱은 지도 작업 스트립으로 인해 지도상에 있는 중요한 정보가 가려지지 않도록, 속도, 속도 제한, 도로 경고와 같은 운전 관련 정보를 안정화된 영역의 크기에 따라 그려야 합니다.

컨텍스트 내 내비게이션 알림

Alert은 내비게이션 화면의 컨텍스트를 벗어나지 않고 운전자에게 중요한 정보와 선택적 작업을 표시합니다. 운전자에게 최상의 환경을 제공하기 위해 AlertNavigationTemplate 내에서 작동하여 내비게이션 경로를 차단하지 않고 운전자 주의 분산 행동을 최소화합니다.

AlertNavigationTemplate 내에서만 사용할 수 있습니다. NavigationTemplate 외부의 사용자에게 알리려면 알림 표시의 설명대로 운전자 알림(HUN)을 사용하는 것이 좋습니다.

예를 들어 Alert를 사용하여 다음 작업을 할 수 있습니다.

  • 운전자에게 교통상황 변경 등 현재 내비게이션과 관련된 업데이트를 알립니다.
  • 운전자에게 이동식 과속 단속 카메라의 존재 등 현재 내비게이션과 관련된 업데이트를 요청합니다.
  • 예정된 작업을 제안하고 운전자가 수락할지를 묻습니다(예: 운전자가 도중에 누군가를 태울지).

기본 형태로 Alert는 제목과 Alert 지속 시간으로 구성됩니다. 지속 시간은 진행률 표시줄로 표시됩니다. 원할 경우 부제목, 아이콘, 최대 2개의 Action을 추가할 수 있습니다.

그림 1. 컨텍스트 내 내비게이션 알림

Alert가 표시된 후에는 운전자 상호작용으로 인해 NavigationTemplate에서 나가는 경우 다른 템플릿으로 이전되지 않습니다. Alert 시간이 초과되거나 사용자가 작업을 실행하거나 앱에서 Alert를 닫을 때까지 원래 NavigationTemplate에 유지됩니다.

알림 지속 시간 구성

앱의 요구사항과 일치하는 Alert 지속 시간을 선택합니다. 권장되는 내비게이션 Alert의 지속 시간은 10초입니다. 가이드라인은 자동차용 Android 디자인 가이드라인을 참고하세요.

알림 만들기

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

자바

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를 표시하려면 앱의 CarContext를 통해 사용할 수 있는 AppManager.showAlert 메서드를 호출합니다.

// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
  • 현재 표시되고 있는 Alert와 동일한 alertId가 있는 Alert를 사용하여 showAlert를 호출하면 아무 일도 일어나지 않습니다. Alert는 업데이트되지 않습니다. Alert를 업데이트하려면 새 alertIdAlert를 다시 만들어야 합니다.
  • 현재 표시되고 있는 Alert와 다른 alertId가 있는 Alert를 사용하여 showAlert를 호출하면 현재 표시되는 Alert가 닫힙니다.

알림 닫기

Alert는 시간 제한 또는 운전자 상호작용으로 인해 자동으로 닫히지만 수동으로 Alert를 닫을 수도 있습니다. Alert의 정보가 오래되어 닫으려는 경우를 예로 들 수 있습니다. Alert를 닫으려면 AlertalertId를 사용하여 dismissAlert 메서드를 호출하세요.

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

현재 표시되는 Alert(있는 경우)과 일치하지 않는 alertId를 사용하여 dismissAlert을 호출하면 아무 일도 일어나지 않습니다. 예외가 발생하지는 않습니다.