实现自定义浏览操作

与使用自定义播放操作在播放视图中支持独特功能类似,您可以使用自定义浏览操作在浏览视图中支持独特功能。例如,您可以使用自定义浏览操作,以便用户可以下载播放列表或将项目添加到队列中。

如果自定义操作的数量多于原始设备制造商 (OEM) 显示的数量,系统会向用户显示溢出菜单。每项自定义浏览操作都通过以下方式定义:

  • 操作 ID:唯一字符串标识符
  • 操作标签:向用户显示的文字
  • 操作图标统一资源标识符 (URI):可着色的矢量可绘制对象

自定义浏览操作溢出

图 1. 自定义浏览操作溢出。

您可以在 BrowseRoot 中全局定义自定义浏览操作列表。然后,将这些操作的子集附加到各个 MediaItem

当用户与自定义浏览操作互动时,您的应用会在 onCustomAction 中收到回调。然后,您处理该操作,并根据需要更新 MediaItem 的操作列表。这对于有状态操作(例如“收藏”和“下载”)非常有用。对于不需要更新的操作(例如 Play Radio),您无需更新操作列表。

自定义浏览操作工具栏

图 2. 自定义浏览操作工具栏。

您还可以将自定义浏览操作附加到浏览节点根。这些操作显示在主工具栏下方的辅助工具栏中。

如需向应用添加自定义浏览操作,请执行以下操作:

  1. MediaBrowserServiceCompat 实现中替换两种方法:

  2. 在运行时解析操作限制:

    onGetRoot 中,使用 rootHints Bundle 中的键 BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT 获取每个 MediaItem 允许的最大操作数。限制为 0 表示系统不支持相应功能。

  3. 构建自定义浏览操作的全局列表。针对每项操作,创建一个包含以下键的 Bundle 对象:

    • 操作 ID EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
    • 操作标签 EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
    • 操作图标 URI EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
  4. 将所有操作 Bundle 对象添加到一个列表中。

  5. 将全局列表添加到 BrowseRoot。在 BrowseRoot extras Bundle 中,使用键 BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST 将操作列表添加为 Parcelable ArrayList

  6. MediaItem 对象添加操作。您可以使用键 DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST 将操作 ID 列表包含在 MediaDescriptionCompat extra 中,从而向各个 MediaItem 对象添加操作。此列表必须是您在 BrowseRoot 中定义的全局操作列表的子集。

  7. 处理操作并返回进度或结果:

更新操作状态

如需在 MediaBrowserServiceCompat 中替换这些方法,请执行以下操作:

public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)

public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)

解析操作限制

检查支持多少自定义浏览操作:

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
    rootHints.getInt(
            MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0)
}

构建自定义浏览操作

每个操作都需要打包到单独的 Bundle 中。

  • 操作 ID:

    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
    
  • 操作标签:

    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
    
  • 操作图标 URI:

    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")
    

向 Parcelable ArrayList 添加自定义浏览操作

将所有自定义浏览操作 Bundle 对象添加到 ArrayList 中:

private ArrayList<Bundle> createCustomActionsList(
                                        CustomBrowseAction browseActions) {
    ArrayList<Bundle> browseActionsBundle = new ArrayList<>();
    for (CustomBrowseAction browseAction : browseActions) {
        Bundle action = new Bundle();
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                browseAction.mId);
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                getString(browseAction.mLabelResId));
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                browseAction.mIcon);
        browseActionsBundle.add(action);
    }
    return browseActionsBundle;
}

向浏览根添加自定义浏览操作列表

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {
    Bundle browserRootExtras = new Bundle();
    browserRootExtras.putParcelableArrayList(
            BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST,
            createCustomActionsList()));
    mRoot = new BrowserRoot(ROOT_ID, browserRootExtras);
    return mRoot;
}

向 MediaItem 添加操作

MediaItem 中浏览操作 ID 必须是 onGetRoot 中给出的浏览操作全局列表的子集。系统会忽略不在全局列表中的操作。

MediaDescriptionCompat buildDescription (long id, String title, String subtitle,
                String description, Uri iconUri, Uri mediaUri,
                ArrayList<String> browseActionIds) {

    MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
    bob.setMediaId(id);
    bob.setTitle(title);
    bob.setSubtitle(subtitle);
    bob.setDescription(description);
    bob.setIconUri(iconUri);
    bob.setMediaUri(mediaUri);

    Bundle extras = new Bundle();
    extras.putStringArrayList(
          DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST,
          browseActionIds);

    bob.setExtras(extras);
    return bob.build();
}
MediaItem mediaItem = new MediaItem(buildDescription(...), flags);

构建 onCustomAction 结果

如需构建结果,请执行以下操作:

  1. Bundle extras 解析 mediaId

    @Override
    public void onCustomAction(
                @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){
        String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID);
                }
    
  2. 对于异步结果,请分离结果,result.detach

  3. 构建结果 bundle:

    1. 向用户显示消息:

      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                    mContext.getString(stringRes))
      
    2. 更新商品(用于更新商品中的操作):

      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
      
    3. 打开播放视图:

      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
      
    4. 更新浏览节点:

      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  4. 查看结果:

    • 错误:调用 result.sendError(resultBundle)
    • 进度更新:通话 result.sendProgressUpdate(resultBundle)
    • 完成:调用 result.sendResult(resultBundle)

更新操作状态

通过将 result.sendProgressUpdate(resultBundle) 方法与 EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM 键搭配使用,您可以更新 MediaItem 以反映操作的新状态。这样,您就可以向用户实时反馈其操作的进度和结果。

下载操作示例

此示例介绍了如何使用此功能来实现具有三种状态的下载操作:

  • 下载是操作的初始状态。当用户选择此操作时,您可以将其与“正在下载”操作交换,并调用 sendProgressUpdate 来更新界面 (UI)。

  • 正在下载状态表示下载正在进行中。您可以使用此状态向用户显示进度条或其他指示器。

  • 已下载状态表示下载已完成。下载完成后,您可以将“正在下载”替换为“已下载”,并使用 EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM 键调用 sendResult,以指示应刷新相应项。此外,您还可以使用 EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE 键向用户显示成功消息。

这种方法可让您向用户清晰反馈下载过程及其当前状态。您可以使用图标添加更多详细信息,以显示 25%、50% 和 75% 的下载状态。

收藏操作示例

另一个示例是具有两种状态的“收藏”操作:

这种方法可为用户提供清晰一致的方式来管理其喜爱的商品。这些示例展示了自定义浏览操作的灵活性,以及如何使用它们在汽车的媒体应用中实现各种功能并提供实时反馈,从而提升用户体验。

您可以在 TestMediaApp 项目中查看此功能的全面示例实现。