Add option to resume playback when a device is connected (#497)
This commit is contained in:
parent
bb4c9ada73
commit
fd54a4c483
4 changed files with 69 additions and 0 deletions
|
@ -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()
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue