Support headset buttons in background
This commit is contained in:
parent
9653f082a3
commit
4c61dfef62
|
@ -196,7 +196,12 @@
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".core.services.CallService"
|
android:name=".core.services.CallService"
|
||||||
android:exported="false" />
|
android:exported="false" >
|
||||||
|
<!-- in order to get headset button events -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".core.services.VectorSyncService"
|
android:name=".core.services.VectorSyncService"
|
||||||
|
@ -226,6 +231,16 @@
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
A media button receiver receives and helps translate hardware media playback buttons,
|
||||||
|
such as those found on wired and wireless headsets, into the appropriate callbacks in your app.
|
||||||
|
-->
|
||||||
|
<receiver android:name="androidx.media.session.MediaButtonReceiver" >
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<!-- Providers -->
|
<!-- Providers -->
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
|
|
|
@ -20,7 +20,10 @@ package im.vector.riotx.core.services
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
import android.view.KeyEvent
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.media.session.MediaButtonReceiver
|
||||||
import im.vector.riotx.core.extensions.vectorComponent
|
import im.vector.riotx.core.extensions.vectorComponent
|
||||||
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
|
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
|
||||||
import im.vector.riotx.features.call.telecom.CallConnection
|
import im.vector.riotx.features.call.telecom.CallConnection
|
||||||
|
@ -34,23 +37,27 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
||||||
|
|
||||||
private val connections = mutableMapOf<String, CallConnection>()
|
private val connections = mutableMapOf<String, CallConnection>()
|
||||||
|
|
||||||
/**
|
|
||||||
* call in progress (foreground notification)
|
|
||||||
*/
|
|
||||||
// private var mCallIdInProgress: String? = null
|
|
||||||
|
|
||||||
private lateinit var notificationUtils: NotificationUtils
|
private lateinit var notificationUtils: NotificationUtils
|
||||||
private lateinit var webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
private lateinit var webRtcPeerConnectionManager: WebRtcPeerConnectionManager
|
||||||
|
|
||||||
/**
|
|
||||||
* incoming (foreground notification)
|
|
||||||
*/
|
|
||||||
// private var mIncomingCallId: String? = null
|
|
||||||
|
|
||||||
private var callRingPlayer: CallRingPlayer? = null
|
private var callRingPlayer: CallRingPlayer? = null
|
||||||
|
|
||||||
private var wiredHeadsetStateReceiver: WiredHeadsetStateReceiver? = null
|
private var wiredHeadsetStateReceiver: WiredHeadsetStateReceiver? = null
|
||||||
|
|
||||||
|
// A media button receiver receives and helps translate hardware media playback buttons,
|
||||||
|
// such as those found on wired and wireless headsets, into the appropriate callbacks in your app
|
||||||
|
private var mediaSession : MediaSessionCompat? = null
|
||||||
|
private val mediaSessionButtonCallback = object : MediaSessionCompat.Callback() {
|
||||||
|
override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {
|
||||||
|
val keyEvent = mediaButtonEvent?.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return false
|
||||||
|
if (keyEvent.keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
|
||||||
|
webRtcPeerConnectionManager.headSetButtonTapped()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notificationUtils = vectorComponent().notificationUtils()
|
notificationUtils = vectorComponent().notificationUtils()
|
||||||
|
@ -64,22 +71,36 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
||||||
callRingPlayer?.stop()
|
callRingPlayer?.stop()
|
||||||
wiredHeadsetStateReceiver?.let { WiredHeadsetStateReceiver.unRegister(this, it) }
|
wiredHeadsetStateReceiver?.let { WiredHeadsetStateReceiver.unRegister(this, it) }
|
||||||
wiredHeadsetStateReceiver = null
|
wiredHeadsetStateReceiver = null
|
||||||
|
mediaSession?.release()
|
||||||
|
mediaSession = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
Timber.v("## VOIP onStartCommand $intent")
|
Timber.v("## VOIP onStartCommand $intent")
|
||||||
|
if (mediaSession == null) {
|
||||||
|
mediaSession = MediaSessionCompat(applicationContext, CallService::class.java.name).apply {
|
||||||
|
setCallback(mediaSessionButtonCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
// Service started again by the system.
|
// Service started again by the system.
|
||||||
// TODO What do we do here?
|
// TODO What do we do here?
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
mediaSession?.let {
|
||||||
|
// This ensures that the correct callbacks to MediaSessionCompat.Callback
|
||||||
|
// will be triggered based on the incoming KeyEvent.
|
||||||
|
MediaButtonReceiver.handleIntent(it, intent)
|
||||||
|
}
|
||||||
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_INCOMING_RINGING_CALL -> {
|
ACTION_INCOMING_RINGING_CALL -> {
|
||||||
|
mediaSession?.isActive = true
|
||||||
callRingPlayer?.start()
|
callRingPlayer?.start()
|
||||||
displayIncomingCallNotification(intent)
|
displayIncomingCallNotification(intent)
|
||||||
}
|
}
|
||||||
ACTION_OUTGOING_RINGING_CALL -> {
|
ACTION_OUTGOING_RINGING_CALL -> {
|
||||||
|
mediaSession?.isActive = true
|
||||||
callRingPlayer?.start()
|
callRingPlayer?.start()
|
||||||
displayOutgoingRingingCallNotification(intent)
|
displayOutgoingRingingCallNotification(intent)
|
||||||
}
|
}
|
||||||
|
@ -221,6 +242,7 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
||||||
private fun hideCallNotifications() {
|
private fun hideCallNotifications() {
|
||||||
val notification = notificationUtils.buildCallEndedNotification()
|
val notification = notificationUtils.buildCallEndedNotification()
|
||||||
|
|
||||||
|
mediaSession?.isActive = false
|
||||||
// It's mandatory to startForeground to avoid crash
|
// It's mandatory to startForeground to avoid crash
|
||||||
startForeground(NOTIFICATION_ID, notification)
|
startForeground(NOTIFICATION_ID, notification)
|
||||||
|
|
||||||
|
|
|
@ -326,12 +326,16 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
// for newer version, it will be passed automatically to active media session
|
||||||
|
// in call service
|
||||||
when (keyCode) {
|
when (keyCode) {
|
||||||
KeyEvent.KEYCODE_HEADSETHOOK -> {
|
KeyEvent.KEYCODE_HEADSETHOOK -> {
|
||||||
callViewModel.handle(VectorCallViewActions.HeadSetButtonPressed)
|
callViewModel.handle(VectorCallViewActions.HeadSetButtonPressed)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun headSetButtonTapped() {
|
||||||
|
Timber.v("## VOIP headSetButtonTapped")
|
||||||
|
val call = currentCall?.mxCall ?: return
|
||||||
|
if (call.state is CallState.LocalRinging) {
|
||||||
|
// accept call
|
||||||
|
acceptIncomingCall()
|
||||||
|
}
|
||||||
|
if (call.state is CallState.Connected) {
|
||||||
|
// end call?
|
||||||
|
endCall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createPeerConnectionFactory() {
|
private fun createPeerConnectionFactory() {
|
||||||
if (peerConnectionFactory != null) return
|
if (peerConnectionFactory != null) return
|
||||||
Timber.v("## VOIP createPeerConnectionFactory")
|
Timber.v("## VOIP createPeerConnectionFactory")
|
||||||
|
|
Loading…
Reference in New Issue