Revert "Use MediaButtonReceiver from androidx.media to handle headset button actions"

This reverts commit ad51d09672.
This commit is contained in:
Prathamesh More 2022-06-12 20:15:43 +05:30
parent 192b5c2ac7
commit e4a6906231
3 changed files with 218 additions and 8 deletions

View file

@ -34,12 +34,12 @@
android:configChanges="locale|layoutDirection" android:configChanges="locale|layoutDirection"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:restoreAnyVersion="true" android:restoreAnyVersion="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.RetroMusic.FollowSystem" android:theme="@style/Theme.RetroMusic.FollowSystem"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
tools:targetApi="m"> tools:targetApi="m">
<activity <activity
android:name=".activities.MainActivity" android:name=".activities.MainActivity"
@ -194,7 +194,7 @@
</provider> </provider>
<receiver <receiver
android:name="androidx.media.session.MediaButtonReceiver" android:name=".service.MediaButtonIntentReceiver"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" /> <action android:name="android.intent.action.MEDIA_BUTTON" />
@ -305,9 +305,6 @@
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService" /> <action android:name="android.media.browse.MediaBrowserService" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</service> </service>
<meta-data <meta-data

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.service
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Message
import android.os.PowerManager
import android.os.PowerManager.WakeLock
import android.util.Log
import android.view.KeyEvent
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.media.session.MediaButtonReceiver
import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_PAUSE
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_PLAY
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_STOP
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
/**
* Used to control headset playback.
* Single press: pause/resume
* Double press: actionNext track
* Triple press: previous track
*/
class MediaButtonIntentReceiver : MediaButtonReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (DEBUG) Log.v(TAG, "Received intent: $intent")
if (handleIntent(context, intent) && isOrderedBroadcast) {
abortBroadcast()
}
}
companion object {
val TAG: String = MediaButtonIntentReceiver::class.java.simpleName
private val DEBUG = BuildConfig.DEBUG
private const val MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2
private const val DOUBLE_CLICK = 400
private var wakeLock: WakeLock? = null
private var mClickCounter = 0
private var mLastClickTime: Long = 0
@SuppressLint("HandlerLeak") // false alarm, handler is already static
private val mHandler = object : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT -> {
val clickCount = msg.arg1
if (DEBUG) Log.v(TAG, "Handling headset click, count = $clickCount")
val command = when (clickCount) {
1 -> ACTION_TOGGLE_PAUSE
2 -> ACTION_SKIP
3 -> ACTION_REWIND
else -> null
}
if (command != null) {
val context = msg.obj as Context
startService(context, command)
}
}
}
releaseWakeLockIfHandlerIdle()
}
}
fun handleIntent(context: Context, intent: Intent): Boolean {
println("Intent Action: ${intent.action}")
val intentAction = intent.action
if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
val event = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
?: return false
val keycode = event.keyCode
val action = event.action
val eventTime = if (event.eventTime != 0L)
event.eventTime
else
System.currentTimeMillis()
var command: String? = null
when (keycode) {
KeyEvent.KEYCODE_MEDIA_STOP -> command = ACTION_STOP
KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> command =
ACTION_TOGGLE_PAUSE
KeyEvent.KEYCODE_MEDIA_NEXT -> command = ACTION_SKIP
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> command = ACTION_REWIND
KeyEvent.KEYCODE_MEDIA_PAUSE -> command = ACTION_PAUSE
KeyEvent.KEYCODE_MEDIA_PLAY -> command = ACTION_PLAY
}
if (command != null) {
if (action == KeyEvent.ACTION_DOWN) {
if (event.repeatCount == 0) {
// Only consider the first event in a sequence, not the repeat events,
// so that we don't trigger in cases where the first event went to
// a different app (e.g. when the user ends a phone call by
// long pressing the headset button)
// The service may or may not be running, but we need to send it
// a command.
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
mClickCounter = 0
}
mClickCounter++
if (DEBUG) Log.v(TAG, "Got headset click, count = $mClickCounter")
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)
val msg = mHandler.obtainMessage(
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context
)
val delay = (if (mClickCounter < 3) DOUBLE_CLICK else 0).toLong()
if (mClickCounter >= 3) {
mClickCounter = 0
}
mLastClickTime = eventTime
acquireWakeLockAndSendMessage(context, msg, delay)
} else {
startService(context, command)
}
return true
}
}
}
}
return false
}
private fun startService(context: Context, command: String?) {
val intent = Intent(context, MusicService::class.java)
intent.action = command
try {
// IMPORTANT NOTE: (kind of a hack)
// on Android O and above the following crashes when the app is not running
// there is no good way to check whether the app is running so we catch the exception
// we do not always want to use startForegroundService() because then one gets an ANR
// if no notification is displayed via startForeground()
// according to Play analytics this happens a lot, I suppose for example if command = PAUSE
context.startService(intent)
} catch (ignored: IllegalStateException) {
ContextCompat.startForegroundService(context, intent)
}
}
private fun acquireWakeLockAndSendMessage(context: Context, msg: Message, delay: Long) {
if (wakeLock == null) {
val appContext = context.applicationContext
val pm = appContext.getSystemService<PowerManager>()
wakeLock = pm?.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"RetroMusicApp:Wakelock headset button"
)
wakeLock!!.setReferenceCounted(false)
}
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what)
// Make sure we don't indefinitely hold the wake lock under any circumstances
wakeLock!!.acquire(10000)
mHandler.sendMessageDelayed(msg, delay)
}
private fun releaseWakeLockIfHandlerIdle() {
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock")
return
}
if (wakeLock != null) {
if (DEBUG) Log.v(TAG, "Releasing wake lock")
wakeLock!!.release()
wakeLock = null
}
}
}
}

