New to Claude Skills? Learn how to install them →

android on GitHub

Media3 Cast Integration

Free

Integrate Google Cast support in Android apps seamlessly.

by android6.7k stars on android/skills
5 views
Updated Aug 7, 2026
Get this skill

Free · Opens the source repo

What Media3 Cast Integration does

The Media3 Cast Integration skill provides a comprehensive guide for implementing Google Cast support in Android applications using Jetpack Media3. This skill is particularly useful for developers looking to add casting functionality to their apps or migrate from the legacy Google Cast SDK to the more modern Media3 framework. It covers the necessary steps to set up dependencies, update the Android manifest, and implement the player and service required for casting.

To get started, developers must ensure that their project uses Jetpack Media3 version 1.9.0 or higher, as casting features are not available in earlier versions. The skill outlines the required dependencies that need to be declared in the app-level build file, ensuring that all Media3 components are compatible and up-to-date. For those migrating from the legacy SDK, the skill provides clear instructions on how to integrate Media3 while maintaining existing functionality until the transition is complete.

The skill also details the architecture needed for local and remote playback, including the use of CastPlayer and RemoteCastPlayer. Developers will find specific code examples illustrating how to initialize these players within a MediaSessionService, allowing for seamless playback management across both local and remote devices. Additionally, for Compose-based UIs, the skill explains how to integrate the MediaRouteButton composable, enabling users to easily control casting from their application interface.

Overall, this skill is designed for Android developers who want to leverage the latest casting capabilities in their applications, providing a step-by-step approach to ensure a smooth integration process.

When to use it

Use this skill when developing new Android applications that require casting functionality or when updating existing apps to use the latest Media3 framework.

When not to use it

Avoid this skill if your app does not require casting capabilities or if you are already using a different casting solution that meets your needs.

What you can build with it

New App Development

When building a new Android app that requires Google Cast functionality, this skill provides all necessary steps to implement casting using Media3.

Migrating from Legacy SDK

If you are transitioning an existing application from the legacy Google Cast SDK, this skill offers a clear migration path to Media3, ensuring a smooth update.

Integrating Casting in Compose UIs

For developers using Jetpack Compose, this skill guides the integration of UI components needed for casting, enhancing user experience.

How to install Media3 Cast Integration

View source

1. Install with the skills CLI

npx skills add android/skills/media3-cast-integration --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by android

Prerequisites

  • Jetpack Media3 version must be >= 1.9.0. Cast isn't available in lower versions.

Glossary

  • CastPlayer : Media3 Player that controls playback on both local and remote Cast devices.
  • RemoteCastPlayer : Media3 Player that 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 GMS CastContext.

Common guidelines

Step 1: Set up dependencies

To complete this step, you MUST ensure the following:

  • In the app-level build file, declare the media3-cast dependency version 1.9.0 or higher.

    implementation("androidx.media3:media3-cast:1.10.1")
    
  • Ensure required Media3 dependencies are present:

    • androidx.media3:media3-exoplayer
    • androidx.media3:media3-session
    • androidx.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 DefaultCastOptionsProvider by default. See the "OptionsProvider" section in Getting started with CastPlayer.
  • Declare a custom OptionsProvider only if explicitly requested. See Customize CastOptions.
  • Ensure INTERNET permission 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 ExoPlayer only to support local playback.
  • Legacy Cast setup: Uses ExoPlayer for local playback, alongside a Player wrapper over the legacy RemoteMediaClient for remote playback. The UI interfaces with a MediaSession interacting with a ForwardingPlayer, which finally routes controls to either local or remote playback.

To complete this step, you MUST ensure the following:

  • Inside the application's MediaSessionService (or MediaLibraryService) onCreate() method, initialize ExoPlayer and CastPlayer.
  • Use CastPlayer by default unless RemoteCastPlayer is explicitly requested. See the "Build a CastPlayer" section in Getting started with CastPlayer.
  • For CastPlayer, pass the instance directly to MediaSession.Builder.
  • Replace all legacy forwarding player wrappers.
  • Don't delete legacy class files yet to prevent compilation errors during migration.

Advanced: RemoteCastPlayer

  • Use RemoteCastPlayer only if explicitly requested by user.

  • Initialize MediaSession with localPlayer and set a SessionAvailabilityListener on RemoteCastPlayer to 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 MediaRouteButton composable from androidx.media3.cast package.

  • Don't use AndroidView in the Compose UI hierarchy.

  • Place MediaRouteButton in an area next to playback controls. Don't hide it behind system UI.

  • Don't use PlayerSurface for custom player UI. Use the Material3 Player composable.

  • Force recomposition on playback location shifts to ensure UI sync. Use key constraints on DeviceInfo changes:

    @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 AppCompatActivity or FragmentActivity and use a Theme.AppCompat descendant.

  • Ensure the AppCompat theme has a visible ActionBar if adding MediaRouteButton to the options menu.

  • Replace all instances and imports of CastButtonFactory with MediaRouteButtonFactory.

  • Rebind PlayerView.player references upon onDeviceInfoChanged events 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:

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 OptionsProvider classes and manifest entries if DefaultCastOptionsProvider is adopted.
  • Remove legacy MediaTransferReceiver manifest declarations if present.
  • Remove all references to legacy Cast SDK components such as legacy helper wrappers, forwarding players, and RemoteMediaClient interfaces.
  • Delete legacy View XML layouts, menu files, and references to PlayerView if the migration to Compose is complete.

Frequently asked questions about Media3 Cast Integration

Similar skills