Implementar ações de navegação personalizadas

Assim como você usa ações de reprodução personalizadas para oferecer suporte a recursos exclusivos na visualização de reprodução, é possível usar ações de navegação personalizadas para oferecer suporte a recursos exclusivos nas visualizações de navegação. Por exemplo, você pode usar ações de navegação personalizadas para que os usuários possam baixar playlists ou adicionar um item a uma fila.

Quando há mais ações personalizadas do que as exibidas pelo fabricante de equipamento original (OEM), um menu flutuante é mostrado ao usuário. Cada ação de navegação personalizada é definida com um:

  • ID da ação:identificador de string exclusivo
  • Rótulo da ação:texto exibido para o usuário
  • Identificador uniforme de recurso (URI) do ícone de ação: drawable vetorial que pode ser colorido.

Menu flutuante de ações de navegação personalizadas

Figura 1. Menu flutuante de ações de navegação personalizadas.

Você define uma lista de ações de navegação personalizadas globalmente como parte da sua BrowseRoot. Em seguida, anexe um subconjunto dessas ações a cada MediaItem.

Quando um usuário interage com uma ação de navegação personalizada, seu app recebe um callback em onCustomAction. Em seguida, você processa a ação e atualiza a lista de ações para o MediaItem, se necessário. Isso é útil para ações com estado, como "Adicionar aos favoritos" e "Fazer o download". No caso de ações que não precisam ser atualizadas, como Tocar rádio, não é necessário atualizar a lista.

Barra de ferramentas de ações de navegação personalizadas

Figura 2. Barra de ferramentas de ações de navegação personalizadas.

Você também pode anexar ações de navegação personalizadas a uma raiz de nó de navegação. Essas ações são mostradas em uma barra de ferramentas secundária abaixo da barra de ferramentas principal.

Para adicionar ações de navegação personalizadas ao seu app:

  1. Substitua dois métodos na implementação de MediaBrowserServiceCompat:

  2. Analise os limites de ação durante a execução:

    Em onGetRoot, confira o número máximo de ações permitidas para cada MediaItem usando a chave BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT no rootHints Bundle. Um limite de 0 indica que o sistema não oferece suporte ao recurso.

  3. Crie a lista global de ações de navegação personalizadas. Para cada ação, crie um objeto Bundle com estas chaves:

    • ID da ação EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
    • Rótulo da ação EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
    • URI do ícone de ação EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
  4. Adicione todos os objetos de ação Bundle a uma lista.

  5. Adicione a lista global ao seu BrowseRoot. Nos Bundle extras da BrowseRoot, adicione a lista de ações como um Parcelable ArrayList usando a chave BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST.

  6. Adicione ações aos objetos MediaItem. Você pode adicionar ações a objetos MediaItem individuais incluindo a lista de IDs de ação nos extras MediaDescriptionCompat usando a chave DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST. Essa lista precisa ser um subconjunto da lista global de ações definida na BrowseRoot.

  7. Processe as ações e retorne o progresso ou os resultados:

Atualizar o estado da ação

Para substituir esses métodos em MediaBrowserServiceCompat:

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

e

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

Analisar limite de ações

Confira quantas ações de navegação personalizadas têm suporte:

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

Criar uma ação de navegação personalizada

Cada ação precisa ser empacotada em um Bundle separado.

  • ID da ação:

    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
    
  • Rótulo da ação:

    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
    
  • URI do ícone de ação:

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

Adicionar ações de navegação personalizadas a Parcelable ArrayList

Adicione todos os objetos Bundle de ação de navegação personalizada a uma 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;
}

Adicionar a lista de ações de navegação personalizadas à raiz de navegação

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

Adicionar ações a um MediaItem

Os IDs das ações de navegação em um MediaItem precisam ser um subconjunto da lista global de ações de navegação fornecida em onGetRoot. As ações que não estiverem na lista global serão ignoradas.

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

Resultado do build onCustomAction

Para criar o resultado:

  1. Analisar mediaId de Bundle extras

    @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. Para resultados assíncronos, remova o resultado, result.detach.

  3. Crie o pacote de resultados:

    1. Exiba uma mensagem para o usuário:

      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                    mContext.getString(stringRes))
      
    2. Atualize o item (use para atualizar as ações em um item):

      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
      
    3. Abra a visualização de reprodução:

      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
      
    4. Atualize o nó de navegação:

      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  4. Confira o resultado:

    • Erro:chamada result.sendError(resultBundle)
    • Atualização de progresso:ligue para result.sendProgressUpdate(resultBundle)
    • Concluir:ligue para result.sendResult(resultBundle)

Atualizar o estado da ação

Ao usar o método result.sendProgressUpdate(resultBundle) com a chave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, você pode atualizar o MediaItem para refletir o novo estado da ação. Isso permite fornecer feedback em tempo real ao usuário sobre o progresso e o resultado da ação.

Exemplo de ação de download

Este exemplo descreve como usar esse recurso para implementar uma ação de download com três estados:

  • Fazer o download é o estado inicial da ação. Quando o usuário selecionar essa ação, troque por "Fazendo o download" e chame sendProgressUpdate para atualizar a interface do usuário (UI).

  • O estado Fazendo o download indica que o download está em andamento. É possível usar esse estado para mostrar uma barra de progresso ou outro indicador ao usuário.

  • O estado Salvo indica que o download foi concluído. Quando o download for concluído, troque "Fazendo o download" por "Salvo" e chame sendResult com a chave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM para indicar que o item precisa ser atualizado. Além disso, você pode usar a chave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE para mostrar uma mensagem de êxito para o usuário.

Essa abordagem permite fornecer um feedback claro ao usuário sobre o processo de download e o estado atual dele. Você pode adicionar ainda mais detalhes com ícones para mostrar os estados de download de 25%, 50% e 75%.

Exemplo de ação de adicionar aos favoritos

Outro exemplo é uma ação de adicionar aos favoritos com dois estados:

  • Adicionar aos favoritos é exibido para itens que não estão na lista de favoritos do usuário. Quando o usuário selecionar essa ação, troque por Adicionado aos favoritos e chame sendResult com a chave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM para atualizar a interface.

  • Adicionado aos favoritos é exibido para itens na lista de favoritos do usuário. Quando o usuário selecionar essa ação, troque por Adicionar aos favoritos e chame sendResult com a chave EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM para atualizar a interface.

Essa abordagem oferece uma maneira clara e consistente para os usuários gerenciarem os itens favoritos. Esses exemplos mostram a flexibilidade das ações de navegação personalizadas e como você pode usá-las para implementar uma variedade de funcionalidades com feedback em tempo real para melhorar a experiência do usuário no app de mídia do carro.

Confira um exemplo completo de implementação desse recurso no projeto TestMediaApp.