Skip to main content
skill CX Codex

media3-cast-integration — Claude Code skill: how to install

Adds Google Cast capabilities to Android projects using Jetpack Media3. It configures dependencies, manifest entries, CastPlayer or RemoteCastPlayer, and Compose UI controls.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add android/skills --skill media3-cast-integration -a claude-code
Open on GitHub

What media3-cast-integration does

This skill guides coding agents through setting up Google Cast with Jetpack Media3. It adds the required androidx.media3:media3-cast artifact alongside media3-exoplayer and media3-session. When migrating away from the legacy Cast SDK, it replaces custom forwarding wrappers around RemoteMediaClient while keeping old dependencies in place until migration finishes.

Manifest setup includes registering DefaultCastOptionsProvider and declaring INTERNET access. Inside MediaSessionService, the agent initializes ExoPlayer together with CastPlayer by default. For remote-only workflows, it wires RemoteCastPlayer and uses PlayerTransferState within a SessionAvailabilityListener to swap playback between local and cast devices. In Jetpack Compose, it renders MediaRouteButton directly without embedding legacy AndroidView wrappers.

Jetpack Media3 must be version 1.9.0 or higher because earlier releases lack Cast modules.

What it helps with

  • integrate Google Cast into Android apps with Jetpack Media3
  • migrate away from the legacy Google Cast SDK without breaking builds
  • wire CastPlayer or RemoteCastPlayer inside MediaSessionService
  • embed MediaRouteButton in Jetpack Compose playback controls

Example prompts for the agent

  • Set up Google Cast playback in my Android app using Jetpack Media3
  • Migrate this legacy Cast SDK implementation to Media3 CastPlayer
  • Add a Cast MediaRouteButton to the Compose player UI

How to install media3-cast-integration

1Skills CLI (Vercel)

npx skills add android/skills --skill media3-cast-integration -a claude-code

2Manually: put the files in place

~/.claude/skills/media3-cast-integration/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/media3-cast-integration/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about media3-cast-integration

How do I install media3-cast-integration in Claude Code?

Run npx skills add android/skills --skill media3-cast-integration -a claude-code. This installs the skill instructions into your agent environment.

What does media3-cast-integration do?

It manages build dependencies, updates AndroidManifest.xml with Cast options, configures CastPlayer inside a MediaSessionService, and places Cast buttons in Compose.

What minimum Media3 version is required?

Your project must use Jetpack Media3 version 1.9.0 or higher. Earlier releases do not include the Media3 Cast module.

Source text

SKILL.md · Apache-2.0
---
name: media3-cast-integration
description: Implements Google Cast support in Android apps using Jetpack Media3.
  Handles adding build dependencies, updating manifest, configuring OptionsProvider,
  and managing CastPlayer or RemoteCastPlayer for playback in both Compose and View-based
  UIs. Use when adding Cast functionality or migrating from legacy Cast SDK to Media3
  Cast.
license: Complete terms in LICENSE.txt
metadata:
  author: Google LLC
  last-updated: '2026-10-22'
  keywords:
  - Android
  - Media3
  - Cast
  - Integration
  - Migration
  - ExoPlayer
  - MediaSession
  - Jetpack Media3
---

## 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

- Legacy [Google Cast SDK](https://developers.google.com/cast) is in maintenance mode.
- For new Cast setups:
  - You must use [Jetpack Media3 Cast](references/android/media/media3/cast/index.md).
  - 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-cast` dependency version
  1.9.0 or higher.

      implementation("androidx.media3:media3-cast:1.11.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](references/android/media/media3/cast/create-castplayer.md) 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](references/android/media/media3/cast/create-castplayer.md).
- Declare a custom `OptionsProvider` only if explicitly requested. See [Customize CastOptions](references/android/media/media3/cast/customize-castoptions.md).
- 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](references/android/media/media3/cast/create-castplayer.md).
- 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](references/android/media/media3/cast/create-castplayer.md) for Compose integration guidelines.
- Use the [`MediaRouteButton` composable](https://developer.android.com/reference/kotlin/androidx/media3/cast/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](https://developer.android.com/reference/kotlin/androidx/media3/ui/compose/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](references/android/media/media3/cast/create-castplayer.md).
- 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:**

  - Don't use `AndroidView` to wrap the legacy `PlayerView`.
  - Implement Material3 [`Player` composable](https://developer.android.com/reference/kotlin/androidx/media3/ui/compose/material3/Player.composable) and [`MediaRouteButton` composable](https://developer.android.com/reference/kotlin/androidx/media3/cast/MediaRouteButton.composable) as per [Getting started with CastPlayer](references/android/media/media3/cast/create-castplayer.md).
  - 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 `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.

© Google LLC · Apache-2.0 · android/skills

Similar skills

Everything for Mobile →

From the same set24

camerax Guides Android camera development using CameraX, Jetpack, and Media3. Useful when wiring video capture, handling foldables, or migrating from Camera2. CX Codex +1 1,200installs agp-9-upgrade Moves an Android project from AGP 8 to Android Gradle Plugin 9 step by step: dependency bumps, built-in Kotlin, the new DSL, kapt cleanup. CX Codex +1 1,200installs android-profiler Captures and inspects Android performance metrics, from Perfetto traces and memory leaks to frame drops and battery drain. CX Codex +1 1,000installs leanback-to-compose-tv-migration A skill that guides the move from Leanback and Android Views to Jetpack Compose for TV. Useful when a browse, settings or playback screen needs rewriting. CX Codex +1 772installs restore-credentials This skill guides Android Restore Credentials integration with androidx.credentials for silent sign-in after an app is restored on a new device. CX Codex +1 638installs navigation-event Intercepts back gestures and sets up Predictive Back animations via androidx.navigationevent in Compose Android. Helps migrate from legacy BackHandler. CX Codex +1 546installs android-permissions-security Audits an Android project for permission and IPC holes: weak custom permissions, spoofable caller checks, over-broad URI grants. Useful when reviewing AndroidManifest.xml. CX Codex +1 508installs ml-kit-genai-prompt-api An Android skill that walks your codebase through ML Kit GenAI Prompt API: on-device Gemini Nano calls, typed output and prefix caching. CX Codex +1 442installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor