Skip to content

Instantly share code, notes, and snippets.

@dkaera
Last active April 8, 2021 10:33
Show Gist options
  • Save dkaera/6a841bcbee767ae96bef6ffa91f4ba3c to your computer and use it in GitHub Desktop.
Save dkaera/6a841bcbee767ae96bef6ffa91f4ba3c to your computer and use it in GitHub Desktop.
package com.dkaera.test
import android.content.Context
import android.database.ContentObserver
import android.media.AudioManager
import android.os.Handler
import android.os.HandlerThread
import io.reactivex.Observable
import io.reactivex.ObservableEmitter
import io.reactivex.ObservableOnSubscribe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposables
import timber.log.Timber
import java.lang.ref.WeakReference
class RxMusicLevel(
context: Context,
private var emitFirstValue: Boolean = true
) : ObservableOnSubscribe<Int> {
private val contextRef: WeakReference<Context>?
private var handlerThread: HandlerThread?
private var contentObserver: MusicLevelContentObserver? = null
@Volatile
private var isObserverRegistered = false
@Throws(Exception::class)
override fun subscribe(emitter: ObservableEmitter<Int>) {
try {
contextRef?.get()?.apply {
contentObserver = object : MusicLevelContentObserver(
getSystemService(Context.AUDIO_SERVICE) as AudioManager,
handlerThread?.let { Handler(it.looper) }
) {
override fun onVolumeChange(volumeLevel: Int) {
emitter.onNext(volumeLevel)
}
}
contentResolver.registerContentObserver(
android.provider.Settings.System.CONTENT_URI,
true,
contentObserver!!
)
isObserverRegistered = true
Timber.d("Register audio observer")
if (emitFirstValue) {
contentObserver?.onChange(true)
}
}
emitter.setDisposable(Disposables.fromRunnable {
contentObserver?.let { musicObserver ->
if (isObserverRegistered) {
contextRef?.get()?.apply {
contentResolver.unregisterContentObserver(musicObserver)
Timber.d("Unregister audio observer")
handlerThread?.quitSafely()
handlerThread = null
}
}
}
isObserverRegistered = false
})
} catch (ex: Exception) {
Timber.w(ex, "Error registering observer")
emitter.onError(ex)
}
}
companion object {
fun create(
context: Context,
emitFirstValue: Boolean
): Observable<Int> {
return Observable.defer {
Observable.create(RxMusicLevel(context, emitFirstValue))
}.observeOn(AndroidSchedulers.mainThread())
}
}
init {
contextRef = WeakReference(context)
handlerThread = HandlerThread("ContentObserverThread").apply { start() }
}
abstract class MusicLevelContentObserver(
private var audioManager: AudioManager,
handler: Handler? = null
) : ContentObserver(handler) {
override fun onChange(selfChange: Boolean) {
super.onChange(selfChange)
val volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
onVolumeChange(volumeLevel)
}
abstract fun onVolumeChange(volumeLevel: Int)
}
}
RxMusicLevel.create(context, true)
.bindToLifecycle(this)
.subscribe({ Timber.i("Consume music level here") }, { it.printStackTrace() })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment