From fd54a4c483f1baa355c86edfa127da1724d68761 Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Sun, 23 Oct 2022 15:36:54 +0200 Subject: [PATCH] Add option to resume playback when a device is connected (#497) --- .../vimusic/service/PlayerService.kt | 50 +++++++++++++++++++ .../ui/screens/settings/PlayerSettings.kt | 15 ++++++ .../it/vfsfitvnm/vimusic/utils/Preferences.kt | 1 + .../it/vfsfitvnm/vimusic/utils/Utils.kt | 3 ++ 4 files changed, 69 insertions(+) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt index aa6be00..56159ad 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt @@ -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) { + if (!player.isPlaying && addedDevices.any(::canPlayMusic)) { + player.play() + } + } + + override fun onAudioDevicesRemoved(removedDevices: Array) = 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() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettings.kt index 5460c2a..f1f4f61 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettings.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/PlayerSettings.kt @@ -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") diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index 5600eea..d97ef52 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -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" diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Utils.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Utils.kt index 0b4b169..a6aeb2c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Utils.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Utils.kt @@ -113,5 +113,8 @@ suspend fun Result.completed(): Result= Build.VERSION_CODES.M + inline val isAtLeastAndroid13 get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU