Sử dụng Thư viện ứng dụng Android cho Ô tô

Thư viện ứng dụng Android cho Ô tô cho phép bạn mang các ứng dụng điều hướng, địa điểm yêu thích (POI) và Internet của vạn vật (IOT) lên ô tô. Thư viện này cung cấp một bộ mẫu được thiết kế để đáp ứng các tiêu chuẩn về sự phân tâm của người lái xe, đồng thời quan tâm đến những chi tiết như sự đa dạng của các yếu tố trên màn hình ô tô và phương thức đầu vào.

Hướng dẫn này cung cấp thông tin tổng quan về các tính năng và khái niệm chính của thư viện, đồng thời hướng dẫn bạn qua quy trình thiết lập một ứng dụng đơn giản. Để biết phần giới thiệu từng bước đầy đủ, hãy xem lớp học lập trình Tìm hiểu kiến thức cơ bản về Thư viện ứng dụng cho ô tô.

Trước khi bắt đầu

  1. Xem các trang Design for Driving (Design cho hoạt động lái xe) bao gồm Thư viện ứng dụng cho ô tô
  2. Xem các thuật ngữ và khái niệm chính trong phần sau.
  3. Làm quen với Giao diện người dùng hệ thống Android Automẫu thiết kế Android Automotive OS.
  4. Xem phần Ghi chú phát hành.
  5. Xem phần Ứng dụng mẫu.

Các thuật ngữ và khái niệm chính

Mô hình và mẫu
Giao diện người dùng được biểu thị bằng một biểu đồ gồm các đối tượng mô hình có thể được sắp xếp cùng nhau theo nhiều cách, tuỳ theo mẫu chứa chúng cho phép. Mẫu là một tập hợp con gồm các mô hình có thể đóng vai trò là gốc trong các biểu đồ đó. Mô hình bao gồm thông tin sẽ hiển thị với người dùng dưới dạng văn bản và hình ảnh, cũng như các thuộc tính để định cấu hình các khía cạnh về giao diện của thông tin đó, ví dụ: màu văn bản hoặc kích thước hình ảnh. Máy chủ lưu trữ chuyển đổi các mô hình thành những khung hiển thị được thiết kế để đáp ứng các tiêu chuẩn về sự phân tâm của người lái xe, đồng thời quan tâm đến những chi tiết như sự đa dạng của các yếu tố trên màn hình ô tô và phương thức đầu vào.
Máy chủ lưu trữ
Máy chủ lưu trữ là thành phần phụ trợ giúp triển khai chức năng do API của thư viện cung cấp để ứng dụng của bạn có thể chạy trên ô tô. Máy chủ lưu trữ đảm nhận nhiều trách nhiệm, từ việc khám phá ứng dụng và quản lý vòng đời của ứng dụng cho đến việc chuyển đổi mô hình thành khung hiển thị, cũng như thông báo cho ứng dụng về hoạt động tương tác của người dùng. Trên các thiết bị di động, máy chủ lưu trữ này được Android Auto triển khai. Trên Android Automotive OS, máy chủ lưu trữ này được cài đặt dưới dạng một ứng dụng hệ thống.
Các hạn chế khi dùng mẫu
Các mẫu khác nhau thực thi các hạn chế về nội dung trong mô hình của chúng. Ví dụ: mẫu dạng danh sách có giới hạn về số lượng mục có thể hiển thị với người dùng. Các mẫu cũng có những hạn chế về cách kết nối để tạo thành luồng nhiệm vụ. Ví dụ: ứng dụng chỉ có thể đẩy tối đa 5 mẫu vào ngăn xếp màn hình. Hãy xem phần Các hạn chế khi dùng mẫu để biết thêm thông tin.
Screen
Screen là một lớp do thư viện cung cấp. Đây chính là lớp mà các ứng dụng triển khai để quản lý giao diện người dùng được hiển thị với người dùng. Một Screen có một vòng đời và cung cấp cơ chế để ứng dụng gửi mẫu cần hiển thị khi người dùng nhìn thấy màn hình. Bạn cũng có thể đẩy và kéo các thực thể Screen đến và từ một ngăn xếp Screen. Điều này đảm bảo các thực thể đó tuân thủ các hạn chế về luồng mẫu.
CarAppService
CarAppService là một lớp Service trừu tượng mà ứng dụng của bạn phải triển khai và xuất để được máy chủ lưu trữ khám phá và quản lý. CarAppService của ứng dụng chịu trách nhiệm xác thực rằng kết nối của máy chủ lưu trữ có thể tin cậy được bằng cách sử dụng createHostValidator và sau đó cung cấp các thực thể Session cho từng kết nối bằng onCreateSession.
Session

Session là một lớp trừu tượng mà ứng dụng của bạn phải triển khai và trả về bằng CarAppService.onCreateSession. Lớp này đóng vai trò là điểm vào để hiển thị thông tin trên màn hình ô tô. Nó có vòng đời thông báo trạng thái hiện tại của ứng dụng trên màn hình ô tô, chẳng hạn như thời điểm ứng dụng hiển thị hoặc bị ẩn.

Khi Session khởi động, chẳng hạn như khi ứng dụng chạy lần đầu tiên, máy chủ lưu trữ yêu cầu Screen ban đầu hiển thị bằng phương thức onCreateScreen.

Cài đặt Thư viện ứng dụng cho ô tô

Xem trang phát hành thư viện Jetpack để biết hướng dẫn về cách thêm thư viện vào ứng dụng của bạn.

Định cấu hình tệp kê khai của ứng dụng

Trước khi bạn có thể tạo ứng dụng cho ô tô, hãy định cấu hình tệp kê khai của ứng dụng như sau.

Khai báo CarAppService

Máy chủ lưu trữ kết nối ứng dụng của bạn thông qua quá trình triển khai CarAppService. Bạn khai báo dịch vụ này trong tệp kê khai của mình để cho phép máy chủ lưu trữ khám phá và kết nối với ứng dụng của bạn.

Bạn cũng cần khai báo danh mục ứng dụng của mình trong phần tử <category> trong bộ lọc ý định của ứng dụng. Xem danh sách các danh mục ứng dụng được hỗ trợ để biết các giá trị được phép cho phần tử này.

Đoạn mã sau đây cho biết cách khai báo dịch vụ ứng dụng ô tô cho ứng dụng địa điểm yêu thích trong tệp kê khai của bạn:

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

Danh mục ứng dụng được hỗ trợ

Khai báo danh mục ứng dụng của bạn bằng cách thêm một hoặc nhiều giá trị cho danh mục sau vào bộ lọc ý định khi bạn khai báo CarAppService của mình như được mô tả trong phần trước:

  • androidx.car.app.category.NAVIGATION: một ứng dụng cung cấp thông tin chỉ đường từng chặng. Hãy xem phần Tạo ứng dụng chỉ đường cho ô tô để biết thêm tài liệu về danh mục này.
  • androidx.car.app.category.POI: một ứng dụng cung cấp chức năng liên quan đến việc tìm kiếm các địa điểm yêu thích như điểm đỗ xe, trạm sạc và trạm xăng. Hãy xem phần Tạo ứng dụng địa điểm yêu thích cho ô tô để biết thêm tài liệu về danh mục này.
  • androidx.car.app.category.IOT: Một ứng dụng cho phép người dùng thực hiện các thao tác liên quan trên các thiết bị thông minh ngay trên ô tô. Hãy xem phần Tạo ứng dụng Internet của vạn vật cho ô tô để biết thêm tài liệu về danh mục này.

Hãy xem phần Chất lượng ứng dụng Android cho ô tô để biết nội dung mô tả chi tiết của từng danh mục và tiêu chí xếp ứng dụng vào các danh mục đó.

Chỉ định tên và biểu tượng ứng dụng

Bạn cần chỉ định tên và biểu tượng ứng dụng mà máy chủ lưu trữ có thể dùng để biểu thị ứng dụng của bạn trong giao diện người dùng hệ thống.

Bạn có thể chỉ định tên và biểu tượng ứng dụng dùng để biểu thị ứng dụng của bạn bằng cách dùng thuộc tính labelicon trong CarAppService:

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

Nếu nhãn hoặc biểu tượng không được khai báo trong phần tử <service>, máy chủ lưu trữ sẽ quay lại dùng các giá trị được chỉ định cho phần tử <application>.

Đặt giao diện tuỳ chỉnh

Để đặt giao diện tuỳ chỉnh cho ứng dụng dành cho ô tô, hãy thêm phần tử <meta-data> vào tệp kê khai của bạn như sau:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Sau đó, khai báo tài nguyên kiểu để đặt các thuộc tính sau đây cho giao diện tuỳ chỉnh của ứng dụng dành cho ô tô:

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

Cấp độ Car App API (API Ứng dụng dành cho ô tô)

Thư viện ứng dụng cho ô tô xác định các cấp độ API riêng để bạn có thể biết tính năng nào của thư viện được máy chủ lưu trữ mẫu trên xe hỗ trợ. Để truy xuất Cấp độ Car App API cao nhất được máy chủ lưu trữ hỗ trợ, hãy sử dụng phương thức getCarAppApiLevel().

Khai báo Cấp độ Car App API thấp nhất được ứng dụng của bạn hỗ trợ trong tệp AndroidManifest.xml:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

Xem tài liệu liên quan đến chú giải RequiresCarApi để biết chi tiết về cách duy trì khả năng tương thích ngược và khai báo cấp độ API thấp nhất cần thiết để sử dụng một tính năng. Để biết định nghĩa về cấp độ API nào cần cho việc sử dụng một tính năng nhất định của Thư viện ứng dụng cho ô tô, hãy xem tài liệu tham khảo về CarAppApiLevels.

Tạo CarAppService và Phiên của bạn

Ứng dụng của bạn cần mở rộng lớp CarAppService và triển khai phương thức onCreateSession của lớp để trả về thực thể Session tương ứng với kết nối hiện tại đến máy chủ lưu trữ:

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

Thực thể Session chịu trách nhiệm trả về thực thể Screen để dùng trong lần đầu tiên khởi động ứng dụng:

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

Để xử lý các tình huống trong đó ứng dụng mà bạn tạo cho ô tô cần khởi động từ một màn hình không phải là màn hình chính hoặc màn hình đích của ứng dụng, chẳng hạn như xử lý các đường liên kết sâu, bạn có thể chuẩn bị trước một ngăn xếp lui của các màn hình bằng cách sử dụng ScreenManager.push trước khi quay lại từ onCreateScreen. Khi bạn chuẩn bị trước một ngăn xếp lui, người dùng có thể từ màn hình đầu tiên mà ứng dụng của bạn đang hiển thị quay lại các màn hình trước đó.

Tạo màn hình bắt đầu

Bạn tạo các màn hình do ứng dụng của bạn hiển thị bằng cách xác định các lớp mở rộng lớp Screen, đồng thời triển khai phương thức onGetTemplate của lớp để trả về thực thể Template biểu thị trạng thái của giao diện người dùng cần hiển thị trên màn hình ô tô.

Đoạn mã sau đây cho biết cách khai báo Screen sử dụng một mẫu PaneTemplate để hiển thị chuỗi "Hello world!":

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

Lớp CarContext

Lớp CarContext là một lớp con ContextWrapper có thể truy cập vào thực thể SessionScreen của bạn. Lớp này cung cấp quyền truy cập vào các dịch vụ dành cho ô tô, chẳng hạn như ScreenManager để quản lý ngăn xếp màn hình; AppManager cho chức năng chung liên quan đến ứng dụng (chẳng hạn như truy cập vào đối tượng Surface để vẽ bản đồ của ứng dụng chỉ đường); và NavigationManager được các ứng dụng chỉ đường từng chặng sử dụng để truyền đạt siêu dữ liệu chỉ đường cũng như các sự kiện khác liên quan đến việc chỉ đường với máy chủ lưu trữ.

Hãy xem phần Truy cập vào mẫu chỉ đường để biết danh sách đầy đủ chức năng của thư viện có sẵn cho các ứng dụng chỉ đường.

CarContext cũng cung cấp chức năng khác, chẳng hạn như cho phép bạn dùng cấu hình từ màn hình ô tô để tải tài nguyên có thể vẽ, dùng các ý định để khởi động một ứng dụng trên ô tô, đồng thời cho biết liệu ứng dụng chỉ đường của bạn có nên hiển thị bản đồ ở chế độ tối hay không.

Triển khai hoạt động chỉ đường trên màn hình

Các ứng dụng thường hiển thị một số màn hình khác nhau, mỗi màn hình có thể sử dụng các mẫu khác nhau mà người dùng có thể điều hướng khi tương tác với giao diện hiển thị trên màn hình.

Lớp ScreenManager mang đến một ngăn xếp màn hình giúp bạn đẩy các màn hình có thể bật tự động khi người dùng chọn nút quay lại trên màn hình ô tô hoặc sử dụng nút quay lại phần cứng hiện có trong một số mẫu ô tô.

Đoạn mã sau đây cho biết cách thêm thao tác quay lại vào tin nhắn mẫu, cũng như thao tác đẩy một màn hình mới khi được người dùng chọn:

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

Đối tượng Action.BACK là một Action tiêu chuẩn sẽ tự động gọi ScreenManager.pop. Bạn có thể dùng thực thể OnBackPressedDispatcher trong CarContext để ghi đè hành vi này.

Để giúp đảm bảo ứng dụng an toàn khi sử dụng trong quá trình lái xe, ngăn xếp màn hình có thể có độ sâu tối đa là 5 màn hình. Hãy xem phần Các hạn chế khi dùng mẫu để biết thêm thông tin chi tiết.

Làm mới nội dung của một mẫu

Ứng dụng của bạn có thể yêu cầu vô hiệu hoá nội dung của một Screen bằng cách gọi phương thức Screen.invalidate. Sau đó, máy chủ lưu trữ sẽ gọi lại phương thức Screen.onGetTemplate của ứng dụng để truy xuất mẫu có nội dung mới.

Khi làm mới Screen, bạn phải hiểu rằng có thể cập nhật nội dung cụ thể trong mẫu đó để máy chủ lưu trữ không tính mẫu mới vào hạn mức mẫu. Hãy xem phần Các hạn chế khi dùng mẫu để biết thêm thông tin.

Bạn nên cấu trúc các màn hình sao cho có mối liên kết một với một giữa Screen và kiểu mẫu trả về thông qua quá trình triển khai onGetTemplate.

Tương tác với người dùng

Ứng dụng của bạn có thể tương tác với người dùng bằng các mẫu tương tự như ứng dụng di động.

Xử lý hoạt động đầu vào của người dùng

Ứng dụng của bạn có thể phản hồi hoạt động đầu vào của người dùng bằng cách chuyển các trình nghe thích hợp đến các mô hình hỗ trợ chúng. Đoạn mã sau đây cho biết cách tạo một mô hình Action sẽ đặt OnClickListener để gọi lại phương thức do mã của ứng dụng xác định:

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

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

Sau đó, phương thức onClickNavigate có thể khởi động ứng dụng chỉ đường mặc định trên ô tô bằng cách dùng phương thức 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);
}

Để biết thêm thông tin về cách khởi động các ứng dụng, bao gồm cả hình thức của ý định ACTION_NAVIGATE, hãy xem phần Khởi động ứng dụng dành cho ô tô bằng ý định.

Một số thao tác, chẳng hạn như các thao tác yêu cầu hướng dẫn người dùng tiếp tục tương tác trên các thiết bị di động của họ, chỉ được phép thực hiện khi ô tô đã đỗ. Bạn có thể dùng ParkedOnlyOnClickListener để triển khai các thao tác đó. Nếu ô tô chưa đỗ, máy chủ lưu trữ sẽ hiển thị một chỉ báo cho người dùng biết rằng thao tác đó không được phép thực hiện trong trường hợp này. Nếu ô tô đang đỗ, mã sẽ thực thi bình thường. Đoạn mã sau cho biết cách sử dụng ParkedOnlyOnClickListener để mở một màn hình cài đặt trên thiết bị di động:

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

Hiện thông báo

Các thông báo gửi đến thiết bị di động chỉ hiện trên màn hình ô tô nếu chúng được mở rộng bằng CarAppExtender. Bạn có thể đặt một số thuộc tính của thông báo như tiêu đề nội dung, văn bản, biểu tượng và thao tác trong CarAppExtender. Những thuộc tính này sẽ ghi đè các thuộc tính của thông báo đó khi chúng xuất hiện trên màn hình ô tô.

Đoạn mã sau đây cho biết cách gửi một thông báo tới màn hình ô tô hiển thị một tiêu đề khác với tiêu đề hiện trên thiết bị di động:

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

Thông báo có thể ảnh hưởng đến các phần sau trên giao diện người dùng:

  • Thông báo quan trọng (HUN) có thể hiển thị với người dùng.
  • Bạn có thể thêm mục nhập trong trung tâm thông báo theo tuỳ ý với huy hiệu hiển thị trên thanh thông báo.
  • Đối với các ứng dụng chỉ đường, thông báo có thể hiển thị trong tiện ích thanh thông báo như mô tả trong phần Thông báo từng chặng.

Bạn có thể chọn cách định cấu hình các thông báo của ứng dụng để tác động đến từng phần tử trong các phần tử giao diện người dùng đó bằng cách sử dụng mức độ ưu tiên của thông báo như mô tả trong tài liệu CarAppExtender.

Nếu NotificationCompat.Builder.setOnlyAlertOnce được gọi với giá trị true, thì thông báo có mức độ ưu tiên cao chỉ hiển thị dưới dạng HUN một lần.

Để biết thêm thông tin về cách thiết kế các thông báo của ứng dụng dành cho ô tô, hãy xem phần Thông báo trên Google Design for Driving (Google Design cho hoạt động lái xe).

Hiện thông báo ngắn

Ứng dụng của bạn có thể hiện một thông báo ngắn bằng CarToast như trong đoạn mã này:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

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

Yêu cầu cấp quyền

Nếu ứng dụng của bạn cần quyền truy cập vào dữ liệu hoặc thao tác bị hạn chế, chẳng hạn như thông tin vị trí, thì các quy tắc chuẩn về quyền trên Android sẽ được áp dụng. Để yêu cầu quyền, bạn có thể sử dụng phương thức CarContext.requestPermissions().

