Android Auto용 내비게이션, 주차, 충전 앱 빌드

자동차용 Android 앱 라이브러리를 사용하면 내비게이션, 주차, 충전 앱을 자동차에서 사용할 수 있습니다. 운전자 주의 분산 행동 표준을 충족하도록 설계된 일련의 템플릿을 제공하고 다양한 자동차 화면 요소 및 입력 모달리티와 같은 세부사항을 처리하여 이를 달성합니다.

이 가이드에서는 라이브러리의 주요 기능과 개념을 간략히 설명하고 간단한 앱을 설정하는 과정을 안내합니다.

시작하기 전에

  1. 자동차용 Android 앱 라이브러리 디자인 가이드라인을 검토합니다.
  2. 이 섹션에 나열된 주요 용어와 개념을 검토합니다.
  3. Android Auto 시스템 UI를 숙지합니다.
  4. 출시 노트를 검토합니다.
  5. 샘플을 검토합니다.
  6. [비공개 소스 라이브러리에만 적용] 자동차용 Android 앱 라이브러리 이용약관을 검토합니다.

주요 용어 및 개념

모델 및 템플릿
사용자 인터페이스는 모델 객체가 속한 템플릿에서 허용하는 다양한 방법으로 함께 정렬될 수 있는 모델 객체의 그래프로 표현됩니다. 템플릿은 이러한 그래프에서 루트 역할을 할 수 있는 모델의 하위 집합입니다. 모델에는 사용자에게 표시될 정보(텍스트 및 이미지 형식)와 이러한 정보의 시각적 표시 측면을 구성하는 속성(예: 텍스트 색상 또는 이미지 크기)이 포함됩니다. 호스트는 운전자 주의 분산 행동 표준을 충족하도록 설계된 뷰로 모델을 변환하고 다양한 자동차 화면 요소 및 입력 모달리티와 같은 세부사항을 처리합니다.
호스트
호스트는 앱이 자동차에서 실행되도록 라이브러리의 API에서 제공하는 기능을 구현하는 백엔드 구성요소입니다. 호스트의 역할은 앱을 찾고 앱의 수명 주기를 관리하는 것부터 모델을 뷰로 변환하고 앱에 사용자 상호작용을 알리는 것까지 다양합니다. 휴대기기에서 이 호스트는 Android Auto로 구현됩니다.
템플릿 제한사항
다양한 템플릿이 모델의 콘텐츠에 제한을 적용합니다. 예를 들어 목록 템플릿에는 사용자에게 표시할 수 있는 항목 수에 제한이 있습니다. 템플릿에는 작업 흐름을 형성하기 위해 연결할 수 있는 방법에도 제한이 있습니다. 예를 들어 앱은 화면 스택으로 템플릿을 최대 5개만 푸시할 수 있습니다. 자세한 내용은 템플릿 제한사항을 참고하세요.
Screen
Screen은 앱이 사용자에게 표시되는 사용자 인터페이스를 관리하기 위해 구현하는 라이브러리에서 제공하는 클래스입니다. Screen에는 lifecycle이 있고 화면이 표시될 때 앱에서 표시할 템플릿을 전송할 수 있는 메커니즘을 제공합니다. Screen 인스턴스는 화면 스택으로/에서 푸시되고 팝될 수도 있으므로 템플릿 흐름 제한사항을 준수할 수 있습니다.
CarAppService
CarAppService는 호스트에서 검색하고 관리하기 위해 앱에서 구현하고 내보내야 하는 추상 Service 클래스입니다. 앱의 CarAppServiceCarAppService.createHostValidator를 사용하여 호스트 연결을 신뢰할 수 있는지 검증한 후 CarAppService.onCreateSession을 사용하여 각 연결에 Session 인스턴스를 제공하는 역할을 합니다.
Session
Session은 앱에서 CarAppService.onCreateSession을 사용하여 구현하고 반환해야 하는 추상 클래스입니다. 자동차 화면에 정보를 표시하는 진입점 역할을 하고 앱이 표시되거나 숨겨질 때와 같이 자동차 화면에 앱의 현재 상태를 알려주는 수명 주기가 있습니다.

Session이 시작되면(예: 앱이 처음 실행될 때) 호스트에서는 Session.onCreateScreen 메서드를 사용하여 표시할 초기 Screen을 요청합니다.

라이브러리 설치

앱에 라이브러리를 추가하는 방법에 관한 안내는 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.PARKING"/>
      </intent-filter>
    </service>

    ...
<application>

지원되는 앱 카테고리

Android Auto용 Play 스토어에 등록하려면 앱이 지원되는 자동차 앱 카테고리 중 하나에 속해야 합니다. 자동차 앱 서비스를 선언할 때 인텐트 필터에서 다음 지원되는 카테고리 값 중 하나 이상을 추가하여 앱의 카테고리를 선언합니다.

  • androidx.car.app.category.NAVIGATION: 세부 경로 내비게이션 안내를 제공하는 앱입니다.
  • androidx.car.app.category.PARKING: 주차 위치 찾기와 관련된 기능을 제공하는 앱입니다.
  • androidx.car.app.category.CHARGING: 전기자동차 충전소 찾기와 관련된 기능을 제공하는 앱입니다.

각 카테고리에 속하는 앱에 관한 자세한 설명과 기준은 자동차용 Android 앱 품질을 참고하세요.

앱 이름 및 아이콘 지정

호스트가 시스템 UI에서 앱을 표시하는 데 사용할 수 있는 앱 이름과 아이콘을 지정해야 합니다.

CarAppServicelabelicon 요소를 사용하여 앱을 표시하는 데 사용되는 앱 이름과 아이콘을 지정할 수 있습니다.

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

service 요소에서 라벨이나 아이콘이 선언되지 않으면 호스트는 애플리케이션에 지정된 값으로 대체합니다.

앱의 minSdkVersion 설정

Android Auto를 사용하려면 앱이 Android 6.0(API 수준 23) 이상을 타겟팅해야 합니다.

프로젝트에서 이 값을 지정하려면 다음 예와 같이 전화 앱 모듈의 AndroidManifest.xml 파일에서 uses-sdk 요소의 minSdkVersion 속성을 23 이상으로 설정합니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
    ...
</manifest>

Android Auto 지원 선언

Android Auto 호스트는 앱이 Android Auto 지원을 선언했는지 확인합니다. 이 지원을 사용 설정하려면 앱 매니페스트에 다음 항목을 포함하세요.

<application>
    ...
    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>

이 매니페스트 항목은 경로를 YourAppProject/app/src/main/res/xml/automotive_app_desc.xml로 지정하여 만들어야 하는 또 하나의 XML 파일을 가리킵니다. 이 경로에서 개발자는 앱에서 지원하는 Android Auto 기능을 선언합니다.

자동차용 Android 앱 라이브러리를 사용하는 앱은 automotive_app_desc.xml 파일에서 template 기능을 선언해야 합니다.

<automotiveApp>
    <uses name="template" />
</automotiveApp>

CarAppService 및 세션 만들기

앱은 CarAppService 클래스를 확장하고 현재 호스트 연결에 상응하는 Session 인스턴스를 반환하는 CarAppService.onCreateSession 메서드를 구현해야 합니다.

public final class HelloWorldService extends CarAppService {
  ...
  @Override
  @NonNull
  public Session onCreateSession() {
    return new HelloWorldSession();
  }
  ...
}

Session 인스턴스는 앱이 처음 시작될 때 사용할 Screen 인스턴스를 반환해야 합니다.

public final class HelloWorldSession extends Session {
  ...
  @Override
  @NonNull
  public Screen onCreateScreen(@NonNull Intent intent) {
    return new HelloWorldScreen();
  }
  ...
}

자동차 앱이 앱의 홈 화면이나 랜딩 화면이 아닌 화면에서 시작해야 하는 시나리오를 처리하려면(예: 딥 링크 처리) ScreenManager.push를 사용하여 화면의 백 스택을 미리 시드할 수 있습니다. 이는 onCreateScreen에서 백 스택을 반환하기 전에 실행합니다. 미리 시드를 사용하면 사용자가 앱에서 표시하는 첫 화면에서 이전 화면으로 이동할 수 있습니다.

시작 화면 만들기

Screen 클래스를 확장하는 클래스를 정의하고 자동차 화면에 표시할 UI 상태를 나타내는 Template 인스턴스를 반환하는 Screen.onGetTemplate 메서드를 구현하여 앱에서 표시하는 화면을 만듭니다.

다음 스니펫은 PaneTemplate 템플릿을 사용하여 간단한 'Hello world!' 문자열을 표시하는 Screen을 선언하는 방법을 보여 줍니다.

public class HelloWorldScreen extends Screen {
  @NonNull
  @Override
  public Template onGetTemplate() {
    Pane pane = new Pane.Builder()
        .addRow(new Row.Builder()
            .setTitle("Hello world!")
            .build())
        .build();
    return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
  }
}

