Xây dựng hệ thống phân cấp nội dung

Android Auto và Android Automotive OS (AAOS) gọi dịch vụ trình duyệt nội dung đa phương tiện của ứng dụng để khám phá nội dung có sẵn. Để hỗ trợ việc này, bạn triển khai 2 phương thức này trong dịch vụ trình duyệt nội dung đa phương tiện.

Triển khai onGetRoot

Phương thức onGetRoot của dịch vụ sẽ trả về thông tin liên quan đến nút gốc trong hệ thống phân cấp nội dung. Android Auto và AAOS dùng nút gốc này để yêu cầu nội dung còn lại bằng phương thức onLoadChildren. Đoạn mã này cho thấy cách triển khai phương thức onGetRoot:

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? =
    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        null
    } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        return null;
    }

    return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
}

Để biết ví dụ chi tiết về phương thức này, hãy xem onGetRoot trong ứng dụng mẫu Universal Android Music Player trên GitHub.

Thêm phương thức xác thực gói

Khi bạn thực hiện lệnh gọi đến phương thức onGetRoot của dịch vụ, gói gọi sẽ chuyển thông tin nhận dạng đến dịch vụ của bạn. Dịch vụ của bạn có thể dùng thông tin này để quyết định xem gói đó có thể truy cập vào nội dung của bạn hay không.

Ví dụ: bạn có thể chỉ cho phép các gói được phê duyệt truy cập vào nội dung của ứng dụng:

  • So sánh clientPackageName với danh sách cho phép của bạn.
  • Xác minh chứng chỉ dùng để ký APK cho gói.

Nếu không thể xác minh gói, hãy trả về null để từ chối quyền truy cập vào nội dung của bạn.

Để cấp cho các ứng dụng hệ thống (chẳng hạn như Android Auto và AAOS) quyền truy cập vào nội dung, dịch vụ của bạn phải trả về một BrowserRoot khác rỗng khi các ứng dụng hệ thống này gọi phương thức onGetRoot.

Chữ ký của ứng dụng hệ thống AAOS sẽ khác nhau tuỳ theo nhà sản xuất và mẫu xe ô tô. Hãy nhớ cho phép các kết nối từ tất cả ứng dụng hệ thống để hỗ trợ AAOS.

Đoạn mã này cho biết cách dịch vụ của bạn có thể xác thực rằng gói gọi là một ứng dụng hệ thống:

fun isKnownCaller(
    callingPackage: String,
    callingUid: Int
): Boolean {
    ...
    val isCallerKnown = when {
       // If the system is making the call, allow it.
       callingUid == Process.SYSTEM_UID -> true
       // If the app was signed by the same certificate as the platform
       // itself, also allow it.
       callerSignature == platformSignature -> true
       // ... more cases
    }
    return isCallerKnown
}

Đoạn mã này là một đoạn trích từ lớp PackageValidator trong ứng dụng mẫu Universal Android Music Player trên GitHub. Hãy xem lớp đó để biết ví dụ chi tiết hơn về cách triển khai tính năng xác thực gói cho phương thức onGetRoot của dịch vụ.

Ngoài việc cho phép các ứng dụng hệ thống, bạn phải cho phép Trợ lý Google kết nối với MediaBrowserService của mình. Trợ lý Google sử dụng tên gói riêng cho điện thoại (bao gồm cả Android Auto và Android Automotive OS).

Triển khai onLoadChildren

Sau khi nhận được đối tượng nút gốc, Android Auto và AAOS sẽ tạo trình đơn cấp cao nhất bằng cách gọi onLoadChildren trên đối tượng nút gốc để lấy các phần tử con. Các ứng dụng khách xây dựng trình đơn con bằng cách gọi cùng một phương thức này thông qua đối tượng nút con.

Mỗi nút trong hệ thống phân cấp nội dung của bạn được biểu thị bằng một đối tượng MediaBrowserCompat.MediaItem. Mỗi mục nội dung đa phương tiện này được xác định bằng một chuỗi mã nhận dạng duy nhất. Ứng dụng khách coi những chuỗi mã này là mã thông báo mờ.

Khi một ứng dụng khách muốn duyệt đến trình đơn con hoặc phát một mục nội dung đa phương tiện, ứng dụng đó sẽ chuyển mã thông báo. Ứng dụng của bạn chịu trách nhiệm liên kết mã thông báo với mục nội dung đa phương tiện thích hợp.

Đoạn mã này cho thấy cách triển khai onLoadChildren

Kotlin

override fun onLoadChildren(
    parentMediaId: String,
    result: Result<List<MediaBrowserCompat.MediaItem>>
) {
    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems: MutableList&lt;MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result&lt;List&lt;MediaBrowserCompat.MediaItem>> result) {

    // Assume for example that the music catalog is already loaded/cached.

    List&lt;MediaBrowserCompat.MediaItem> mediaItems = new ArrayList&lt;>();

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

Để xem ví dụ về phương thức này, hãy xem onLoadChildren trong ứng dụng mẫu Universal Android Music Player trên GitHub.

Cấu trúc trình đơn gốc

Android Auto và Android Automotive OS có những hạn chế cụ thể về cấu trúc của trình đơn gốc. Các hạn chế này được truyền đến MediaBrowserService thông qua gợi ý gốc. Hệ thống có thể đọc gợi ý gốc thông qua đối số Bundle được truyền vào onGetRoot(). Khi làm theo các gợi ý này, hệ thống có thể hiển thị nội dung gốc ở dạng thẻ điều hướng. Nếu bạn không làm theo các gợi ý này, hệ thống có thể bỏ hoặc ẩn bớt một số nội dung gốc.

Nội dung gốc xuất hiện ở dạng thẻ điều hướng

Hình 1. Nội dung gốc xuất hiện ở dạng thẻ điều hướng.

Khi áp dụng các gợi ý này, hệ thống sẽ hiển thị nội dung gốc dưới dạng thẻ điều hướng. Nếu bạn không áp dụng các gợi ý này, hệ thống có thể bỏ hoặc ẩn bớt một số nội dung gốc. Các gợi ý này được truyền đi:

Kotlin

import androidx.media.utils.MediaConstants

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}

Java

import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

Bạn có thể chọn phân nhánh logic cho cấu trúc của hệ phân cấp nội dung dựa trên giá trị của các gợi ý này, cụ thể là nếu hệ phân cấp của bạn thay đổi giữa các hoạt động tích hợp MediaBrowser bên ngoài Android Auto và AAOS.

Ví dụ: nếu thường hiển thị một mục gốc có thể phát, bạn nên lồng mục đó vào một mục gốc có thể xem, do giá trị của gợi ý về cờ được hỗ trợ.

Ngoài gợi ý gốc, hãy sử dụng các nguyên tắc sau để hiển thị thẻ một cách tối ưu:

  • Biểu tượng đơn sắc (tốt nhất là màu trắng) cho từng mục trong thẻ

  • Nhãn ngắn gọn và dễ hiểu cho từng mục trên thẻ (nhãn ngắn giúp giảm khả năng nhãn có thể bị cắt bớt)