Skip to content

Instantly share code, notes, and snippets.

@dimkinware
Created March 18, 2019 11:46
Show Gist options
  • Save dimkinware/f063bb94a6ecf6c0b28df67ca364f464 to your computer and use it in GitHub Desktop.
Save dimkinware/f063bb94a6ecf6c0b28df67ca364f464 to your computer and use it in GitHub Desktop.
package com.dimkinware.mediacast
import android.content.Context
import android.database.ContentObserver
import android.media.AudioManager
import android.os.Handler
import android.provider.Settings
import android.widget.SeekBar
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.google.android.gms.cast.Cast
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastSession
import com.google.android.gms.cast.framework.Session
import com.google.android.gms.cast.framework.SessionManagerListener
import com.google.android.gms.cast.framework.SessionManager
interface CastSessionManagerListenerAdapter<T : Session> : SessionManagerListener<T> {
override fun onSessionStarted(session: T?, sessionId: String?) = Unit
override fun onSessionResumeFailed(session: T?, error: Int) = Unit
override fun onSessionSuspended(session: T?, reason: Int) = Unit
override fun onSessionEnded(session: T?, error: Int) = Unit
override fun onSessionResumed(session: T?, wasSuspended: Boolean) = Unit
override fun onSessionStarting(session: T?) = Unit
override fun onSessionResuming(session: T?, sessionId: String?) = Unit
override fun onSessionEnding(session: T?) = Unit
override fun onSessionStartFailed(session: T?, error: Int) = Unit
}
/**
* Base class for specific volume controller
*/
private abstract class Volumizer(val volumeListener: OnVolumeChangedListener) {
interface OnVolumeChangedListener {
fun onVolumeChanged(currentValue: Int, maxValue: Int)
}
abstract fun start()
abstract fun stop()
abstract fun setVolume(value: Int)
abstract fun getVolume(): Int
abstract fun getMaxVolume(): Int
}
/**
* Volume controller for AudioManager.STREAM_MUSIC
*/
private class MusicVolumizer(val context: Context, listener: OnVolumeChangedListener) : Volumizer(listener) {
private val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
private val contentObserver = object : ContentObserver(Handler()) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
volumeListener.onVolumeChanged(getVolume(), getMaxVolume())
}
}
override fun start() {
volumeListener.onVolumeChanged(getVolume(), getMaxVolume())
context.applicationContext.contentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, contentObserver)
}
override fun stop() {
context.applicationContext.contentResolver.unregisterContentObserver(contentObserver)
}
override fun setVolume(value: Int) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, value, AudioManager.FLAG_PLAY_SOUND)
}
override fun getVolume(): Int = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
override fun getMaxVolume(): Int = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
}
/**
* Volume controller for cast session
*/
private class CastVolumizer(val session: CastSession, listener: OnVolumeChangedListener) : Volumizer(listener) {
private val VOLUME_STEP = 0.05
private val VOLUME_MAX = 20
private val castListener = object : Cast.Listener() {
override fun onVolumeChanged() {
volumeListener.onVolumeChanged(getVolume(), getMaxVolume())
}
}
override fun start() {
volumeListener.onVolumeChanged(getVolume(), getMaxVolume())
session.addCastListener(castListener)
}
override fun stop() {
session.removeCastListener(castListener)
}
override fun setVolume(value: Int) {
session.volume = VOLUME_STEP * value
}
override fun getVolume(): Int = (session.volume / VOLUME_STEP).toInt()
override fun getMaxVolume(): Int = VOLUME_MAX
}
class SeekBarCastAwareVolumizer(private val context: Context,
private val lifecycle: Lifecycle,
private val seekBar: SeekBar) : CastSessionManagerListenerAdapter<CastSession>,
SeekBar.OnSeekBarChangeListener,
Volumizer.OnVolumeChangedListener,
LifecycleObserver {
private val castSessionManager : SessionManager = CastContext.getSharedInstance(context).sessionManager
private var volumizer: Volumizer
init {
lifecycle.addObserver(this)
seekBar.isSaveEnabled = false
volumizer = if (castSessionManager.currentCastSession != null) {
CastVolumizer(castSessionManager.currentCastSession, this)
} else {
MusicVolumizer(context, this)
}
}
fun muteVolume() {
volumizer.setVolume(0)
}
fun maximizeVolume() {
volumizer.setVolume(volumizer.getMaxVolume())
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
volumizer.start()
seekBar.setOnSeekBarChangeListener(this)
castSessionManager.addSessionManagerListener(this, CastSession::class.java)
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
volumizer.stop()
seekBar.setOnSeekBarChangeListener(null)
castSessionManager.removeSessionManagerListener(this, CastSession::class.java)
}
override fun onSessionStarted(session: CastSession?, sessionId: String?) {
session?.let {
changeVolumizerTo(CastVolumizer(session, this))
}
}
override fun onSessionEnded(session: CastSession?, error: Int) {
changeVolumizerTo(MusicVolumizer(context, this))
}
override fun onVolumeChanged(currentValue: Int, maxValue: Int) {
setupSeekBar(currentValue, maxValue)
}
private fun changeVolumizerTo(newVolumizer: Volumizer) {
volumizer.stop()
volumizer = newVolumizer
volumizer.start()
}
private fun setupSeekBar(curValue: Int, maxValue: Int) {
seekBar.setOnSeekBarChangeListener(null)
seekBar.max = maxValue
seekBar.progress = curValue
seekBar.setOnSeekBarChangeListener(this)
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
volumizer.setVolume(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment