[Android] Add an option to ignore media button presses when a headset is inserted
NyanCatTW1
git at videolan.org
Mon Feb 13 10:52:58 UTC 2023
vlc-android | branch: master | NyanCatTW1 <49344-NyanCatTW1 at users.noreply.code.videolan.org> | Mon Feb 13 10:52:57 2023 +0000| [05bacb8ff10e9b14b9d0aefcaaaf09280151716a] | committer: Duncan McNamara
Add an option to ignore media button presses when a headset is inserted
> https://code.videolan.org/videolan/vlc-android/commit/05bacb8ff10e9b14b9d0aefcaaaf09280151716a
---
.../resources/src/main/res/values/strings.xml | 2 ++
.../television/ui/preferences/PreferencesAudio.kt | 1 +
application/vlc-android/AndroidManifest.xml | 1 +
.../vlc-android/res/xml/preferences_audio.xml | 8 +++++
.../src/org/videolan/vlc/MediaSessionCallback.kt | 40 +++++++++++++++++++++-
.../src/org/videolan/vlc/PlaybackService.kt | 5 ++-
6 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml
index e9777a4678..573102ee46 100644
--- a/application/resources/src/main/res/values/strings.xml
+++ b/application/resources/src/main/res/values/strings.xml
@@ -1102,6 +1102,8 @@
<string name="talkback_display_settings">Display settings</string>
<string name="talkback_already_played">Already played</string>
<string name="show_only_favs">Show only favorites</string>
+ <string name="ignore_headset_media_button_presses">Ignore headset media button presses</string>
+ <string name="ignore_headset_media_button_presses_summary">Useful, for instance, if you are using a headset with broken physical buttons</string>
</resources>
diff --git a/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesAudio.kt b/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesAudio.kt
index eb09387fc4..a41b2f7467 100644
--- a/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesAudio.kt
+++ b/application/television/src/main/java/org/videolan/television/ui/preferences/PreferencesAudio.kt
@@ -75,6 +75,7 @@ class PreferencesAudio : BasePreferenceFragment(), SharedPreferences.OnSharedPre
findPreference<Preference>("enable_headset_detection")?.isVisible = false
findPreference<Preference>("enable_play_on_headset_insertion")?.isVisible = false
+ findPreference<Preference>("ignore_headset_media_button_presses")?.isVisible = false
findPreference<Preference>("headset_prefs_category")?.isVisible = false
val aoutPref = findPreference<ListPreference>("aout")
findPreference<Preference>(RESUME_PLAYBACK)?.isVisible = false
diff --git a/application/vlc-android/AndroidManifest.xml b/application/vlc-android/AndroidManifest.xml
index cfe224cfc6..de25a926d5 100644
--- a/application/vlc-android/AndroidManifest.xml
+++ b/application/vlc-android/AndroidManifest.xml
@@ -5,6 +5,7 @@
<uses-sdk tools:overrideLibrary="com.jraska.livedata.ktx, com.jraska.livedata, tools.fastlane.screengrab, android_libs.ub_uiautomator"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission
android:name="android.permission.READ_LOGS"
android:maxSdkVersion="18"/> <!-- android:maxSdkVersion="15" -->
diff --git a/application/vlc-android/res/xml/preferences_audio.xml b/application/vlc-android/res/xml/preferences_audio.xml
index d9d360322c..2b71812951 100644
--- a/application/vlc-android/res/xml/preferences_audio.xml
+++ b/application/vlc-android/res/xml/preferences_audio.xml
@@ -53,6 +53,14 @@
android:dependency="enable_headset_detection"
android:summary="@string/enable_play_on_headset_insertion_summary"
android:title="@string/enable_play_on_headset_insertion"/>
+ <CheckBoxPreference
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:defaultValue="false"
+ android:key="ignore_headset_media_button_presses"
+ android:summary="@string/ignore_headset_media_button_presses_summary"
+ android:title="@string/ignore_headset_media_button_presses"
+ app:singleLineTitle="false" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/controls_prefs_category">
diff --git a/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt b/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt
index 99ef649868..a2b27b9140 100644
--- a/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt
+++ b/application/vlc-android/src/org/videolan/vlc/MediaSessionCallback.kt
@@ -23,6 +23,8 @@
package org.videolan.vlc
import android.annotation.SuppressLint
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothProfile
import android.content.ContentUris
import android.content.Intent
import android.net.Uri
@@ -31,7 +33,6 @@ import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import android.view.KeyEvent
-import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
@@ -67,6 +68,23 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean {
val keyEvent = mediaButtonEvent.parcelable(Intent.EXTRA_KEY_EVENT) as KeyEvent? ?: return false
+
+ if (playbackService.detectHeadset &&
+ playbackService.settings.getBoolean("ignore_headset_media_button_presses", false)) {
+ // Wired headset
+ if (playbackService.headsetInserted && isWiredHeadsetHardKey(keyEvent)) {
+ return true
+ }
+
+ // Bluetooth headset
+ val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
+ if (bluetoothAdapter != null &&
+ BluetoothAdapter.STATE_CONNECTED == bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) &&
+ isBluetoothHeadsetHardKey(keyEvent)) {
+ return true
+ }
+ }
+
if (!playbackService.hasMedia()
&& (keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)) {
return if (keyEvent.action == KeyEvent.ACTION_DOWN) {
@@ -109,6 +127,26 @@ internal class MediaSessionCallback(private val playbackService: PlaybackService
return super.onMediaButtonEvent(mediaButtonEvent)
}
+ /**
+ * The following two functions are based on the following KeyEvent captures. They may need to be updated if the behavior changes in the future.
+ *
+ * KeyEvent from Media Control UI:
+ * {action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY_PAUSE, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0}
+ *
+ * KeyEvent from a wired headset's media button:
+ * {action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY_PAUSE, scanCode=0, metaState=0, flags=0x40000000, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0}
+ *
+ * KeyEvent from a Bluetooth earphone:
+ * {action=ACTION_DOWN, keyCode=KEYCODE_MEDIA_PLAY, scanCode=0, metaState=0, flags=0x0, repeatCount=0, eventTime=0, downTime=0, deviceId=-1, source=0x0, displayId=0}
+ */
+ private fun isWiredHeadsetHardKey(keyEvent: KeyEvent): Boolean {
+ return !(keyEvent.deviceId == -1 && keyEvent.flags == 0x0)
+ }
+
+ private fun isBluetoothHeadsetHardKey(keyEvent: KeyEvent): Boolean {
+ return keyEvent.keyCode != KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE && keyEvent.deviceId == -1 && keyEvent.flags == 0x0
+ }
+
/**
* This function is based on the following KeyEvent captures. This may need to be updated if the behavior changes in the future.
*
diff --git a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
index ced87dc42b..c0a34203fa 100644
--- a/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
+++ b/application/vlc-android/src/org/videolan/vlc/PlaybackService.kt
@@ -108,7 +108,8 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
private val callbacks = mutableListOf<Callback>()
private val subtitleMessage = ArrayDeque<String>(1)
private lateinit var cbActor: SendChannel<CbAction>
- private var detectHeadset = true
+ var detectHeadset = true
+ var headsetInserted = false
private lateinit var wakeLock: PowerManager.WakeLock
private val audioFocusHelper by lazy { VLCAudioFocusHelper(this) }
private lateinit var browserCallback: MediaBrowserCallback
@@ -182,12 +183,14 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner, CoroutineSc
MiniPlayerAppWidgetProvider.ACTION_WIDGET_ENABLED, VLCAppWidgetProvider.ACTION_WIDGET_DISABLED -> updateHasWidget()
AudioManager.ACTION_AUDIO_BECOMING_NOISY -> if (detectHeadset) {
if (BuildConfig.DEBUG) Log.i(TAG, "Becoming noisy")
+ headsetInserted = false
wasPlaying = isPlaying
if (wasPlaying && playlistManager.hasCurrentMedia())
pause()
}
Intent.ACTION_HEADSET_PLUG -> if (detectHeadset && state != 0) {
if (BuildConfig.DEBUG) Log.i(TAG, "Headset Inserted.")
+ headsetInserted = true
if (wasPlaying && playlistManager.hasCurrentMedia() && settings.getBoolean("enable_play_on_headset_insertion", false))
play()
}
More information about the Android
mailing list