Add option to resume playback when a device is connected (#497)

This commit is contained in:
vfsfitvnm 2022-10-23 15:36:54 +02:00
parent bb4c9ada73
commit fd54a4c483
4 changed files with 69 additions and 0 deletions

View file

@ -1,6 +1,7 @@
package it.vfsfitvnm.vimusic.service
import android.os.Binder as AndroidBinder
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
@ -14,6 +15,9 @@ import android.content.res.Configuration
import android.database.SQLException
import android.graphics.Bitmap
import android.graphics.Color
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.MediaMetadata
import android.media.audiofx.AudioEffect
import android.media.session.MediaSession
@ -87,12 +91,14 @@ import it.vfsfitvnm.vimusic.utils.forceSeekToPrevious
import it.vfsfitvnm.vimusic.utils.getEnum
import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid13
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
import it.vfsfitvnm.vimusic.utils.isShowingThumbnailInLockscreenKey
import it.vfsfitvnm.vimusic.utils.mediaItems
import it.vfsfitvnm.vimusic.utils.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.preferences
import it.vfsfitvnm.vimusic.utils.queueLoopEnabledKey
import it.vfsfitvnm.vimusic.utils.resumePlaybackWhenDeviceConnectedKey
import it.vfsfitvnm.vimusic.utils.shouldBePlaying
import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.timer
@ -148,6 +154,9 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
private var isShowingThumbnailInLockscreen = true
override var isInvincibilityEnabled = false
private var audioManager: AudioManager? = null
private var audioDeviceCallback: AudioDeviceCallback? = null
private val binder = Binder()
private var isNotificationStarted = false
@ -248,6 +257,8 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
}
registerReceiver(notificationActionReceiver, filter)
maybeResumePlaybackWhenDeviceConnected()
}
override fun onTaskRemoved(rootIntent: Intent?) {
@ -443,6 +454,42 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
mediaSession.setMetadata(metadataBuilder.build())
}
@SuppressLint("NewApi")
private fun maybeResumePlaybackWhenDeviceConnected() {
if (!isAtLeastAndroid6) return
if (preferences.getBoolean(resumePlaybackWhenDeviceConnectedKey, false)) {
if (audioManager == null) {
audioManager = getSystemService(AUDIO_SERVICE) as AudioManager?
}
audioDeviceCallback = object : AudioDeviceCallback() {
private fun canPlayMusic(audioDeviceInfo: AudioDeviceInfo): Boolean {
if (!audioDeviceInfo.isSink) return false
return audioDeviceInfo.type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP ||
audioDeviceInfo.type == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
audioDeviceInfo.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES ||
audioDeviceInfo.type == AudioDeviceInfo.TYPE_USB_HEADSET
}
override fun onAudioDevicesAdded(addedDevices: Array<AudioDeviceInfo>) {
if (!player.isPlaying && addedDevices.any(::canPlayMusic)) {
player.play()
}
}
override fun onAudioDevicesRemoved(removedDevices: Array<AudioDeviceInfo>) = Unit
}
audioManager?.registerAudioDeviceCallback(audioDeviceCallback, handler)
} else {
audioManager?.unregisterAudioDeviceCallback(audioDeviceCallback)
audioDeviceCallback = null
}
}
private fun sendOpenEqualizerIntent() {
sendBroadcast(
Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION).apply {
@ -535,6 +582,8 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
maybeNormalizeVolume()
}
resumePlaybackWhenDeviceConnectedKey -> maybeResumePlaybackWhenDeviceConnected()
isInvincibilityEnabledKey -> isInvincibilityEnabled =
sharedPreferences.getBoolean(key, isInvincibilityEnabled)
@ -868,6 +917,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
private class NotificationActionReceiver(private val player: Player) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
println(intent.action)
when (intent.action) {
Action.pause.value -> player.pause()
Action.play.value -> player.play()

View file

@ -24,8 +24,11 @@ import androidx.compose.foundation.layout.only
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid13
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6
import it.vfsfitvnm.vimusic.utils.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.resumePlaybackWhenDeviceConnectedKey
import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.volumeNormalizationKey
@ -37,6 +40,7 @@ fun PlayerSettings() {
val binder = LocalPlayerServiceBinder.current
var persistentQueue by rememberPreference(persistentQueueKey, false)
var resumePlaybackWhenDeviceConnected by rememberPreference(resumePlaybackWhenDeviceConnectedKey, false)
var skipSilence by rememberPreference(skipSilenceKey, false)
var volumeNormalization by rememberPreference(volumeNormalizationKey, false)
@ -68,6 +72,17 @@ fun PlayerSettings() {
}
)
if (isAtLeastAndroid6) {
SwitchSettingEntry(
title = "Resume playback",
text = "When a wired or bluetooth device is connected",
isChecked = resumePlaybackWhenDeviceConnected,
onCheckedChange = {
resumePlaybackWhenDeviceConnected = it
}
)
}
SettingsGroupSpacer()
SettingsEntryGroupText(title = "AUDIO")

View file

@ -30,6 +30,7 @@ const val trackLoopEnabledKey = "trackLoopEnabled"
const val queueLoopEnabledKey = "queueLoopEnabled"
const val skipSilenceKey = "skipSilence"
const val volumeNormalizationKey = "volumeNormalization"
const val resumePlaybackWhenDeviceConnectedKey = "resumePlaybackWhenDeviceConnected"
const val persistentQueueKey = "persistentQueue"
const val isShowingSynchronizedLyricsKey = "isShowingSynchronizedLyrics"
const val isShowingThumbnailInLockscreenKey = "isShowingThumbnailInLockscreen"

View file

@ -113,5 +113,8 @@ suspend fun Result<Innertube.PlaylistOrAlbumPage>.completed(): Result<Innertube.
return Result.success(playlistPage)
}
inline val isAtLeastAndroid6
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
inline val isAtLeastAndroid13
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU