How to Add Chromecast (Cast) Functionality in Your Android Kotlin App

For Dummies · Beginner-Friendly · Step‑by‑Step

Integrating Chromecast (Google Cast) into your Android app can feel daunting, especially if you’re new to media streaming. In this guide, you’ll learn how to:

  • Set up the Google Cast SDK in Kotlin
  • Retrieve the total duration of your media file
  • Implement a seek bar so users can skip forward or rewind

Let’s break it down into simple, actionable steps!


📋 Prerequisites

Before you begin, make sure you have:

  1. Android Studio (Arctic Fox or newer)
  2. Kotlin configured in your project
  3. Internet permission in AndroidManifest.xml:
    <uses-permission android:name="android.permission.INTERNET" />
    
  4. A basic MediaSessionCompat or ExoPlayer setup (optional, for local playback fallback).

🔧 1. Add Required Dependencies

Open your app-level build.gradle and add:

dependencies {
    implementation "com.google.android.gms:play-services-cast:22.1.0"
    implementation "com.google.android.gms:play-services-cast-framework:22.1.0"
}
  • play-services-cast: core Cast APIs
  • cast-framework: UI widgets and lifecycle helpers

Sync your project after saving.


🚀 2. Initialize CastContext Early

CastContext is the entry point for all Cast operations. Initialize it in your custom Application class:

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        // This warms up the Cast SDK
        CastContext.getSharedInstance(this)
    }
}

Register it in AndroidManifest.xml:

<application
    android:name=".MyApp"
    …>
    <!-- other entries -->
</application>

🎛️ 3. Add a Cast Button to Your Toolbar

Let users discover available Cast devices:

  1. menu_main.xml
    <item
        android:id="@+id/media_route_menu_item"
        android:title="@string/cast"
        app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
        app:showAsAction="always" />
    
  2. In Your Activity
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        val mediaRouteItem = menu.findItem(R.id.media_route_menu_item)
        val provider = mediaRouteItem.actionProvider as MediaRouteActionProvider
        provider.routeSelector = CastContext.getSharedInstance(this).mergedSelector
        return true
    }
    

🛠️ 4. Listen for Cast Sessions & Get a RemoteMediaClient

In a service (e.g., CastService) or your main Activity, set up a SessionManagerListener:

private lateinit var sessionManager: SessionManager
private var remoteClient: RemoteMediaClient? = null

private val sessionListener = object : SessionManagerListener<CastSession> {
    override fun onSessionStarted(session: CastSession, id: String) {
        remoteClient = session.remoteMediaClient
        remoteClient?.registerCallback(mediaCallback)
        remoteClient?.addProgressListener(progressListener, 1000L)
    }
    override fun onSessionEnded(session: CastSession, error: Int) {
        remoteClient?.unregisterCallback(mediaCallback)
        remoteClient?.removeProgressListener(progressListener)
        remoteClient = null
    }
    // other overrides can be empty…
}

// In onCreate or onStart:
sessionManager = CastContext.getSharedInstance(this).sessionManager
sessionManager.addSessionManagerListener(sessionListener, CastSession::class.java)

⏱️ 5. Retrieve Media Duration & Current Position

Use a ProgressListener to get real‑time updates:

private val _streamDuration = MutableLiveData<Long>()
val streamDuration: LiveData<Long> = _streamDuration

private val _streamPosition = MutableLiveData<Long>()
val streamPosition: LiveData<Long> = _streamPosition

private val progressListener = RemoteMediaClient.ProgressListener { posMs, durMs ->
    _streamPosition.postValue(posMs)
    _streamDuration.postValue(durMs)
}
  • posMs = current position (ms)
  • durMs = total duration (ms)

Observe these LiveData in your UI to update a SeekBar and duration TextView:

viewModel.streamDuration.observe(this) { dur ->
    seekBar.max = dur.toInt()
    durationTextView.text = formatMs(dur)
}
viewModel.streamPosition.observe(this) { pos ->
    seekBar.progress = pos.toInt()
    positionTextView.text = formatMs(pos)
}

Helper to format ms → mm:ss:

fun formatMs(ms: Long): String {
    val totalSec = ms / 1000
    val min = totalSec / 60
    val sec = totalSec % 60
    return "%02d:%02d".format(min, sec)
}

🔀 6. Implement Seek (Skip)

When the user drags the SeekBar, call:

seekBar.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
    override fun onProgressChanged(sb: SeekBar, progress: Int, fromUser: Boolean) {
        if (fromUser) {
            remoteClient?.seek(progress.toLong())
        }
    }
    override fun onStartTrackingTouch(sb: SeekBar) {}
    override fun onStopTrackingTouch(sb: SeekBar) {}
})

Tip: .seek(positionMs: Long) is easy and works, but you can also use the newer seek(MediaSeekOptions) if you need more control.


✅ 7. (Optional) Pre‑Set Stream Duration

For VOD (fixed‑length files), you can speed up UI updates by telling Cast the duration up‑front:

val mediaInfo = MediaInfo.Builder(url)
    .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
    .setContentType("audio/mp3")
    .setStreamDuration(180_000L)           // 3 minutes in ms
    .setMetadata(metadata)
    .build()

Cast will then show the total length immediately, without waiting for server headers.


🚩 Common Pitfalls & Tips

  • Live/Radio streams have no end: duration will be 0. Skip setting setStreamDuration.
  • Permission errors? Make sure INTERNET is declared and your Cast button appears only on HTTPS streams.
  • Deprecated APIs: if seek(MediaSeekOptions) or ProgressListener imports aren’t found, bump your play-services-cast-framework to the latest version and re‑sync.

🔚 Conclusion

You’ve now got a full Chromecast integration in Kotlin—complete with real‑time duration display and seek controls. Your users can cast audio/video, see how long the media is, and skip to any part they like.

Feel free to copy‑paste the code snippets, and tweak them to fit your app’s architecture. Happy coding!

This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back