View file

@ -15,6 +15,7 @@ package code.name.monkey.retromusic.service
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothDevice.EXTRA_DEVICE import android.bluetooth.BluetoothDevice.EXTRA_DEVICE
@ -39,7 +40,6 @@ import android.widget.Toast
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.media.MediaBrowserServiceCompat import androidx.media.MediaBrowserServiceCompat
import androidx.media.session.MediaButtonReceiver.handleIntent
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
@ -94,6 +94,7 @@ import kotlinx.coroutines.Dispatchers.Main
import org.koin.java.KoinJavaComponent.get import org.koin.java.KoinJavaComponent.get
import java.util.* import java.util.*
/** /**
* @author Karim Abou Zeid (kabouzeid), Andrew Neal. Modified by Prathamesh More * @author Karim Abou Zeid (kabouzeid), Andrew Neal. Modified by Prathamesh More
*/ */
@ -649,7 +650,6 @@ class MusicService : MediaBrowserServiceCompat(),
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null && intent.action != null) { if (intent != null && intent.action != null) {
handleIntent(mediaSession, intent)
serviceScope.launch { serviceScope.launch {
restoreQueuesAndPositionIfNecessary() restoreQueuesAndPositionIfNecessary()
when (intent.action) { when (intent.action) {
@ -1305,13 +1305,25 @@ class MusicService : MediaBrowserServiceCompat(),
} }
private fun setupMediaSession() { private fun setupMediaSession() {
val mediaButtonReceiverComponentName = ComponentName(applicationContext,
MediaButtonIntentReceiver::class.java)
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
mediaButtonIntent.component = mediaButtonReceiverComponentName
val mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(
applicationContext, 0, mediaButtonIntent,
if (VersionUtils.hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE else 0
)
mediaSession = MediaSessionCompat( mediaSession = MediaSessionCompat(
this, this,
"RetroMusicPlayer" BuildConfig.APPLICATION_ID,
mediaButtonReceiverComponentName,
mediaButtonReceiverPendingIntent
) )
val mediaSessionCallback = MediaSessionCallback(this) val mediaSessionCallback = MediaSessionCallback(this)
mediaSession?.setCallback(mediaSessionCallback) mediaSession?.setCallback(mediaSessionCallback)
mediaSession?.isActive = true mediaSession?.isActive = true
mediaSession?.setMediaButtonReceiver(mediaButtonReceiverPendingIntent)
} }
inner class MusicBinder : Binder() { inner class MusicBinder : Binder() {