Trên Android Auto, hộp thoại cấp quyền cho người dùng sẽ xuất hiện trên điện thoại. Theo mặc định, sẽ không có nền phía sau hộp thoại đó. Để đặt nền tuỳ chỉnh, hãy khai báo giao diện ứng dụng dành cho ô tô trong tệp AndroidManifest.xml rồi đặt thuộc tính carPermissionActivityLayout cho giao diện ứng dụng dành cho ô tô của bạn.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Sau đó, đặt thuộc tính carPermissionActivityLayout cho giao diện ứng dụng dành cho ô tô:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Trái với việc sử dụng API Android chuẩn, khi sử dụng CarContext.requestPermissions(), bạn không cần chạy Activity của riêng mình để tạo hộp thoại cấp quyền. Hơn nữa, bạn có thể dùng cùng một mã trên cả Android Auto và Android Automotive OS, thay vì phải tạo các luồng phụ thuộc vào nền tảng.

Khởi động ứng dụng dành cho ô tô bằng ý định

Bạn có thể gọi phương thức CarContext.startCarApp để thực hiện một trong các thao tác sau:

Ví dụ sau đây cho biết cách tạo thông báo bằng thao tác mở ứng dụng của bạn với màn hình hiển thị chi tiết về việc đặt chỗ đỗ xe. Bạn mở rộng bản sao thông báo bằng ý định nội dung chứa PendingIntent gói ý định tường minh cho thao tác của ứng dụng:

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

Ứng dụng của bạn cũng phải khai báo một BroadcastReceiver được gọi để xử lý ý định khi người dùng chọn thao tác trong giao diện thông báo và gọi CarContext.startCarApp với ý định bao gồm URI của dữ liệu:

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

Cuối cùng, phương thức Session.onNewIntent trong ứng dụng của bạn xử lý ý định này bằng cách đẩy màn hình đặt chỗ đỗ xe lên ngăn xếp, nếu màn hình này chưa ở trên cùng:

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

Hãy xem phần Hiện thông báo để biết thêm thông tin về cách xử lý các thông báo đối với ứng dụng dành cho ô tô.

Các hạn chế khi dùng mẫu

Máy chủ lưu trữ giới hạn số lượng mẫu hiển thị cho một tác vụ nhất định ở mức tối đa là 5 mẫu, trong đó mẫu cuối cùng phải là một trong các kiểu sau:

Lưu ý rằng giới hạn này áp dụng cho số lượng mẫu chứ không phải số lượng thực thể Screen trong ngăn xếp. Ví dụ: nếu một ứng dụng gửi 2 mẫu khi ở màn hình A rồi đẩy màn hình B thì giờ đây ứng dụng đó có thể gửi thêm 3 mẫu nữa. Ngoài ra, nếu mỗi màn hình được cấu trúc để gửi một mẫu duy nhất, thì ứng dụng đó có thể đẩy 5 thực thể màn hình vào ngăn xếp ScreenManager.

Những hạn chế này có các trường hợp đặc biệt như: làm mới mẫu, thao tác quay lại và đặt lại.

Làm mới mẫu

Một số cập nhật nội dung nhất định không được tính vào giới hạn mẫu. Nói chung, nếu một ứng dụng đẩy một mẫu mới cùng kiểu và chứa cùng nội dung chính với mẫu trước đó thì mẫu mới sẽ không được tính vào hạn mức. Ví dụ: việc cập nhật trạng thái chuyển đổi của một hàng trong ListTemplate không được tính vào hạn mức. Hãy xem tài liệu về từng mẫu để tìm hiểu thêm về những kiểu cập nhật nội dung có thể được coi là làm mới.

Thao tác quay lại

Để kích hoạt các luồng phụ trong một nhiệm vụ, máy chủ lưu trữ sẽ phát hiện thời điểm một ứng dụng bật Screen từ ngăn xếp ScreenManager, đồng thời cập nhật hạn mức còn lại dựa trên số lượng mẫu mà ứng dụng đang lùi lại.

Ví dụ: nếu ứng dụng gửi 2 mẫu khi ở màn hình A, sau đó đẩy màn hình B và gửi thêm 2 mẫu nữa, thì ứng dụng còn lại một hạn mức. Sau đó, nếu ứng dụng quay lại màn hình A, máy chủ lưu trữ sẽ đặt lại hạn mức thành 3 vì ứng dụng đã lùi lại 2 mẫu.

Lưu ý rằng, khi quay lại một màn hình, ứng dụng phải gửi mẫu cùng kiểu với mẫu được màn hình đó gửi lần cuối. Việc gửi bất kỳ kiểu mẫu nào khác đều gây ra lỗi. Tuy nhiên, miễn là kiểu đó vẫn giữ nguyên trong thao tác quay lại, thì ứng dụng có thể tự do sửa đổi nội dung của mẫu mà không ảnh hưởng đến hạn mức.

Thao tác đặt lại

Một số mẫu nhất định có ngữ nghĩa đặc biệt biểu thị việc kết thúc một nhiệm vụ. Ví dụ: NavigationTemplate là một khung hiển thị dự kiến sẽ ở lại màn hình đó và được làm mới bằng các hướng dẫn từng chặng mới để người dùng sử dụng. Khi đạt đến một trong các mẫu này, máy chủ lưu trữ sẽ đặt lại hạn mức mẫu, xử lý mẫu đó như thể đó là bước đầu tiên của một nhiệm vụ mới. Điều này cho phép ứng dụng bắt đầu một nhiệm vụ mới. Hãy xem tài liệu về từng mẫu để biết mẫu nào kích hoạt thao tác đặt lại trên máy chủ lưu trữ.

Nếu máy chủ nhận được một ý định khởi động ứng dụng từ thao tác thông báo hoặc từ trình chạy thì hạn mức cũng được đặt lại. Cơ chế này cho phép ứng dụng bắt đầu một luồng nhiệm vụ mới từ thông báo và cơ chế này vẫn đúng ngay cả khi ứng dụng đã bị ràng buộc và ở nền trước.

Hãy xem phần Hiện thông báo để biết thêm thông tin về cách hiện thông báo của ứng dụng trên màn hình ô tô. Hãy xem phần Khởi động ứng dụng dành cho ô tô bằng ý định để biết thông tin về cách khởi động ứng dụng của bạn từ thao tác thông báo.

API Kết nối

Bạn có thể xác định xem ứng dụng của mình đang chạy trên Android Auto hay Android Automotive OS bằng cách sử dụng API CarConnection để truy xuất thông tin kết nối trong thời gian chạy.

Ví dụ: trong Session của ứng dụng dành cho ô tô, hãy khởi chạy CarConnection và đăng ký nhận bản cập nhật LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

Sau đó, trong trình quan sát, bạn có thể phản ứng với các thay đổi về trạng thái kết nối:

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

Constraints API (API Giới hạn)

Nhiều loại ô tô có thể cho phép hiển thị số lượng thực thể Item khác nhau cho người dùng tại một thời điểm. Hãy dùng ConstraintManager để kiểm tra giới hạn nội dung trong thời gian chạy và đặt số lượng mục thích hợp trong mẫu của bạn.

Hãy bắt đầu bằng cách nhận ConstraintManager từ CarContext:

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

Sau đó, bạn có thể truy vấn đối tượng ConstraintManager được truy xuất để biết giới hạn nội dung liên quan. Ví dụ: để nhận được số mục có thể hiển thị trong một lưới, hãy gọi getContentLimit bằng CONTENT_LIMIT_TYPE_GRID:

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

Thêm một quy trình đăng nhập

Nếu ứng dụng của bạn cung cấp trải nghiệm đăng nhập cho người dùng, bạn có thể sử dụng các mẫu như SignInTemplateLongMessageTemplate với Car App API (API Ứng dụng dành cho ô tô) cấp 2 trở lên để xử lý việc đăng nhập vào ứng dụng trên đầu phát trung tâm của ô tô.

Để tạo một SignInTemplate, hãy xác định SignInMethod. Thư viện ứng dụng cho ô tô hiện hỗ trợ các phương thức đăng nhập sau:

  • InputSignInMethod để đăng nhập bằng tên người dùng/mật khẩu.
  • PinSignInMethod để đăng nhập bằng mã PIN, trong đó người dùng liên kết tài khoản của họ từ điện thoại bằng mã PIN hiện trên đầu phát trung tâm.
  • ProviderSignInMethod để đăng nhập thông qua nhà cung cấp, chẳng hạn như Đăng nhập bằng GoogleMột lần chạm.
  • QRCodeSignInMethod để đăng nhập bằng mã QR, trong đó người dùng quét một mã QR để hoàn tất quy trình đăng nhập trên điện thoại của họ. Phương thức này sử dụng được với API Ô tô cấp 4 trở lên.

Ví dụ: để triển khai một mẫu thu thập mật khẩu của người dùng, hãy bắt đầu bằng cách tạo InputCallback để xử lý và xác thực hoạt động đầu vào của người dùng:

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.
    }
};

Cần có InputCallback cho 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();

Cuối cùng, hãy sử dụng InputSignInMethod mới của bạn để tạo 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();

Sử dụng AccountManager

