Add Android alert effect playback
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package se.ajpanton.notificationsmaster.alerts
|
||||
|
||||
import android.content.Context
|
||||
import android.media.AudioAttributes
|
||||
import android.media.MediaPlayer
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.VibrationAttributes
|
||||
import android.os.VibrationEffect
|
||||
import android.os.VibratorManager
|
||||
|
||||
/** Plays resolved notification-class effects and releases all platform resources on stop. */
|
||||
class AndroidAlertEffectPlayer(context: Context) : AlertEffectPlayer {
|
||||
private val context = context.applicationContext
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val vibrator = context.getSystemService(VibratorManager::class.java).defaultVibrator
|
||||
private var generation = 0L
|
||||
private var remainingEffects = 0
|
||||
private var player: MediaPlayer? = null
|
||||
private var completion: (() -> Unit)? = null
|
||||
|
||||
override fun play(alert: QueuedAlert, onFinished: () -> Unit) = synchronized(this) {
|
||||
stopLocked()
|
||||
val token = generation
|
||||
completion = onFinished
|
||||
val snapshot = alert.snapshot
|
||||
val vibrationDuration = snapshot.vibrationPattern.sum()
|
||||
remainingEffects = (if (snapshot.soundUri == null) 0 else 1) + (if (vibrationDuration > 0) 1 else 0)
|
||||
snapshot.soundUri?.let { startSound(Uri.parse(it), token) }
|
||||
if (vibrationDuration > 0) {
|
||||
vibrator.vibrate(
|
||||
VibrationEffect.createWaveform(snapshot.vibrationPattern.toLongArray(), -1),
|
||||
VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_NOTIFICATION).build(),
|
||||
)
|
||||
handler.postDelayed({ finishEffect(token) }, vibrationDuration)
|
||||
}
|
||||
if (remainingEffects == 0) finishEffect(token)
|
||||
}
|
||||
|
||||
override fun stop() = synchronized(this) { stopLocked() }
|
||||
|
||||
private fun startSound(uri: Uri, token: Long) {
|
||||
val mediaPlayer = MediaPlayer()
|
||||
try {
|
||||
player = mediaPlayer
|
||||
mediaPlayer.setAudioAttributes(
|
||||
AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build(),
|
||||
)
|
||||
mediaPlayer.setDataSource(context, uri)
|
||||
mediaPlayer.setOnCompletionListener { finishSound(token, mediaPlayer) }
|
||||
mediaPlayer.setOnErrorListener { _, _, _ ->
|
||||
finishSound(token, mediaPlayer)
|
||||
true
|
||||
}
|
||||
mediaPlayer.prepareAsync()
|
||||
} catch (_: Exception) {
|
||||
if (player === mediaPlayer) player = null
|
||||
mediaPlayer.release()
|
||||
finishEffect(token)
|
||||
}
|
||||
}
|
||||
|
||||
private fun finishSound(token: Long, mediaPlayer: MediaPlayer): Unit = synchronized(this) {
|
||||
if (player !== mediaPlayer) return
|
||||
player = null
|
||||
mediaPlayer.release()
|
||||
finishEffect(token)
|
||||
}
|
||||
|
||||
private fun finishEffect(token: Long): Unit = synchronized(this) {
|
||||
if (token != generation || remainingEffects == 0) return
|
||||
if (--remainingEffects != 0) return
|
||||
completion.also { completion = null }?.invoke()
|
||||
}
|
||||
|
||||
private fun stopLocked() {
|
||||
generation++
|
||||
remainingEffects = 0
|
||||
completion = null
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
player?.release()
|
||||
player = null
|
||||
vibrator.cancel()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user