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 package it.vfsfitvnm.vimusic.service
import android.os.Binder as AndroidBinder import android.os.Binder as AndroidBinder
import android.annotation.SuppressLint
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
@ -14,6 +15,9 @@ import android.content.res.Configuration
import android.database.SQLException import android.database.SQLException
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.MediaMetadata import android.media.MediaMetadata
import android.media.audiofx.AudioEffect import android.media.audiofx.AudioEffect
import android.media.session.MediaSession 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.getEnum
import it.vfsfitvnm.vimusic.utils.intent import it.vfsfitvnm.vimusic.utils.intent
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid13 import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid13
import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
import it.vfsfitvnm.vimusic.utils.isShowingThumbnailInLockscreenKey import it.vfsfitvnm.vimusic.utils.isShowingThumbnailInLockscreenKey
import it.vfsfitvnm.vimusic.utils.mediaItems import it.vfsfitvnm.vimusic.utils.mediaItems
import it.vfsfitvnm.vimusic.utils.persistentQueueKey import it.vfsfitvnm.vimusic.utils.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.preferences import it.vfsfitvnm.vimusic.utils.preferences
import it.vfsfitvnm.vimusic.utils.queueLoopEnabledKey import it.vfsfitvnm.vimusic.utils.queueLoopEnabledKey
import it.vfsfitvnm.vimusic.utils.resumePlaybackWhenDeviceConnectedKey
import it.vfsfitvnm.vimusic.utils.shouldBePlaying import it.vfsfitvnm.vimusic.utils.shouldBePlaying
import it.vfsfitvnm.vimusic.utils.skipSilenceKey import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.timer import it.vfsfitvnm.vimusic.utils.timer
@ -148,6 +154,9 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
private var isShowingThumbnailInLockscreen = true private var isShowingThumbnailInLockscreen = true
override var isInvincibilityEnabled = false override var isInvincibilityEnabled = false
private var audioManager: AudioManager? = null
private var audioDeviceCallback: AudioDeviceCallback? = null
private val binder = Binder() private val binder = Binder()
private var isNotificationStarted = false private var isNotificationStarted = false
@ -248,6 +257,8 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
} }
registerReceiver(notificationActionReceiver, filter) registerReceiver(notificationActionReceiver, filter)
maybeResumePlaybackWhenDeviceConnected()
} }
override fun onTaskRemoved(rootIntent: Intent?) { override fun onTaskRemoved(rootIntent: Intent?) {
@ -443,6 +454,42 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
mediaSession.setMetadata(metadataBuilder.build()) 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() { private fun sendOpenEqualizerIntent() {
sendBroadcast( sendBroadcast(
Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION).apply { Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION).apply {
@ -535,6 +582,8 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
maybeNormalizeVolume() maybeNormalizeVolume()
} }
resumePlaybackWhenDeviceConnectedKey -> maybeResumePlaybackWhenDeviceConnected()
isInvincibilityEnabledKey -> isInvincibilityEnabled = isInvincibilityEnabledKey -> isInvincibilityEnabled =
sharedPreferences.getBoolean(key, isInvincibilityEnabled) sharedPreferences.getBoolean(key, isInvincibilityEnabled)
@ -868,6 +917,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
private class NotificationActionReceiver(private val player: Player) : BroadcastReceiver() { private class NotificationActionReceiver(private val player: Player) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
println(intent.action)
when (intent.action) { when (intent.action) {
Action.pause.value -> player.pause() Action.pause.value -> player.pause()
Action.play.value -> player.play() 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.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance 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.persistentQueueKey
import it.vfsfitvnm.vimusic.utils.rememberPreference import it.vfsfitvnm.vimusic.utils.rememberPreference
import it.vfsfitvnm.vimusic.utils.resumePlaybackWhenDeviceConnectedKey
import it.vfsfitvnm.vimusic.utils.skipSilenceKey import it.vfsfitvnm.vimusic.utils.skipSilenceKey
import it.vfsfitvnm.vimusic.utils.volumeNormalizationKey import it.vfsfitvnm.vimusic.utils.volumeNormalizationKey
@ -37,6 +40,7 @@ fun PlayerSettings() {
val binder = LocalPlayerServiceBinder.current val binder = LocalPlayerServiceBinder.current
var persistentQueue by rememberPreference(persistentQueueKey, false) var persistentQueue by rememberPreference(persistentQueueKey, false)
var resumePlaybackWhenDeviceConnected by rememberPreference(resumePlaybackWhenDeviceConnectedKey, false)
var skipSilence by rememberPreference(skipSilenceKey, false) var skipSilence by rememberPreference(skipSilenceKey, false)
var volumeNormalization by rememberPreference(volumeNormalizationKey, 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() SettingsGroupSpacer()
SettingsEntryGroupText(title = "AUDIO") SettingsEntryGroupText(title = "AUDIO")

View file

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

View file

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