자동차용 Android 앱 라이브러리를 사용하면 내비게이션, 관심 장소 (POI), 사물 인터넷 (IOT) 앱을 자동차로 가져올 수 있습니다. 운전자 주의 분산 행동 표준을 충족하도록 설계된 일련의 템플릿을 제공하고 다양한 자동차 화면 요소 및 입력 모달리티와 같은 세부정보를 처리하여 이를 실행합니다.
이 가이드에서는 라이브러리의 주요 기능과 개념을 간략히 설명하고 기본 앱을 설정하는 과정을 안내합니다.
시작하기 전에
- 자동차 앱 라이브러리를 다루는 운전을 위한 디자인 페이지를 검토합니다.
- 내비게이션 앱 및 기타 운전 관련 앱 카테고리 개요
- 템플릿으로 앱 빌드 개요
- 템플릿 및 템플릿 구성요소를 다루는 구성요소
- 일반적인 UX 패턴을 보여주는 샘플 흐름
- 템플릿 형식 앱 요구사항
- 다음 섹션에서 주요 용어 및 개념을 살펴보세요.
- Android Auto 시스템 UI 및 Android Automotive OS 디자인을 숙지합니다.
- 출시 노트를 검토합니다.
- 샘플을 검토합니다.
주요 용어 및 개념
- 모델 및 템플릿
- 사용자 인터페이스는 모델이 속한 템플릿에서 허용하는 대로 다양한 방식으로 함께 정렬할 수 있는 모델 객체의 그래프로 표현됩니다. 템플릿은 이러한 그래프에서 루트 역할을 할 수 있는 모델의 하위 집합입니다. 모델에는 텍스트 및 이미지 형식으로 사용자에게 표시할 정보와 이러한 정보의 시각적 모양을 구성하는 속성(예: 텍스트 색상, 이미지 크기)이 포함됩니다. 호스트는 운전자 주의 분산 행동 표준을 충족하도록 설계된 뷰로 모델을 변환하고 다양한 자동차 화면 요소 및 입력 모달리티와 같은 세부정보를 처리합니다.
- 호스트
- 호스트는 앱이 자동차에서 실행될 수 있도록 라이브러리의 API에서 제공하는 기능을 구현하는 백엔드 구성요소입니다. 호스트의 역할은 앱 검색 및 앱 수명 주기 관리에서부터 모델을 뷰로 변환하고 앱에 사용자 상호작용을 알리는 것까지 다양합니다. 휴대기기에서 이 호스트는 Android Auto에 의해 구현됩니다. Android Automotive OS에서 이 호스트는 시스템 앱으로 설치됩니다.
- 템플릿 제한사항
- 다양한 템플릿이 모델의 콘텐츠에 제한을 적용합니다. 예를 들어 목록 템플릿에는 사용자에게 표시할 수 있는 항목 수에 제한이 있습니다. 템플릿에는 작업 흐름을 형성하기 위해 연결할 수 있는 방법에도 제한이 있습니다. 예를 들어 앱은 화면 스택에 템플릿을 최대 5개만 푸시할 수 있습니다. 자세한 내용은 템플릿 제한사항을 참고하세요.
Screen
Screen
는 앱이 사용자에게 표시되는 사용자 인터페이스를 관리하기 위해 구현하는 라이브러리에서 제공하는 클래스입니다.Screen
의 수명 주기는 화면이 표시될 때 앱에서 표시할 템플릿을 전송할 수 있는 메커니즘을 제공합니다.Screen
인스턴스는Screen
스택에 푸시되거나 팝될 수도 있으므로 템플릿 흐름 제한사항을 준수할 수 있습니다.CarAppService
CarAppService
는 호스트에서 검색하고 관리하기 위해 앱이 구현하고 내보내야 하는 추상Service
클래스입니다. 앱의CarAppService
은createHostValidator
를 사용하여 호스트 연결을 신뢰할 수 있는지 검증한 후onCreateSession
를 사용하여 각 연결에Session
인스턴스를 제공합니다.Session
Session
는 앱에서CarAppService.onCreateSession
를 사용하여 구현하고 반환해야 하는 추상 클래스입니다. 자동차 화면에 정보를 표시하는 진입점 역할을 합니다. 앱이 표시되거나 숨겨질 때와 같이 자동차 화면에 앱의 현재 상태를 알려주는 수명 주기가 있습니다.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.POI"/>
</intent-filter>
</service>
...
<application>
지원되는 앱 카테고리
이전 섹션에 설명된 대로 CarAppService
를 선언할 때 인텐트 필터에 다음 카테고리 값 중 하나 이상을 추가하여 앱의 카테고리를 선언합니다.
androidx.car.app.category.NAVIGATION
: 세부 경로 내비게이션 경로를 제공하는 앱입니다. 이 카테고리에 관한 추가 문서는 자동차용 내비게이션 앱 빌드를 참고하세요.androidx.car.app.category.POI
: 주차장, 충전소, 주유소 등 관심 장소를 찾는 것과 관련된 기능을 제공하는 앱입니다. 이 카테고리에 관한 추가 문서는 자동차용 관심 장소 앱 빌드를 참고하세요.androidx.car.app.category.IOT
: 사용자가 자동차 내에서 연결된 기기를 대상으로 관련 작업을 실행할 수 있는 앱입니다. 이 카테고리에 관한 추가 문서는 자동차용 사물 인터넷 앱 빌드를 참고하세요.
각 카테고리에 관한 자세한 설명과 카테고리에 속하는 앱 기준에 관한 자세한 내용은 자동차용 Android 앱 품질을 참고하세요.
앱 이름 및 아이콘 지정
호스트가 시스템 UI에서 앱을 표시하는 데 사용할 수 있는 앱 이름과 아이콘을 지정해야 합니다.
CarAppService
의 label
및 icon
속성을 사용하여 앱을 나타내는 데 사용되는 앱 이름과 아이콘을 지정할 수 있습니다.
...
<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 수준
자동차 앱 라이브러리는 자체 API 수준을 정의하므로 차량의 템플릿 호스트에서 지원하는 라이브러리 기능을 알 수 있습니다.
호스트에서 지원하는 가장 높은 자동차 앱 API 수준을 검색하려면 getCarAppApiLevel()
메서드를 사용합니다.
앱에서 지원하는 최소 자동차 앱 API 수준을 AndroidManifest.xml
파일에서 선언합니다.
<manifest ...>
<application ...>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"/>
</application>
</manifest>
이전 버전과의 호환성을 유지하고 기능을 사용하는 데 필요한 최소 API 수준을 선언하는 방법에 관한 자세한 내용은 RequiresCarApi
주석 문서를 참고하세요. 자동차 앱 라이브러리의 특정 기능을 사용하는 데 필요한 API 수준에 관한 정의는 CarAppApiLevels
참조 문서를 확인하세요.
CarAppService 및 세션 만들기
앱은 CarAppService
클래스를 확장하고 현재 호스트 연결에 상응하는 Session
인스턴스를 반환하는 onCreateSession
메서드를 구현해야 합니다.
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()); } ... }
자동차 앱이 앱의 홈 화면이나 랜딩 화면이 아닌 화면에서 시작해야 하는 시나리오(예: 딥 링크 처리)를 처리하려면 onCreateScreen
에서 반환하기 전에 ScreenManager.push
를 사용하여 화면의 백 스택을 미리 시드할 수 있습니다.
미리 시드를 사용하면 사용자가 앱에서 표시하는 첫 화면에서 이전 화면으로 이동할 수 있습니다.
시작 화면 만들기
앱에서 표시하는 화면을 만들려면 Screen
클래스를 확장하는 클래스를 정의하고 onGetTemplate
메서드를 구현하면 됩니다. 이 메서드는 자동차 화면에 표시할 UI의 상태를 나타내는 Template
인스턴스를 반환합니다.
다음 스니펫은 PaneTemplate
템플릿을 사용하여 간단한 'Hello world!' 문자열을 표시하는 Screen
를 선언하는 방법을 보여줍니다.
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
클래스는 Session
및 Screen
인스턴스에 액세스할 수 있는 ContextWrapper
서브클래스입니다. 자동차 서비스(예: 화면 스택를 관리하기 위한 ScreenManager
, 지도 그리기를 위한 Surface
객체에 액세스)와 같은 일반적인 앱 관련 기능을 위한 AppManager
, 세부 경로 안내 내비게이션 앱에서 사용하는 NavigationManager
, 내비게이션 메타데이터 및 기타 호스트와 내비게이션 메타데이터 및 기타 호스트에 대한 액세스를 제공합니다.{16
내비게이션 앱에 사용할 수 있는 전체 라이브러리 기능 목록은 내비게이션 템플릿에 액세스를 참고하세요.
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
객체는 ScreenManager.pop
을 자동으로 호출하는 표준 Action
입니다.
이 동작은 CarContext
에서 사용할 수 있는 OnBackPressedDispatcher
인스턴스를 사용하여 재정의할 수 있습니다.
운전 중에 앱을 안전하게 사용할 수 있도록 화면 스택의 최대 깊이는 화면이 5개일 수 있습니다. 자세한 내용은 템플릿 제한사항 섹션을 참고하세요.
템플릿 콘텐츠 새로고침
앱에서 Screen.invalidate
메서드를 호출하여 Screen
의 콘텐츠를 무효화하도록 요청할 수 있습니다.
이후 호스트는 앱의 Screen.onGetTemplate
메서드를 다시 호출하여 새 콘텐츠가 포함된 템플릿을 가져옵니다.
Screen
를 새로고침할 때 호스트가 템플릿 할당량에 새 템플릿을 집계하지 않도록 업데이트할 수 있는 템플릿의 특정 콘텐츠를 이해하는 것이 중요합니다.
자세한 내용은 템플릿 제한사항 섹션을 참고하세요.
Screen
와 onGetTemplate
구현을 통해 반환되는 템플릿 유형이 일대일로 매핑되도록 화면을 구성하는 것이 좋습니다.
지도 그리기
다음 템플릿을 사용하는 내비게이션 및 관심 장소 (POI) 앱은 Surface
에 액세스하여 지도를 그릴 수 있습니다.
템플릿 | 템플릿 권한 | 카테고리 안내 |
---|---|---|
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 |
탐색 |
노출 영역 권한 선언
앱에서 사용 중인 템플릿에 필요한 권한 외에도 앱은 AndroidManifest.xml
파일에서 androidx.car.app.ACCESS_SURFACE
권한을 선언해야 노출 영역에 액세스할 수 있습니다.
<manifest ...>
...
<uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
...
</manifest>
노출 영역 액세스
호스트에서 제공하는 Surface
에 액세스하려면 SurfaceCallback
를 구현하고 이 구현을 AppManager
자동차 서비스에 제공해야 합니다. 현재 Surface
는 onSurfaceAvailable()
및 onSurfaceDestroyed()
콜백의 SurfaceContainer
매개변수에 있는 SurfaceCallback
에 전달됩니다.
Kotlin
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
Java
carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);
표면에서 보이는 영역 이해하기
호스트는 지도 위에 여러 템플릿의 사용자 인터페이스 요소를 그릴 수 있습니다. 호스트는 SurfaceCallback.onVisibleAreaChanged
메서드를 호출하여 방해물이 없고 사용자에게 완전히 표시되는 노출 영역 영역을 전달합니다. 변경 횟수를 최소화하기 위해 호스트는 현재 템플릿에 따라 항상 표시되는 가장 작은 직사각형을 사용하여 SurfaceCallback.onStableAreaChanged
메서드도 호출합니다.
예를 들어 내비게이션 앱에서 상단에 작업 스트립이 있는 NavigationTemplate
를 사용하는 경우 작업 스트립은 사용자가 지도 공간을 더 만들기 위해 한동안 화면과 상호작용하지 않을 때 자신을 숨길 수 있습니다. 이 경우에는 동일한 직사각형을 사용하는 onStableAreaChanged
및 onVisibleAreaChanged
콜백이 있습니다. 작업 표시줄이 숨겨지면 onVisibleAreaChanged
만 더 큰 영역으로 호출됩니다. 사용자가 화면과 상호작용하면 다시 첫 번째 직사각형으로 onVisibleAreaChanged
만 호출됩니다.
어두운 테마 지원
앱은 자동차용 Android 앱 품질에 설명된 대로 호스트가 조건이 충족된다고 판단하면 적절한 어두운 색상으로 Surface
인스턴스에 지도를 다시 그려야 합니다.
어두운 지도를 그려야 하는지 판단하려면 CarContext.isDarkMode
메서드를 사용하면 됩니다. 어두운 테마 상태가 변경될 때마다 Session.onCarConfigurationChanged
호출을 수신합니다.
사용자가 지도와 상호작용할 수 있도록 지원
다음 템플릿을 사용하면 확대/축소 및 화면 이동을 통해 지도의 여러 부분을 볼 수 있는 등 사용자가 그린 지도와 상호작용할 수 있는 지원을 추가할 수 있습니다.
템플릿 | 자동차 앱 API 수준 이후 지원되는 상호작용 |
---|---|
NavigationTemplate |
2 |
PlaceListNavigationTemplate (지원 중단됨) |
4 |
RoutePreviewNavigationTemplate (지원 중단됨) |
4 |
MapTemplate (지원 중단됨) |
5 (템플릿 소개) |
MapWithContentTemplate |
7 (템플릿 소개) |
상호작용 콜백 구현
SurfaceCallback
인터페이스에는
이전 섹션의 템플릿으로 빌드된 지도에 상호작용을 추가하기 위해
구현할 수 있는 여러 콜백 메서드가 있습니다.
상호작용 | SurfaceCallback 메서드 |
자동차 앱 API 수준 이후 지원됨 |
---|---|---|
탭 | onClick |
5 |
손가락 모으기(확대/축소) | onScale |
2 |
원터치 드래그 | onScroll |
2 |
원터치 플링 | onFling |
2 |
두 번 탭 | onScale (템플릿 호스트에 의해 결정되는 배율 사용) |
2 |
화면 이동 모드에서 로터리 이동 | onScroll (템플릿 호스트에서 결정하는 거리 계수 사용) |
2 |
지도 작업 스트립 추가
이러한 템플릿에는 확대/축소, 중심 재설정, 나침반 표시 및 표시하도록 선택한 기타 작업과 같은 지도 관련 작업을 위한 지도 작업 스트립이 있을 수 있습니다. 지도 작업 스트립에는 작업 깊이에 영향을 주지 않고 새로고침할 수 있는 아이콘 전용 버튼이 최대 4개까지 있을 수 있습니다. 유휴 상태 시 숨겨지고 활성 상태에 다시 표시됩니다.
지도 상호작용 콜백을 수신하려면 반드시 지도 작업 스트립에 Action.PAN
버튼을 추가해야 합니다. 사용자가 화면 이동 버튼을 누르면 호스트가 다음 섹션에 설명된 대로 화면 이동 모드로 전환됩니다.
앱이 지도 작업 스트립에서 Action.PAN
버튼을 생략하면 SurfaceCallback
메서드로부터 사용자 입력을 수신하지 않게 되고 호스트는 이전에 활성화된 화면 이동 모드를 종료합니다.
터치스크린에는 화면 이동 버튼이 표시되지 않습니다.
화면 이동 모드 이해하기
화면 이동 모드에서는 템플릿 호스트가 터치 입력을 사용하지 않는 기기(예: 로터리 컨트롤러, 터치패드)의 사용자 입력을 적절한 SurfaceCallback
메서드로 변환합니다. 사용자 작업에 관한 응답으로 NavigationTemplate.Builder
의 setPanModeListener
메서드를 사용하여 화면 이동 모드에 진입하거나 화면 이동 모드를 종료합니다. 사용자가 화면 이동 모드에 있는 동안 호스트는 템플릿에서 다른 UI 구성요소를 숨길 수 있습니다.
사용자와 상호작용
앱은 모바일 앱과 유사한 패턴을 사용하여 사용자와 상호작용할 수 있습니다.
사용자 입력 처리
앱은 리스너를 지원하는 모델에 적절한 리스너를 전달하여 사용자 입력에 응답할 수 있습니다. 다음 스니펫은 앱 코드에 정의된 메서드를 다시 호출하는 OnClickListener
를 설정하는 Action
모델을 만드는 방법을 보여줍니다.
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
값으로 호출하면 우선순위가 높은 알림이 한 번만 HUN으로 표시됩니다.
자동차 앱의 알림을 디자인하는 방법에 관한 자세한 내용은 알림에 관한 Google 운전용 디자인 가이드를 참고하세요.
토스트 메시지 표시
다음 스니펫과 같이 앱은 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());
앱은 사용자가 알림 인터페이스에서 작업을 선택하고 데이터 URI가 포함된 인텐트로 CarContext.startCarApp
를 호출할 때 인텐트를 처리하기 위해 호출되는 BroadcastReceiver
도 선언해야 합니다.
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())); } } }
자동차 앱의 알림을 처리하는 방법에 관한 자세한 내용은 알림 표시 섹션을 참고하세요.
템플릿 제한사항
호스트는 지정된 작업에 표시할 템플릿 수를 최대 5개로 제한하며 그중 마지막 템플릿은 다음 유형 중 하나여야 합니다.
이 제한은 템플릿 수에 적용되며 스택의 Screen
인스턴스 수에는 적용되지 않습니다. 예를 들어 앱이 화면 A에 있는 동안 템플릿 2개를 전송하고 화면 B를 푸시하면 이제 템플릿을 3개 더 전송할 수 있습니다. 또는 각 화면이 단일 템플릿을 전송하도록 구성된 경우 앱은 화면 인스턴스 5개를 ScreenManager
스택으로 푸시할 수 있습니다.
이러한 제한사항에는 템플릿 새로고침, 뒤로 및 재설정 작업과 같은 특수한 사례가 있습니다.
템플릿 새로고침
특정 콘텐츠 업데이트는 템플릿 제한에 포함되지 않습니다. 일반적으로 앱이 이전 템플릿과 유형이 동일하고 기본 콘텐츠가 동일한 새 템플릿을 푸시하면 새 템플릿은 할당량에 포함되지 않습니다. 예를 들어 ListTemplate
에서 행의 전환 상태를 업데이트해도 할당량에 포함되지 않습니다. 새로고침으로 간주할 수 있는 콘텐츠 업데이트 유형에 관한 자세한 내용은 개별 템플릿 문서를 참고하세요.
뒤로 작업
작업 내에서 하위 흐름을 사용 설정하기 위해 호스트는 앱이 ScreenManager
스택에서 Screen
를 표시할 때를 감지하고 앱이 뒤로 이동하는 템플릿 수에 따라 남은 할당량을 업데이트합니다.
예를 들어 앱이 화면 A에 있는 동안 템플릿 2개를 전송한 다음 화면 B를 푸시하고 템플릿을 2개 더 전송하면 앱에는 남은 할당량은 1개입니다. 그런 다음 앱이 화면 A로 다시 돌아오면 호스트는 할당량을 3으로 재설정합니다. 앱이 템플릿 두 개만큼 뒤로 이동했기 때문입니다.
화면으로 다시 돌아올 때 앱은 화면에서 마지막으로 전송한 템플릿과 동일한 유형의 템플릿을 전송해야 합니다. 다른 템플릿 유형을 전송하면 오류가 발생합니다. 그러나 뒤로 작업 시 유형이 동일하게 유지되는 한 앱은 할당량에 영향을 주지 않고 템플릿의 콘텐츠를 자유롭게 수정할 수 있습니다.
작업 재설정
특정 템플릿에는 작업의 끝을 나타내는 특수한 의미 체계가 있습니다. 예를 들어 NavigationTemplate
는 화면에 유지되고 사용자 소비에 관한 새로운 세부 경로 안내로 새로고침될 것으로 예상되는 뷰입니다. 템플릿 중 하나에 도달하면 호스트는 템플릿 할당량을 재설정하여 템플릿을 새 작업의 첫 번째 단계인 것처럼 처리합니다. 이렇게 하면 앱이 새 작업을 시작할 수 있습니다.
호스트에서 재설정을 트리거하는 템플릿은 개별 템플릿 문서를 참고하세요.
호스트가 알림 작업이나 런처에서 앱을 시작하기 위한 인텐트를 수신하면 할당량도 재설정됩니다. 이 메커니즘을 통해 앱은 알림에서 새 작업 흐름을 시작할 수 있으며, 앱이 이미 바인딩되어 있고 포그라운드에 있더라도 마찬가지입니다.
앱의 알림을 자동차 화면에 표시하는 방법에 관한 자세한 내용은 알림 표시 섹션을 참고하세요. 알림 작업에서 앱을 시작하는 방법에 관한 자세한 내용은 인텐트로 자동차 앱 시작 섹션을 참고하세요.
연결 API
런타임에 연결 정보를 검색하는 CarConnection
API를 사용하여 앱이 Android Auto 또는 Android Automotive OS에서 실행되는지 확인할 수 있습니다.
예를 들어 자동차 앱의 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
객체에 관련 콘텐츠 한도를 쿼리할 수 있습니다. 예를 들어 그리드에 표시할 수 있는 항목의 수를 가져오려면 CONTENT_LIMIT_TYPE_GRID
를 사용하여 getContentLimit
를 호출합니다.
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
로그인 흐름 추가
앱에서 사용자에게 로그인 환경을 제공하는 경우 자동차 앱 API 수준 2 이상에서 SignInTemplate
및 LongMessageTemplate
과 같은 템플릿을 사용하여 자동차 헤드 단위에서 앱 로그인을 처리할 수 있습니다.
SignInTemplate
를 만들려면 SignInMethod
를 정의합니다. 자동차 앱 라이브러리는 현재 다음과 같은 로그인 방법을 지원합니다.
InputSignInMethod
: 사용자 이름/비밀번호 로그인PinSignInMethod
: 사용자가 헤드 단위에 표시된 PIN을 사용하여 휴대전화에서 계정을 연결하는 PIN 로그인ProviderSignInMethod
: 제공업체 로그인(예: Google 로그인 및 원탭)QRCodeSignInMethod
: 사용자가 QR 코드를 스캔하여 휴대전화에서 로그인을 완료하는 QR 코드 로그인 이 기능은 자동차 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. } };
InputCallback
은 InputSignInMethod
Builder
에 필요합니다.
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를 사용해야 합니다.
- 향상된 UX 및 간편한 계정 관리: 사용자는 로그인 및 로그아웃을 포함하여 시스템 설정의 계정 메뉴에서 모든 계정을 쉽게 관리할 수 있습니다.
- '게스트' 환경: 자동차는 공유 기기이므로 OEM은 계정을 추가할 수 없는 차량에 게스트 환경을 사용 설정할 수 있습니다.
텍스트 문자열 변형 추가
자동차 화면 크기에 따라 표시되는 텍스트양이 다를 수 있습니다. 자동차 앱 API 수준 2 이상에서는 화면에 가장 적합하도록 텍스트 문자열의 변형을 여러 가지로 지정할 수 있습니다. 텍스트 변형이 허용되는 위치를 확인하려면 CarText
를 사용하는 템플릿 및 구성요소를 찾아야 합니다.
CarText.Builder.addVariant()
메서드를 사용하여 CarText
에 텍스트 문자열 변형을 추가할 수 있습니다.
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
문서를 참고하세요. 스팬을 사용한 텍스트 스타일 지정의 작동 방식에 관한 개요는 스팬으로 멋진 텍스트 스타일 지정을 참고하세요.
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
자동차 앱 API 수준 3부터 자동차 앱 라이브러리에는 차량 속성 및 센서에 액세스하는 데 사용할 수 있는 API가 있습니다.
요구사항
Android Auto에서 API를 사용하려면 먼저 androidx.car.app:app-projected
의 종속 항목을 Android Auto 모듈의 build.gradle
파일에 추가합니다. Android Automotive OS의 경우 androidx.car.app:app-automotive
의 종속 항목을 Android Automotive OS 모듈의 build.gradle
파일에 추가합니다.
또한 AndroidManifest.xml
파일에서 사용하려는 자동차 데이터를 요청하는 데 필요한 관련 권한을 선언해야 합니다. 이러한 권한은 사용자가 개발자에게 부여해야 합니다. 플랫폼에 종속된 흐름을 만드는 대신 Android Auto와 Android Automotive OS에서 동일한 코드를 사용하면 됩니다. 그러나 필요한 권한은 다릅니다.
자동차 정보
다음 표에서는 CarInfo
API에서 표시하는 속성과 이를 사용하기 위해 요청해야 하는 권한을 설명합니다.
메서드 | 속성 | Android Auto 권한 | Android Automotive OS 권한 | 자동차 앱 API 수준 이후 지원됨 |
---|---|---|---|---|
fetchModel |
제조업체, 모델, 연도 | android.car.permission.CAR_INFO |
3 | |
fetchEnergyProfile |
전기자동차 커넥터 유형, 연료 유형 | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
3 |
fetchExteriorDimensions
이 데이터는 API 30 이상을 실행하는 일부 Android Automotive OS 차량에서만 사용할 수 있습니다. |
외부 크기 | N/A | 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에서 테스트할 때 센서 데이터를 시뮬레이션하려면 Android Automotive OS 에뮬레이터 가이드의 하드웨어 상태 에뮬레이션 섹션을 참고하세요.
CarAppService, Session 및 Screen 수명 주기
Session
및 Screen
클래스는 LifecycleOwner
인터페이스를 구현합니다. 다음 다이어그램과 같이 사용자가 앱과 상호작용할 때 Session
및 Screen
객체의 수명 주기 콜백이 호출됩니다.
CarAppService 및 세션의 수명 주기
![](https://developer.android.com/static/images/training/cars/carappservice-session-lifecycle.png?authuser=9&hl=ko)
Session
수명 주기자세한 내용은 Session.getLifecycle
메서드에 관한 문서를 참고하세요.
화면의 수명 주기
![](https://developer.android.com/static/images/training/cars/screen-lifecycle.png?authuser=9&hl=ko)
Screen
수명 주기자세한 내용은 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 앱 라이브러리 문제 신고
라이브러리에 문제가 있다면 Google Issue Tracker를 사용하여 신고하세요. 문제 템플릿에 요청된 모든 정보를 작성해야 합니다.
새로운 문제를 신고하기 전에 문제가 라이브러리의 출시 노트에 등록되어 있는지 또는 문제 목록에 보고되어 있는지 확인하세요. Tracker에서 문제의 별표를 클릭하여 문제를 구독하고 투표를 할 수 있습니다. 자세한 내용은 문제 구독을 참고하세요.