You can customize Cast widgets by setting the colors, styling the buttons, text, and thumbnail appearance, and by choosing the types of buttons to display.
Customize app theme
This example creates a custom theme style Theme.CastVideosTheme
which
can define custom colors, an introductory overlay style, a mini controller
style, and an expanded controller style.
<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>
The last three lines above enable you to define styles specific to introductory overlay, mini controller, and expanded controller as part of this theme. Examples are included in those sections that follow.
Customize Cast Button
To add a custom mediaRouteTheme
to your app's Theme:
<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- ... -->
<item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
</style>
Declare your custom Media Router theme and declare a custom
mediaRouteButtonStyle
:
<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
should be used if the support library version is newer than 26.0.0. For older
support library versions, please use buttonTint
instead.
Customize Introductory Overlay Theme
The
IntroductoryOverlay
class supports various style attributes that your app can override in a custom
theme. This example shows how to override the text appearance of both the button
and title over the overlay widget:
<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>
Customize mini controller
Customize theme
The
MiniControllerFragment
class supports various style attributes that your app can override in a custom
theme. This example shows how to enable the display of the thumbnail image, to
override the text appearance of both the subhead and closed caption, set
the colors, and customize the buttons:
<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>
Choose buttons
A MiniControllerFragment
has three slots that can display the album art
and two buttons, or three control buttons if the album art is not populated.
SLOT SLOT SLOT
1 2 3
By default the fragment shows a play/pause toggle button. Developers can use
the attribute castControlButtons
to override which buttons to show.
The supported control buttons are defined as
ID resources:
Button Type | Description |
---|---|
@id/cast_button_type_empty |
Do not place a button in this slot |
@id/cast_button_type_custom |
Custom button |
@id/cast_button_type_play_pause_toggle |
Toggles between playback and pause |
@id/cast_button_type_skip_previous |
Skips to the previous item in the queue |
@id/cast_button_type_skip_next |
Skips to the next item in the queue |
@id/cast_button_type_rewind_30_seconds |
Rewinds the playback by 30 seconds |
@id/cast_button_type_forward_30_seconds |
Skips forward the playback by 30 seconds |
@id/cast_button_type_mute_toggle |
Mutes and unmutes the receiver |
@id/cast_button_type_closed_caption |
Opens a dialog to select text and audio tracks |
Here is an example that would use the album art, a play/pause toggle button, and a skip forward button in that order from left to right:
<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">
Warning: This array must contain exactly three items, otherwise a runtime
exception will be thrown. If you don't want to show a button in a slot, use
@id/cast_button_type_empty
.
Add custom buttons
A MiniControllerFragment
supports adding custom control buttons which are
not provided by the SDK, such as a "thumb's up" button. The steps are:
Specify a slot to contain a custom button using
@id/cast_button_type_custom
in thecastControlButtons
attribute of theMiniControllerFragment
.Implement a subclass of
UIController
. TheUIController
contains methods that are called by the SDK when the state of the cast session or media session changes. Your subclass ofUIController
should take anImageView
as one of the parameters and update its state as needed.Subclass
MiniControllerFragment
, then overrideonCreateView
and callgetButtonImageViewAt(int)
to get theImageView
for that custom button. Then callbindViewToUIController(View, UIController)
to associate the view with your customUIController
.See
MediaIntentReceiver
at Add Custom Actions for how to handle the action from your custom button.Here is an example of associating a button at slot 2 to a
UIController
calledMyCustomUIController
:
// 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>
// 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) ... } }
// 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); ... } }
Customize expanded controller
Customize theme
If an expanded controller's Activity uses a dark theme toolbar, you can set a theme on the toolbar to use light text and a light icon color:
<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="castExpandedControllerToolbarStyle">
@style/ThemeOverlay.AppCompat.Dark.ActionBar
</item>
</style>
You can specify your own images that are used to draw the buttons on the expanded controller:
<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>
Choose buttons
The expanded controller's Activity has five slots to show control buttons. The middle slot always shows a play/pause toggle button and is non-configurable. The other four slots are configurable, from left to right, by the sender app.
SLOT SLOT PLAY/PAUSE SLOT SLOT
1 2 BUTTON 3 4
By default the Activity shows a closed caption button, a skip to the
previous item button, a skip to the next item button, and a mute toggle
button in these four slots, from left to right. Developers can use the
attribute castControlButtons
to override which buttons to show in
which slots. The list of supported control buttons are defined as
ID resources identical to the
button types for mini controller buttons.
Here is an example that places a rewind button in the second slot, a skip forward button in the third slot, and leaving the first and last slots empty:
// 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>
The array must contain exactly four items, otherwise a runtime exception will
be thrown. If you don't want to show a button in a slot, use
@id/cast_button_type_empty
. CastContext
can manage the lifecycle and
presentation of this activity.
Add custom buttons
An ExpandedControllerActivity
supports adding custom control buttons which are not provided by the SDK,
such as a "thumb's up" button. The steps are:
Specify a slot to contain a custom button using
@id/cast_button_type_custom
in thecastControlButtons
attribute of theExpandedControllerActivity
. You can then usegetButtonImageViewAt(int)
to obtain theImageView
for that custom button.Implement a subclass of
UIController
.UIController
contains methods that are called by the SDK when the state of the cast session or media session changes. Your subclass ofUIController
should take anImageView
as one of the parameters, and update its state as needed.Subclass ExpandedControllerActivity, then override
onCreate
and callgetButtonImageViewAt(int)
to get the view object of the button. Then callbindViewToUIController(View, UIController)
to associate the view with your customUIController
.See
MediaIntentReceiver
at Add Custom Actions for how to handle the action from your custom button.
Here is an example of associating a button at slot 2 to a
UIController
called 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>
// 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) ... } }
// 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); ... } }