CarContext 클래스

CarContext 클래스는 SessionScreen 인스턴스에서 액세스할 수 있는 ContextWrapper 서브클래스로, 자동차 서비스 액세스 권한을 제공합니다. 예를 들면 화면 스택 관리용 ScreenManager, 일반 앱 관련 기능(예: 내비게이션 앱의 지도를 그리는 Surface 객체 액세스)용 AppManager, 세부 경로 안내 내비게이션 앱에서 내비게이션 메타데이터 및 기타 내비게이션 관련 이벤트를 호스트에 전달하는 NavigationManager가 있습니다. 내비게이션 앱에서 사용할 수 있는 전체 라이브러리 기능 목록은 내비게이션 템플릿에 액세스를 참고하세요.

CarContext는 자동차 화면에서 구성을 사용하여 드로어블 리소스를 로드하고 인텐트를 사용하여 자동차에서 앱을 시작하며 내비게이션 앱에서 어두운 모드로 지도를 표시해야 하는지 알릴 수 있는 기타 기능도 제공합니다.

화면 내비게이션 구현

앱에서는 사용자가 화면에 표시된 인터페이스와 상호작용할 때 탐색할 수 있는 여러 화면을 표시하는 경우가 많습니다(각 화면은 다양한 템플릿을 활용할 수 있음).

ScreenManager 클래스는 화면 스택을 제공하며 이 스택은 사용자가 자동차 화면에서 뒤로 버튼을 선택하거나 일부 자동차에 제공되는 하드웨어 뒤로 버튼을 선택할 때 자동으로 표시될 수 있는 화면을 푸시하는 데 사용할 수 있습니다.

다음 스니펫은 메시지 템플릿에 뒤로 작업을 추가하는 방법과 사용자가 선택할 때 새 화면을 푸시하는 작업을 보여 줍니다.

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(() -> getScreenManager().push(new NextScreen()))
            .build())
    .build();

Action.BACK 객체는 ScreenManager.pop을 자동으로 호출하는 표준 Action입니다. 이 동작은 CarContext에서 사용 가능한 OnBackPressedDispatcher 인스턴스를 사용하여 재정의할 수 있습니다.

운전하는 동안 앱이 안전하게 작동하도록 하기 위해 화면 스택에는 화면이 최대 5개 있을 수 있습니다. 자세한 내용은 템플릿 제한사항을 참고하세요.

템플릿 콘텐츠 새로고침

앱에서는 Screen.invalidate 메서드를 호출하여 Screen의 콘텐츠를 무효화하도록 요청할 수 있습니다. 그런 다음 호스트는 앱의 Screen.onGetTemplate 메서드를 다시 호출하여 새 콘텐츠가 있는 템플릿을 검색합니다.

Screen을 새로고침할 때 호스트가 새 템플릿을 템플릿 할당량에 포함하지 않도록 업데이트할 수 있는 템플릿의 특정 콘텐츠를 파악하는 것이 중요합니다. 자세한 내용은 템플릿 제한사항을 참고하세요.

ScreenScreen.onGetTemplate 구현을 통해 반환하는 템플릿 유형 간에 일대일 매핑이 있도록 화면을 구성하는 것이 좋습니다.

사용자 입력 처리

앱은 리스너를 지원하는 모델에 적절한 리스너를 전달하여 사용자 입력에 응답할 수 있습니다. 다음 스니펫은 Action 모델을 만드는 방법을 보여 주며 이 모델은 앱 코드에서 정의된 메서드를 다시 호출하는 OnClickListener를 설정합니다.

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

그러면 onClickNavigate 메서드는 CarContext.startCarApp 메서드를 사용하여 기본 내비게이션 자동차 앱을 시작할 수 있습니다.

private void onClickNavigate() {
  Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
  getCarContext().startCarApp(intent);
}

ACTION_NAVIGATE 인텐트 형식을 비롯하여 앱을 시작하는 방법에 관한 자세한 내용은 인텐트로 자동차 앱 시작을 참고하세요.

사용자가 휴대기기에서 상호작용을 계속하도록 안내해야 하는 일부 작업은 자동차가 주차된 때만 허용됩니다. ParkedOnlyOnClickListener를 사용하여 이러한 작업을 구현할 수 있습니다. 자동차가 주차되어 있지 않으면 호스트는 사용자에게 이 경우 허용되지 않는 작업이라는 메시지를 표시합니다. 자동차가 주차되어 있으면 코드가 정상적으로 실행됩니다. 다음 스니펫은 ParkedOnlyOnClickListener를 사용하여 휴대기기에서 설정 화면을 여는 방법을 보여 줍니다.

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(
        ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)
    .build();

알림 표시

휴대기기로 전송된 알림은 CarAppExtender로 확장된 경우에만 자동차 화면에 표시됩니다. 콘텐츠 제목, 텍스트, 아이콘, 작업과 같은 일부 알림 속성은 CarAppExtender에서 설정할 수 있으므로 자동차 화면에 표시될 때 알림의 속성을 재정의합니다.

다음 스니펫은 휴대기기에 표시된 것과 다른 제목을 표시하는 자동차 화면으로 알림을 전송하는 방법을 보여 줍니다.

Notification notification =
    new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
        .setContentTitle(titleOnThePhone)
        .extend(
            new CarAppExtender.Builder()
                .setContentTitle(titleOnTheCar)
                ...
                .build())
        .build();

알림은 다음과 같은 사용자 인터페이스 부분에 영향을 줄 수 있습니다.

  • 헤드업 알림(HUN)은 사용자에게 표시될 수 있습니다.
  • 알림 센터의 항목은 추가될 수 있고 선택적으로 배지가 레일에 표시됩니다.
  • 내비게이션 앱의 경우 세부 경로 안내 알림에 설명된 대로 알림이 레일 위젯에 표시될 수 있습니다.

애플리케이션에서는 CarAppExtender 문서에 설명된 대로 알림 우선순위를 사용하여 이러한 각 사용자 인터페이스 요소에 영향을 주는 알림을 구성하는 방법을 선택할 수 있습니다.

NotificationCompat.Builder.setOnlyAlertOncetrue 값으로 호출하면 우선순위가 높은 알림이 HUN으로 한 번만 표시됩니다.

자동차 앱의 알림을 디자인하는 방법에 관한 자세한 내용은 알림을 참고하세요.

토스트 메시지 표시

다음 스니펫과 같이 앱에서는 CarToast를 사용하여 토스트 메시지를 표시할 수 있습니다.

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

인텐트로 자동차 앱 시작

CarContext.startCarApp 메서드를 호출하여 다음 작업 중 하나를 실행할 수 있습니다.

  • 다이얼러를 열어 전화를 겁니다.
  • 기본 내비게이션 자동차 앱을 사용하여 위치를 찾는 세부 경로 안내 내비게이션을 시작합니다.
  • 인텐트로 자체 앱을 시작합니다.

다음 예는 주차 예약 세부정보가 표시된 화면에서 앱을 여는 작업으로 알림을 만드는 방법을 보여 줍니다. 명시적 인텐트를 앱 작업에 래핑하는 PendingIntent가 포함된 콘텐츠 인텐트로 알림 인스턴스를 확장합니다.

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

앱은 사용자가 알림 인터페이스에서 작업을 선택하고 데이터 URI 등의 인텐트로 CarContext.startCarApp를 호출할 때 인텐트를 처리하기 위해 호출되는 BroadcastReceiver도 선언해야 합니다.

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 메서드는 아직 상단에 있지 않은 경우 스택에서 주차 예약 화면을 푸시하여 이 인텐트를 처리합니다.

@Override
public void onNewIntent(@NonNull Intent intent) {
  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 screenManager = getCarContext().getCarService(ScreenManager.class);
      screenManager.push(new ParkingReservationScreen(getCarContext()));
    }
  }
}

자동차 앱의 알림을 처리하는 방법에 관한 자세한 내용은 알림 표시를 참고하세요.

템플릿 제한사항

호스트는 주어진 작업에 표시할 템플릿 수를 최대 5개로 제한하고 5개 중 마지막 템플릿은 다음 유형 중 하나여야 합니다.

  1. NavigationTemplate
  2. PaneTemplate
  3. MessageTemplate

템플릿 할당량이 소진되어 앱에서 새 템플릿을 전송하려고 하면 호스트는 사용자에게 오류 메시지를 표시합니다. 이 제한은 템플릿 수에 적용되고 스택의 Screen 인스턴스 수에는 적용되지 않습니다. 예를 들어 화면 A에서 앱이 템플릿을 2개 전송하고 화면 B를 푸시하면 이제 템플릿을 3개 더 전송할 수 있습니다. 또는 각 화면이 단일 템플릿을 전송하도록 구성된 경우 앱은 화면 인스턴스 5개를 ScreenManager 스택으로 푸시할 수 있습니다.

템플릿 새로고침, 뒤로 및 재설정 작업 등 이러한 제한사항에는 특수한 사례가 있습니다.

템플릿 새로고침

특정 콘텐츠 업데이트는 템플릿 제한에 포함되지 않습니다. 일반적으로 앱이 이전 템플릿과 유형이 동일하고 포함된 기본 콘텐츠도 동일한 새 템플릿을 푸시한다면 새 템플릿은 할당량에 포함되지 않습니다. 예를 들어 ListTemplate에서 행의 전환 상태를 업데이트해도 할당량에 포함되지 않습니다. 새로고침으로 간주할 수 있는 콘텐츠 업데이트 유형에 관한 자세한 내용은 개별 템플릿 문서를 참고하세요.

뒤로 작업

작업 내에서 하위 흐름을 사용 설정하기 위해 호스트는 앱이 ScreenManager 스택에서 Screen을 표시할 때를 감지하고 앱이 뒤로 이동할 템플릿 수에 따라 나머지 할당량을 업데이트합니다.

예를 들어 화면 A에서 앱이 템플릿을 2개 전송하고 화면 B를 푸시한 후 템플릿을 2개 더 전송하면 앱에 남은 할당량은 하나입니다. 앱이 다시 화면 A로 돌아오면 호스트는 할당량을 3으로 재설정합니다. 앱이 템플릿 2개만큼 뒤로 이동했기 때문입니다.

화면으로 다시 돌아올 때 앱은 이 화면에서 마지막으로 전송한 것과 동일한 유형의 템플릿을 전송해야 합니다. 다른 템플릿 유형을 전송하면 오류가 발생합니다. 그러나 뒤로 작업 시 유형이 동일하게 유지되는 한 앱은 할당량에 영향을 주지 않고 템플릿의 콘텐츠를 자유롭게 수정할 수 있습니다.

작업 재설정

특정 템플릿에는 작업의 끝을 나타내는 특수한 의미 체계가 있습니다. 예를 들어 NavigationTemplate은 화면에 계속 유지되고 사용자 소비를 위한 새로운 세부 경로 안내로 새로고침될 것으로 예상되는 뷰입니다. 이러한 템플릿 중 하나에 도달하면 호스트는 템플릿 할당량을 재설정하여 템플릿을 새 작업의 첫 번째 단계인 것처럼 간주하므로 앱에서 새 작업을 시작할 수 있습니다. 호스트에서 재설정을 트리거하는 템플릿은 개별 템플릿 문서를 참고하세요.

호스트가 인텐트를 수신하여 알림 작업이나 런처에서 앱을 시작하면 할당량도 재설정됩니다. 이 메커니즘으로 앱은 알림에서 새 작업 흐름을 시작할 수 있고 앱이 이미 바인딩되고 포그라운드에 있더라도 마찬가지입니다.

앱 알림을 자동차 화면에 표시하는 방법에 관한 자세한 내용은 알림 표시를, 알림 작업에서 앱을 시작하는 방법은 인텐트로 자동차 앱 시작을 참고하세요.

주차 또는 충전 앱 빌드

이 섹션에서는 주차 또는 충전 앱의 기능을 구현하는 데 활용할 수 있는 라이브러리의 다양한 기능을 자세하게 설명합니다.

매니페스트에서 카테고리 지원 선언

앱에서는 CarAppService의 인텐트 필터에서 androidx.car.app.category.PARKING 또는androidx.car.app.category.CHARGING 자동차 앱 카테고리를 선언해야 합니다. 예:

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

지도 템플릿에 액세스

앱은 호스트에서 렌더링되는 지도와 함께 관심 장소 목록을 표시하도록 특별히 설계된 PlaceListMapTemplate에 액세스할 수 있습니다.

이 템플릿에 액세스하려면 앱은 AndroidManifest.xml에서 androidx.car.app.MAP_TEMPLATES 권한을 선언해야 합니다.

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

내비게이션 앱 빌드

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

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

내비게이션 앱은 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: 활성 내비게이션 중에 선택적 정보 메시지나 경로 안내 및 예상 이동경로와 함께 지도를 표시합니다.
  • PlaceListNavigationTemplate: 상응하는 마커를 지도에 그릴 수 있는 장소 목록을 표시합니다.
  • RoutePreviewNavigationTemplate: 하나를 선택하여 지도에서 강조표시할 수 있는 경로 목록을 표시합니다.

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

내비게이션 템플릿에 액세스하려면 앱은 AndroidManifest.xml에서 androidx.car.app.NAVIGATION_TEMPLATES 권한을 선언해야 합니다.

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

지도 그리기

내비게이션 애플리케이션은 Surface에 액세스하여 관련 템플릿에 지도를 그릴 수 있습니다.

그러면 SurfaceCallback 인스턴스를 AppManager 자동차 서비스로 설정하여 SurfaceContainer 객체에 액세스할 수 있습니다.

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 자동차 서비스를 통해 제공됩니다.

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
내비게이션 시작, 종료, 중지

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

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

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

경로 정보

활성 내비게이션 중에 앱은 NavigationManager.updateTrip을 호출해야 합니다. 이 호출에서 제공된 정보는 차량의 계기판과 헤드업 디스플레이에 사용됩니다. 운전하는 특정 차량에 따라 사용자에게 일부 정보가 표시되지 않을 수도 있습니다.

정보가 계기판에 도달하는지 테스트하려면 간단한 계기판 디스플레이를 보여 주도록 데스크톱 헤드 단위(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 메서드를 사용하여 설정되지 않으면 알림 채널의 중요도가 사용됩니다.

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

NotificationCompat.Builder.setOnlyAlertOncetrue 값으로 호출하면 중요도가 높은 알림이 HUN에서 한 번만 표시됩니다.

다음 스니펫은 내비게이션 알림을 빌드하는 방법을 보여 줍니다.

new NotificationCompat.Builder(context, myNotificationChannelId)
    ...
    .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())
세부 경로 안내 알림 가이드라인

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

CarAppService, Session 및 Screen 수명 주기

SessionScreen 클래스는 LifecycleOwner 인터페이스를 구현합니다. 사용자가 앱과 상호작용할 때 SessionScreen 객체의 수명 주기 콜백이 다음 다이어그램과 같이 호출됩니다.

CarAppService 및 세션의 수명 주기

그림 1. Session 수명 주기

자세한 내용은 Session.getLifecycle 메서드 문서를 참고하세요.

화면의 수명 주기

그림 2. Screen 수명 주기

자세한 내용은 Screen.getLifecycle 문서를 참고하세요.

테스트 라이브러리

자동차용 Android 테스트 라이브러리는 테스트 환경에서 앱 동작을 확인하는 데 사용할 수 있는 보조 클래스를 제공합니다. 예를 들어 SessionController를 사용하면 호스트 연결을 시뮬레이션하고 ScreenTemplate이 정확하게 생성되고 반환되었는지 확인할 수 있습니다.

사용 예는 샘플을 참고하세요.

실제 헤드 단위에서 앱 실행

앱이 실제 헤드 단위(Google에서 제공하는 데스크톱 헤드 단위가 아님)에서 실행되려면 앱을 Google Play 스토어를 통해 배포해야 합니다. 그러면 애플리케이션이 Google 가이드라인을 준수하는지 테스트되고 검증됩니다. 이러한 가이드라인을 통해 애플리케이션이 자동차 환경과 관련되고 운전자 주의 분산 행동 테스트를 통과합니다.

개발 중에 테스트를 하려는 경우 두 가지 옵션이 있습니다. 하나는 데스크톱 헤드 단위이고 또 하나는 애플리케이션을 Google Play 스토어의 '내부 테스트 트랙'으로 푸시하는 것입니다. 내부 테스트 트랙을 사용하면 팀을 수동으로 추가하여 내부 테스트를 허용할 수 있습니다. 이 트랙의 버전에는 Play 스토어 검토가 필요하지 않습니다.

비공개 트랙을 비롯하여 다른 트랙으로 APK를 출시할 때마다 앱은 Play 스토어에서 트랙 승인을 받기 전에 검토 프로세스를 거칩니다. 애플리케이션이 검토 프로세스를 통과하지 못하면 그 이유에 관한 정보가 전송됩니다. 이 프로세스를 통해 문제를 해결하여 Google 가이드라인을 준수할 수 있습니다.

자동차용 Android 앱 라이브러리 문제 신고

라이브러리에 문제가 있다면 Google Issue Tracker를 사용하여 신고하세요. 문제 템플릿에 요청된 모든 정보를 작성해야 합니다.

새 문제 만들기

새로운 문제를 신고하기 전에 문제가 라이브러리의 출시 노트에 등록되어 있는지 또는 문제 목록에 보고되어 있는지 확인합니다. Tracker에서 문제의 별표를 클릭하여 문제를 구독하고 투표를 할 수 있습니다. 자세한 내용은 문제 구독을 참고하세요.