Personalizar a IU do remetente do Android

É possível personalizar os widgets do Google Cast configurando as cores, estilizando os botões, o texto e a aparência das miniaturas e escolhendo os tipos de botões que serão mostrados.

Personalizar tema do app

O exemplo abaixo cria um estilo de tema personalizado Theme.CastVideosTheme, que pode definir cores personalizadas, um estilo introdutório de sobreposição, um estilo de minicontrole e um estilo de controle expandido.

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Set AppCompat's color theming attrs -->
    <item name="colorPrimary">@color/primary</item>
    <item name="colorPrimaryDark">@color/primary_dark</item>
    <item name="colorAccent">@color/accent</item>
    <item name="android:textColorPrimary">@color/primary_text</item>
    <item name="android:textColorSecondary">@color/secondary_text</item>
    <item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
    <item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
    <item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
</style>

As três últimas linhas acima permitem definir estilos específicos para a sobreposição introdutória, o minicontrole e o controle expandido como parte do tema. Confira exemplos nas seções a seguir.

Personalizar o botão Transmitir

Para adicionar um mediaRouteTheme personalizado ao tema do app:

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
  <!-- ... -->
  <item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
</style>

Declare seu tema de roteador de mídia personalizado e declare um mediaRouteButtonStyle personalizado:

<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
  <item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>

<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
  <item name="mediaRouteButtonTint">#EEFF41</item>
</style>

setTint será usado se a versão da Biblioteca de Suporte for mais recente que a 26.0.0. Para versões mais antigas da Biblioteca de Suporte, use buttonTint.

Personalizar o tema de sobreposição introdutório

A classe IntroductoryOverlay oferece suporte a vários atributos de estilo que o app pode substituir em um tema personalizado. Este exemplo mostra como substituir a aparência do texto do botão e do título no widget de sobreposição:

<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
    <item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
    <item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title"parent="android:style/TextAppearance.Large">
    <item name="android:textColor">#FFFFFF</item>
</style>

Personalizar minicontrole

Personalizar tema

A classe MiniControllerFragment oferece suporte a vários atributos de estilo que o app pode substituir em um tema personalizado. Este exemplo mostra como ativar a exibição da imagem em miniatura para substituir a aparência do texto do subtítulo e da legenda oculta, definir as cores e personalizar os botões:

<style name="CustomCastMiniController" parent="CastMiniController">
    <item name="castShowImageThumbnail">true</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
    <item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
    <item name="castBackground">#FFFFFF</item>
    <item name="castProgressBarColor">#FFFFFF</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_mini_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_mini_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_mini_controller_stop</item>
    <item name="castLargePlayButtonDrawable">@drawable/cast_ic_mini_controller_play_large</item>
    <item name="castLargePauseButtonDrawable">@drawable/cast_ic_mini_controller_pause_large</item>
    <item name="castLargeStopButtonDrawable">@drawable/cast_ic_mini_controller_stop_large</item>
    <item name="castSkipPreviousButtonDrawable">@drawable/cast_ic_mini_controller_skip_prev</item>
    <item name="castSkipNextButtonDrawable">@drawable/cast_ic_mini_controller_skip_next</item>
    <item name="castRewind30ButtonDrawable">@drawable/cast_ic_mini_controller_rewind30</item>
    <item name="castForward30ButtonDrawable">@drawable/cast_ic_mini_controller_forward30</item>
    <item name="castMuteToggleButtonDrawable">@drawable/cast_ic_mini_controller_mute</item>
    <item name="castClosedCaptionsButtonDrawable">@drawable/cast_ic_mini_controller_closed_caption</item
</style>

Escolher botões

Um MiniControllerFragment tem três slots que podem exibir a arte do álbum e dois botões, ou três botões de controle se a arte do álbum não estiver preenchida.

SLOT  SLOT  SLOT
  1     2     3

Por padrão, o fragmento mostra um botão de alternância para reproduzir/pausar. Os desenvolvedores podem usar o atributo castControlButtons para substituir quais botões serão mostrados. Os botões de controle compatíveis são definidos como recursos de código:

Tipo de botão Descrição
@id/cast_button_type_empty Não coloque um botão nesse slot
@id/cast_button_type_custom Botão personalizado
@id/cast_button_type_play_pause_toggle Alterna entre a reprodução e a pausa
@id/cast_button_type_skip_previous Pula para o item anterior na fila
@id/cast_button_type_skip_next Pula para o próximo item da fila
@id/cast_button_type_rewind_30_seconds Retrocede a reprodução em 30 segundos
@id/cast_button_type_forward_30_seconds Avançar a reprodução em 30 segundos
@id/cast_button_type_mute_toggle Desativa e ativa o som do destinatário
@id/cast_button_type_closed_caption Abre uma caixa de diálogo para selecionar faixas de texto e áudio

Confira um exemplo que usa a arte do álbum, um botão ativar/pausar e um botão "Pular para a frente" nessa ordem, da esquerda para a direita:

<array name="cast_mini_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_play_pause_toggle</item>
    <item>@id/cast_button_type_forward_30_seconds</item>
</array>
...
<fragment
    android:id="@+id/cast_mini_controller"
    ...
    app:castControlButtons="@array/cast_mini_controller_control_buttons"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment">

Aviso: essa matriz precisa conter exatamente três itens. Caso contrário, uma exceção de tempo de execução será gerada. Se você não quiser mostrar um botão em um slot, use @id/cast_button_type_empty.

Adicionar botões personalizados

Um MiniControllerFragment oferece suporte à adição de botões de controle personalizados que não são fornecidos pelo SDK, como um botão "Gostei". Essas etapas são:

  1. Especifique um slot para conter um botão personalizado usando @id/cast_button_type_custom no atributo castControlButtons do MiniControllerFragment.

  2. Implemente uma subclasse de UIController. O UIController contém métodos que são chamados pelo SDK quando o estado da sessão de transmissão ou da sessão de mídia muda. A subclasse de UIController precisa usar um ImageView como um dos parâmetros e atualizar o estado conforme necessário.

  3. Crie uma subclasse MiniControllerFragment, substitua onCreateView e chame getButtonImageViewAt(int) para receber o ImageView para esse botão personalizado. Em seguida, chame bindViewToUIController(View, UIController) para associar a visualização à UIController personalizada.

  4. Consulte MediaIntentReceiver em Adicionar ações personalizadas para saber como processar a ação do botão personalizado.

    Confira um exemplo de associação de um botão no slot 2 a um UIController chamado MyCustomUIController:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_custom</item>
    <item>@id/cast_button_type_empty</item>
</array>
Kotlin
// MyCustomUIController.kt
class MyCustomUIController(private val mView: View) : UIController() {
    override fun onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.visibility = View.INVISIBLE
        ...
    }
}

// MyMiniControllerFragment.kt
class MyMiniControllerFragment : MiniControllerFragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        val customButtonView = getButtonImageViewAt(2)
        val myCustomUiController = MyCustomUIController(customButtonView)
        uiMediaController.bindViewToUIController(customButtonView, myCustomUiController)
        ...
    }
}
Java
// MyCustomUIController.java
class MyCustomUIController extends UIController {
    private final View mView;

    public MyCustomUIController(View view) {
            mView = view;
    }

    @Override
    public onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.setVisibility(View.INVISIBLE);
        ...
    }
}

// MyMiniControllerFragment.java
class MyMiniControllerFragment extends MiniControllerFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        ImageView customButtonView = getButtonImageViewAt(2);
        MyCustomUIController myCustomUiController = new MyCustomUIController(customButtonView);
        getUIMediaController().bindViewToUIController(customButtonView, myCustomUiController);
        ...
    }
}

Personalizar controle expandido

Personalizar tema

Se a atividade de um controle expandido usar uma barra de ferramentas de tema escuro, você poderá definir um tema nessa barra para usar texto claro e uma cor de ícone clara:

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="castExpandedControllerToolbarStyle">
        @style/ThemeOverlay.AppCompat.Dark.ActionBar
    </item>
</style>

É possível especificar suas próprias imagens usadas para desenhar os botões no controle expandido:

<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castButtonColor">@null</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
    <item name="castSkipPreviousButtonDrawable">@drawable/cast_ic_expanded_controller_skip_previous</item>
    <item name="castSkipNextButtonDrawable">@drawable/cast_ic_expanded_controller_skip_next</item>
    <item name="castRewind30ButtonDrawable">@drawable/cast_ic_expanded_controller_rewind30</item>
    <item name="castForward30ButtonDrawable">@drawable/cast_ic_expanded_controller_forward30</item>
</style>

Escolher botões

A atividade do controle expandido tem cinco slots para mostrar os botões de controle. O slot do meio sempre mostra um botão ativar/pausar e não é configurável. Os outros quatro slots podem ser configurados, da esquerda para a direita, pelo app remetente.

SLOT  SLOT  PLAY/PAUSE  SLOT  SLOT
  1     2     BUTTON      3     4

Por padrão, a atividade mostra um botão de legenda, um botão para pular para o item anterior, um botão para pular para o próximo item e um botão de ativar o som nesses quatro slots, da esquerda para a direita. Os desenvolvedores podem usar o atributo castControlButtons para substituir quais botões serão mostrados em cada slot. A lista de botões de controle compatíveis é definida como recursos de ID idênticos aos tipos de botões para minicontroles.

Veja um exemplo que coloca um botão "Voltar" no segundo slot e um "Pular" para frente no terceiro slot, deixando o primeiro e o último slots vazios:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_forward_30_seconds</item>
    <item>@id/cast_button_type_empty</item>
</array>
...
// styles.xml
<style name="Theme.MyTheme">
    <item name="castExpandedControllerStyle">
        @style/CustomCastExpandedController
    </item>
</style>
...
<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castControlButtons">
        @array/cast_expanded_controller_control_buttons
    </item>
</style>

A matriz precisa conter exatamente quatro itens. Caso contrário, uma exceção de tempo de execução será gerada. Se você não quiser mostrar um botão em um slot, use @id/cast_button_type_empty. CastContext pode gerenciar o ciclo de vida e a apresentação dessa atividade.

Adicionar botões personalizados

Um ExpandedControllerActivity oferece suporte à adição de botões de controle personalizados que não são fornecidos pelo SDK, como um botão "Gostei". Essas etapas são:

  1. Especifique um slot para conter um botão personalizado usando @id/cast_button_type_custom no atributo castControlButtons do ExpandedControllerActivity. Você pode usar getButtonImageViewAt(int) para acessar o ImageView desse botão personalizado.

  2. Implemente uma subclasse de UIController. UIController contém métodos que são chamados pelo SDK quando o estado da sessão de transmissão ou da sessão de mídia muda. A subclasse de UIController precisa usar um ImageView como um dos parâmetros e atualizar o estado conforme necessário.

  3. Crie uma subclasse ExpandControllerActivity, substitua onCreate e chame getButtonImageViewAt(int) para conseguir o objeto de visualização do botão. Em seguida, chame bindViewToUIController(View, UIController) para associar a visualização ao UIController personalizado.

  4. Consulte MediaIntentReceiver em Adicionar ações personalizadas para saber como processar a ação do botão personalizado.

Confira um exemplo de associação de um botão no slot 2 a um UIController chamado MyCustomUIController:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_custom</item>
    <item>@id/cast_button_type_empty</item>
</array>
Kotlin
// MyCustomUIController.kt
class MyCustomUIController(private val mView: View) : UIController() {
    override fun onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.visibility = View.INVISIBLE
        ...
    }
}

// MyExpandedControllerActivity.kt
internal class MyExpandedControllerActivity : ExpandedControllerActivity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val customButtonView = getButtonImageViewAt(2)
        val myCustomUiController = MyCustomUIController(customButtonView)
        uiMediaController.bindViewToUIController(customButtonView, myCustomUiController)
        ...
    }
}
Java
// MyCustomUIController.java
class MyCustomUIController extends UIController {
    private final View mView;

    public MyCustomUIController(View view) {
        mView = view;
    }

    @Override
    public onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.setVisibility(View.INVISIBLE);
        ...
    }
}

// MyExpandedControllerActivity.java
class MyExpandedControllerActivity extends ExpandedControllerActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ImageView customButtonView = getButtonImageViewAt(2);
        MyCustomUIController myCustomUiController = new MyCustomUIController(customButtonView);
        getUIMediaController().bindViewToUIController(customButtonView, myCustomUiController);
        ...
    }
}