Personalizar a IU do remetente do Android

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

Personalizar tema do app

Este exemplo cria um estilo de tema personalizado Theme.CastVideosTheme, que pode definir cores personalizadas, um estilo de sobreposição introdutório, 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 desse 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 o tema personalizado do roteador de mídia 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 precisa 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ória

A classe IntroductoryOverlay tem 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 tem 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, 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 ativar/pausar. Os desenvolvedores podem usar o atributo castControlButtons para substituir os botões que vão ser mostrados. Os botões de controle compatíveis são definidos como recursos de ID:

Tipo de botão Descrição
@id/cast_button_type_empty Não colocar um botão neste 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 na fila
@id/cast_button_type_rewind_30_seconds Retrocede a reprodução em 30 segundos
@id/cast_button_type_forward_30_seconds Avança a reprodução em 30 segundos
@id/cast_button_type_mute_toggle Desativa e ativa o som do receptor
@id/cast_button_type_closed_caption Abre uma caixa de diálogo para selecionar faixas de texto e áudio

Veja um exemplo que usa a arte do álbum, um botão ativar/pausar e um botão "Pular para 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 da 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 dela conforme necessário.

  3. Crie uma subclasse MiniControllerFragment, depois 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 seu botão personalizado.

    Veja um exemplo de associação de um botão no slot 2 a 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 Activity de um controle expandido usar uma barra de ferramentas de tema escuro, você poderá definir um tema na barra de ferramentas para usar texto claro e uma cor de ícone claro:

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

Você pode especificar suas próprias imagens usadas para desenhar os botões no controlador 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 controlador expandido tem cinco slots para mostrar botões de controle. O slot do meio sempre mostra um botão para reproduzir/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 closed captions, um botão de pular para o item anterior, um botão de pular para o próximo item e um botão de desativar o som nos quatro espaços, da esquerda para a direita. Os desenvolvedores podem usar o atributo castControlButtons para substituir os botões que vão aparecer em quais slots. A lista de botões de controle com suporte é definida como recursos de ID idênticos aos tipos de botões de minicontroles.

Veja um exemplo que coloca um botão "Voltar" no segundo espaço, um botão "Pular para frente" no terceiro e 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. O CastContext pode gerenciar o ciclo de vida e a apresentação dessa atividade.

Adicionar botões personalizados

Uma 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 da 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 dela conforme necessário.

  3. Crie uma subclasse ExpandControllerActivity, modifique onCreate e chame getButtonImageViewAt(int) para receber 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 seu botão personalizado.

Veja um exemplo de como associar um botão no slot 2 a uma UIController chamada 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);
        ...
    }
}