Các ứng dụng trên Android Automotive OS có trình xác thực thì phải sử dụng AccountManager vì những lý do sau:

  • Trải nghiệm người dùng tốt hơn và dễ dàng quản lý tài khoản: Người dùng có thể dễ dàng quản lý tất cả tài khoản của họ trên trình đơn tài khoản trong phần cài đặt hệ thống, bao gồm cả hoạt động đăng nhập và đăng xuất.
  • Trải nghiệm "khách": Vì ô tô là thiết bị dùng chung, nên các OEM có thể cung cấp trải nghiệm khách trong xe khi không thể thêm tài khoản.

Thêm các biến thể chuỗi văn bản

Kích thước màn hình ô tô khác nhau có thể hiện lượng văn bản khác nhau. Với Car App API (API Ứng dụng dành cho ô tô) cấp 2 trở lên, bạn có thể chỉ định nhiều biến thể của một chuỗi văn bản để phù hợp nhất với màn hình. Để biết các biến thể văn bản được chấp nhận ở nơi nào, hãy tìm các mẫu và thành phần có CarText.

Bạn có thể thêm các biến thể chuỗi văn bản vào CarText bằng phương thức CarText.Builder.addVariant():

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

Sau đó, bạn có thể sử dụng CarText này chẳng hạn làm văn bản chính của GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

Thêm các chuỗi theo thứ tự từ được ưa thích nhất đến ít được ưa thích nhất, ví dụ: từ dài nhất đến ngắn nhất. Máy chủ lưu trữ chọn chuỗi có độ dài thích hợp tuỳ theo khoảng không gian hiện có trên màn hình ô tô.

Thêm các biểu tượng ô tô cùng dòng cho các hàng

Bạn có thể thêm các biểu tượng cùng dòng với văn bản để giúp ứng dụng của mình trở nên bắt mắt thông qua CarIconSpan. Hãy xem tài liệu liên quan đến CarIconSpan.create để biết thêm thông tin về cách tạo những span này. Hãy xem phần Spantastic text styling with Spans (Tạo kiểu văn bản spantastic (tuyệt vời) bằng span) để biết thông tin tổng quan về cách hoạt động của việc định kiểu văn bản bằng span.

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

Car Hardware API (API Phần cứng ô tô)

Bắt đầu với Car App API (API Ứng dụng dành cho ô tô) cấp 3, Thư viện ứng dụng cho ô tô có API mà bạn có thể dùng để truy cập vào các thuộc tính và cảm biến của xe.

Yêu cầu

Để sử dụng các API đó với Android Auto, hãy bắt đầu bằng cách thêm một phần phụ thuộc trên androidx.car.app:app-projected vào tệp build.gradle cho mô-đun Android Auto của bạn. Đối với Android Automotive OS, hãy thêm một phần phụ thuộc trên androidx.car.app:app-automotive vào tệp build.gradle cho mô-đun Android Automotive OS.

Ngoài ra, trong tệp AndroidManifest.xml, bạn phải khai báo các quyền liên quan cần để yêu cầu dữ liệu ô tô mà bạn muốn sử dụng. Lưu ý rằng người dùng cũng phải cấp các quyền này cho bạn. Bạn có thể dùng cùng một mã trên cả Android Auto và Android Automotive OS, thay vì phải tạo các luồng phụ thuộc vào nền tảng. Tuy nhiên, các quyền cần thiết sẽ khác nhau.

CarInfo

Bảng này mô tả các thuộc tính do API CarInfo hiển thị và những quyền bạn cần để yêu cầu sử dụng các thuộc tính đó:

Phương thức Thuộc tính Quyền đối với Android Auto Quyền đối với Android Automotive OS
fetchModel Hãng xe, mẫu xe, năm sản xuất android.car.permission.CAR_INFO
fetchEnergyProfile Loại đầu nối xe điện, loại nhiên liệu com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO
addTollListener
removeTollListener
Trạng thái thẻ thu phí cầu đường, loại thẻ thu phí cầu đường
addEnergyLevelListener
removeEnergyLevelListener
Mức pin, mức nhiên liệu, mức nhiên liệu thấp, quãng đường còn đi được 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
addSpeedListener
removeSpeedListener
Tốc độ thô, tốc độ hiển thị (hiện trên màn hình cụm đồng hồ của ô tô) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED,
android.car.permission.READ_CAR_DISPLAY_UNITS
addMileageListener
removeMileageListener
Quãng đường trên công tơ mét com.google.android.gms.permission.CAR_MILEAGE Dữ liệu này không có sẵn trên Android Automotive OS cho các ứng dụng được cài đặt từ Cửa hàng Play.

Ví dụ: để biết quãng đường còn đi được, hãy tạo thực thể cho đối tượng CarInfo, sau đó tạo và đăng ký 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);

Đừng cho rằng dữ liệu từ ô tô luôn có sẵn. Nếu gặp lỗi, hãy kiểm tra trạng thái của giá trị bạn đã yêu cầu để hiểu rõ hơn lý do khiến bạn không truy xuất được dữ liệu mình yêu cầu. Hãy xem tài liệu tham khảo để biết định nghĩa đầy đủ về lớp CarInfo.

