Media3 Cast Integration
Android (Google)OfficialCreated by Google LLCDistributed by Android (Google)
- Skill
- Tool integration
- Software development
- Apache-2.0
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a comprehensive guide for implementing Google Cast support in Android apps using the Jetpack Media3 library. It covers the necessary build configuration, manifest updates, and player initialization required to enable seamless casting. The guide also details how to manage playback state between local and remote devices, supports both Compose and View-based UI architectures, and provides a structured approach for migrating from the legacy Google Cast SDK to the modern Media3 implementation.
What it does
- Configures build dependencies for Jetpack Media3 Cast integration.
- Updates Android manifest files with required Cast options providers.
- Initializes CastPlayer and RemoteCastPlayer for unified playback control.
- Provides patterns for managing playback state transitions between local and remote devices.
- Offers guidance for implementing Cast UI components in both Jetpack Compose and legacy View systems.
- Outlines a safe migration path from the legacy Google Cast SDK to Media3.
When to use it
- When adding new Google Cast functionality to an Android application.
- When migrating an existing application from the legacy Google Cast SDK to Jetpack Media3.
- When building media playback services that require switching between local and remote Cast-enabled devices.
Advantages
- Uses modern Jetpack Media3 APIs for consistent media playback management.
- Simplifies the transition between local and remote playback states.
- Provides clear architectural patterns for both Compose and View-based UIs.
- Reduces complexity by replacing legacy forwarding player wrappers.
Usage examples
- “How do I add Google Cast support to my Android app using Media3?”
- The agent provides the necessary Gradle dependencies, manifest configuration steps, and instructions for initializing the CastPlayer within a MediaSessionService.
- “How can I migrate my existing Cast implementation to Media3?”
- The agent outlines a step-by-step migration process, including adding Media3 dependencies, updating the manifest, and eventually removing legacy Cast SDK components.
- “How do I implement a MediaRouteButton in Jetpack Compose?”
- The agent provides the implementation details for the MediaRouteButton composable and explains how to sync UI state with the active player's device information.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/android/media3-cast-integration/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip media3-cast-integration.zip -d .agents/skills
Documentation by Google LLC
Prerequisites
Jetpack Media3 version must be
>= 1.9.0. Cast isn't available in lower versions.
Glossary
CastPlayer: Media3Playerthat controls playback on both local and remote Cast devices.RemoteCastPlayer: Media3Playerthat communicates with a Cast receiver, only used for remote playback.Google Cast SDK: Legacy casting SDK in maintenance mode, superseded by Jetpack Media3.
OptionsProvider: Interface providing configuration options to initialize GMSCastContext.
Common guidelines
Legacy Google Cast SDK is in maintenance mode.
For new Cast setups:
You must use Jetpack Media3 Cast.
You mustn't use legacy Cast SDK unless explicitly requested.
Step 1: Set up dependencies
To complete this step, you MUST ensure the following:
In the app-level build file, declare the
media3-castdependency version 1.9.0 or higher.implementation("androidx.media3:media3-cast:1.11.1")Ensure required Media3 dependencies are present:
androidx.media3:media3-exoplayerandroidx.media3:media3-sessionandroidx.media3:media3-ui-compose
If the application uses legacy Views, add
media3-ui.Enforce the same versions across all Media3 dependencies.
Use configurations in "Add build dependencies" section of Getting started with CastPlayer as the source of truth.
For apps without an existing Cast integration:
Verify legacy Cast SDK (
libs.play.services.cast.framework) is absent.
If Migrating from Legacy Cast SDK:
Add Media3 Cast dependencies first.
Keep existing legacy dependencies untouched at this stage to prevent compilation errors.
Step 2: Update the manifest
To complete this step, you MUST ensure the following:
Inside the manifest's
<application>tag, declare the Cast options provider.Use
DefaultCastOptionsProviderby default. See the "OptionsProvider" section in Getting started with CastPlayer.Declare a custom
OptionsProvideronly if explicitly requested. See Customize CastOptions.Ensure
INTERNETpermission is present. Don't add any unnecessary permissions.If Migrating from Legacy Cast SDK:
Don't delete existing custom options provider files or manifest entries.
Step 3: Implement the player and service
Architecture baseline
Before integrating Media3 Cast, an existing app follows one of two setups:
Local-only playback: Uses Media3
ExoPlayeronly to support local playback.Legacy Cast setup: Uses
ExoPlayerfor local playback, alongside aPlayerwrapper over the legacyRemoteMediaClientfor remote playback. The UI interfaces with aMediaSessioninteracting with aForwardingPlayer, which finally routes controls to either local or remote playback.
To complete this step, you MUST ensure the following:
Inside the application's
MediaSessionService(orMediaLibraryService)onCreate()method, initializeExoPlayerandCastPlayer.Use
CastPlayerby default unlessRemoteCastPlayeris explicitly requested. See the "Build a CastPlayer" section in Getting started with CastPlayer.For
CastPlayer, pass the instance directly toMediaSession.Builder.Replace all legacy forwarding player wrappers.
Don't delete legacy class files yet to prevent compilation errors during migration.
Advanced: RemoteCastPlayer
Use
RemoteCastPlayeronly if explicitly requested by user.Initialize
MediaSessionwithlocalPlayerand set aSessionAvailabilityListeneronRemoteCastPlayerto transfer playback state on Cast session availability changes:class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null private lateinit var localPlayer: ExoPlayer private lateinit var remotePlayer: RemoteCastPlayer override fun onCreate() { super.onCreate() localPlayer = ExoPlayer.Builder(this).build() remotePlayer = RemoteCastPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, localPlayer).build() remotePlayer.setSessionAvailabilityListener( object : SessionAvailabilityListener { override fun onCastSessionAvailable() { transferPlaybackState(localPlayer, remotePlayer) } override fun onCastSessionUnavailable() { transferPlaybackState(remotePlayer, localPlayer) } } ) } private fun transferPlaybackState(previousPlayer: Player, newPlayer: Player) { if (previousPlayer.mediaItemCount > 0) { val transferStateBuilder = PlayerTransferState.builderFromPlayer(previousPlayer) if (previousPlayer.playbackState == Player.STATE_ENDED || previousPlayer.currentPosition == C.TIME_END_OF_SOURCE) { transferStateBuilder.setCurrentMediaItemIndex(0) transferStateBuilder.setCurrentPosition(0) } transferStateBuilder.build().setToPlayer(newPlayer) } previousPlayer.stop() previousPlayer.clearMediaItems() newPlayer.prepare() mediaSession?.setPlayer(newPlayer) } }
Step 4: Set up the UI
Compose-based UI
To complete this step, you MUST ensure the following:
See the "Add a MediaRouteButton Composable to the Player" section in Getting started with CastPlayer for Compose integration guidelines.
Use the
MediaRouteButtoncomposable fromandroidx.media3.castpackage.Don't use
AndroidViewin the Compose UI hierarchy.Place
MediaRouteButtonin an area next to playback controls. Don't hide it behind system UI.Don't use
PlayerSurfacefor custom player UI. Use the Material3Playercomposable.Force recomposition on playback location shifts to ensure UI sync. Use key constraints on
DeviceInfochanges:@OptIn(UnstableApi::class) @Composable fun MainScreen() { val player = rememberMediaController() val deviceInfo = rememberDeviceInfo(player) player?.let { activePlayer -> key(deviceInfo) { PlayerScreen(player = activePlayer) } } } @Composable private fun rememberMediaController(): Player? { // Logic to connect MediaController to MediaSession and release it } @Composable private fun rememberDeviceInfo(player: Player?): DeviceInfo? { var deviceInfo by remember(player) { mutableStateOf(player?.deviceInfo) } DisposableEffect(player) { val activePlayer = player ?: return@DisposableEffect onDispose {} deviceInfo = activePlayer.deviceInfo val listener = object : Player.Listener { override fun onDeviceInfoChanged(info: DeviceInfo) { deviceInfo = info } } activePlayer.addListener(listener) onDispose { activePlayer.removeListener(listener) } } return deviceInfo }
View-based UI
To complete this step, you MUST ensure the following:
For View-based UI setups, see the "Add UI elements" section in Getting started with CastPlayer.
Casting Activities must extend
AppCompatActivityorFragmentActivityand use aTheme.AppCompatdescendant.Ensure the
AppCompattheme has a visibleActionBarif addingMediaRouteButtonto the options menu.Replace all instances and imports of
CastButtonFactorywithMediaRouteButtonFactory.Rebind
PlayerView.playerreferences upononDeviceInfoChangedevents to prevent black screens or UI freezes:private val playerListener: Player.Listener = object : Player.Listener { override fun onDeviceInfoChanged(deviceInfo: DeviceInfo) { // Resetting to null bypasses PlayerView.setPlayer()'s instance equality check // (this.player == player), forcing it to re-bind the video surface to the controller. playerView.player = null playerView.player = controller } }Migration to Compose:
Don't use
AndroidViewto wrap the legacyPlayerView.Implement Material3
Playercomposable andMediaRouteButtoncomposable as per Getting started with CastPlayer.Remove legacy XML layout declarations, menu files, and View component references.
Step 5: Clean up legacy Cast SDK code
[!WARNING] Warning: Don't perform cleanup directly. Remove legacy files and dependencies only when explicitly requested by the user.
To complete this step, you MUST ensure the following:
Remove legacy GMS Cast SDK (
libs.play.services.cast.framework) and MediaRouter (libs.androidx.mediarouter) dependencies.Delete custom
OptionsProviderclasses and manifest entries ifDefaultCastOptionsProvideris adopted.Remove legacy
MediaTransferReceivermanifest declarations if present.Remove all references to legacy Cast SDK components such as legacy helper wrappers, forwarding players, and
RemoteMediaClientinterfaces.Delete legacy View XML layouts, menu files, and references to
PlayerViewif the migration to Compose is complete.
Files
- ATTRIBUTION.mdAdded by AutoGineer978 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md10.5 KiB
- references/android/media/media3/cast/create-castplayer.md14.0 KiB
- references/android/media/media3/cast/customize-castoptions.md2.9 KiB
- references/android/media/media3/cast/index.md1.3 KiB
references/android/media/media3/cast/customize-castoptions.md
To configure your app's Cast session, provide an OptionsProvider. Use the
CastOptions object built by the provider to set the receiver application ID,
manage session lifecycles, and customize media playback behavior.
Use the default options provider
For a basic setup that uses the default Cast receiver application, add the
DefaultCastOptionsProvider to your app's AndroidManifest.xml file:
<application>
...
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
...
</application>Create a custom options provider
For more advanced configurations, such as setting a custom receiver application
ID, you need to create your own OptionsProvider.
1. Declare the provider in your manifest
First, declare your custom provider in AndroidManifest.xml. Make sure to use
the fully qualified class name.
<application>
...
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="path.to.your.class.MyCustomCastOptionsProvider" />
...
</application>2. Implement the OptionsProvider interface
Next, create a class that implements the OptionsProvider interface. In this
class, you must override getCastOptions() to return a CastOptions instance.
The custom OptionsProvider class is where you configure your Cast session, for
example, by setting your custom receiver application ID.
For more information, see CastOptions.Builder.
Kotlin
class MyCustomCastOptionsProvider : OptionsProvider {
override fun getCastOptions(context: Context): CastOptions {
return CastOptions.Builder()
.setReceiverApplicationId(APP_ID)
.setRemoteToLocalEnabled(true)
.build()
}
override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
return null
}
companion object {
// Add your receiver app ID in <APP_ID>.
private const val APP_ID = "<APP_ID>"
}
}Java
public static final class MyCustomCastOptionsProvider implements OptionsProvider {
// Add your receiver app ID in <APP_ID>.
public static final String APP_ID = "<APP_ID>";
@Override
public CastOptions getCastOptions(Context context) {
return new CastOptions.Builder()
.setReceiverApplicationId(APP_ID)
.setRemoteToLocalEnabled(true)
.build();
}
@Override
public @Nullable List<SessionProvider> getAdditionalSessionProviders(Context context) {
return null;
}
}<br />
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Google LLC
- Publisher
- Android (Google) (Official)
- Distributed by
- Android (Google)
- Source
- android/skills · media/media3-cast-integration · commit bf68e2d
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.txt)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Google LLC or Android (Google).
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download media3-cast-integration.zip