CarSensors

Lớp CarSensors cho phép bạn truy cập vào gia tốc kế, con quay hồi chuyển, la bàn và dữ liệu vị trí của xe. Tính sẵn có của các giá trị này có thể phụ thuộc vào OEM. Định dạng của dữ liệu từ gia tốc kế, con quay hồi chuyển và la bàn giống như định dạng bạn nhận được từ API SensorManager. Ví dụ: để kiểm tra hướng của xe, hãy làm như sau:

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

Để truy cập vào dữ liệu vị trí từ ô tô, bạn cũng cần khai báo và yêu cầu quyền android.permission.ACCESS_FINE_LOCATION.

Kiểm thử

Để mô phỏng dữ liệu cảm biến khi kiểm thử trên Android Auto, hãy tham khảo phần Cảm biếnCấu hình cảm biến trong hướng dẫn Đầu phát trung tâm trên máy tính. Để mô phỏng dữ liệu cảm biến khi kiểm thử trên Android Automotive OS, hãy tham khảo phần Mô phỏng trạng thái phần cứng trong hướng dẫn Trình mô phỏng Android Automotive OS.

Vòng đời của CarAppService, Phiên và Màn hình

Các lớp SessionScreen triển khai giao diện LifecycleOwner. Khi người dùng tương tác với ứng dụng, phương thức gọi lại trong vòng đời của các đối tượng SessionScreen sẽ được gọi, như mô tả trong biểu đồ sau đây.

Vòng đời của CarAppService và Phiên

Hình 1. Vòng đời Session.

Để biết thông tin đầy đủ, hãy xem tài liệu về phương thức Session.getLifecycle.

Vòng đời của màn hình

Hình 2. Vòng đời Screen.

Để biết thông tin đầy đủ, hãy xem tài liệu về phương thức Screen.getLifecycle.

Ghi âm bằng micrô trên ô tô

Khi dùng API CarAudioRecordCarAppService của ứng dụng, bạn có thể cho phép ứng dụng truy cập vào micrô trên ô tô của người dùng. Người dùng cần cấp cho ứng dụng của bạn quyền truy cập vào micrô trên ô tô. Ứng dụng của bạn có thể ghi âm và xử lý thông tin đầu vào của người dùng ngay trong ứng dụng.

Quyền ghi âm

Trước khi ghi âm, bạn phải khai báo quyền ghi âm trong AndroidManifest.xml và yêu cầu người dùng cấp quyền đó.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

Bạn cần yêu cầu quyền ghi âm trong thời gian chạy. Hãy xem phần Yêu cầu cấp quyền để biết thông tin về cách yêu cầu cấp quyền trong ứng dụng dành cho ô tô của bạn.

Ghi âm

Sau khi người dùng cấp quyền ghi âm, bạn có thể ghi âm và xử lý bản ghi đó.

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

Quyền phát âm thanh

Khi ghi âm từ micrô trên ô tô, trước tiên, hãy lấy quyền phát âm thanh để đảm bảo rằng mọi nội dung nghe nhìn đang phát đều dừng lại. Nếu bạn mất quyền phát âm thanh, hãy dừng ghi âm.

Dưới đây là ví dụ về cách lấy quyền phát âm thanh:

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
 

Thư viện kiểm thử

Thư viện kiểm thử của Android cho Ô tô cung cấp các lớp phụ trợ mà bạn có thể sử dụng để xác thực hành vi của ứng dụng trong một môi trường thử nghiệm. Ví dụ: SessionController cho phép bạn mô phỏng một kết nối đến máy chủ lưu trữ và xác minh rằng ScreenTemplate được tạo và trả về là chính xác.

Hãy tham khảo phần Ứng dụng mẫu để biết các ví dụ về cách sử dụng.

Báo cáo sự cố Thư viện ứng dụng Android cho Ô tô

Nếu bạn thấy thư viện có lỗi, hãy báo cáo lỗi đó bằng Công cụ theo dõi lỗi của Google. Hãy nhớ điền tất cả thông tin được yêu cầu vào mẫu báo cáo lỗi.

Báo lỗi mới

Trước khi báo lỗi mới, vui lòng kiểm tra xem lỗi đó có trong bản ghi chú phát hành của thư viện hay không hoặc có được báo cáo trong danh sách lỗi hay không. Bạn có thể đăng ký theo dõi và tán thành các lỗi bằng cách nhấp vào dấu sao cho một lỗi trong công cụ theo dõi. Để biết thêm thông tin, hãy xem bài viết Đăng ký theo dõi lỗi.