Create foreground call service.
This commit is contained in:
parent
4a4edcf82a
commit
4169f580b8
|
@ -362,4 +362,7 @@
|
|||
|
||||
<string name="key_verification_request_fallback_message">%s is requesting to verify your key, but your client does not support in-chat key verification. You will need to use legacy key verification to verify keys.</string>
|
||||
|
||||
<string name="call_notification_answer">Accept</string>
|
||||
<string name="call_notification_reject">Reject</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
<!-- READ_PHONE_STATE is needed only if your calling app reads numbers from the `PHONE_STATE`
|
||||
intent action. -->
|
||||
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
|
||||
<!-- Adding CAMERA permission prevents Chromebooks to see the application on the PlayStore -->
|
||||
<!-- Tell that the Camera is not mandatory to install the application -->
|
||||
|
@ -195,15 +197,24 @@
|
|||
android:name=".core.services.VectorSyncService"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name=".features.call.VectorConnectionService"
|
||||
<service
|
||||
android:name=".features.call.telecom.VectorConnectionService"
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.telecom.ConnectionService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".features.call.service.CallHeadsUpService"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- Receivers -->
|
||||
|
||||
<receiver
|
||||
android:name=".features.call.service.CallHeadsUpActionReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- Exported false, should only be accessible from this app!! -->
|
||||
<receiver
|
||||
android:name=".features.notifications.NotificationBroadcastReceiver"
|
||||
|
|
|
@ -24,7 +24,7 @@ import android.content.Intent
|
|||
import android.os.Binder
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.riotx.core.extensions.vectorComponent
|
||||
import im.vector.riotx.features.call.CallConnection
|
||||
import im.vector.riotx.features.call.telecom.CallConnection
|
||||
import im.vector.riotx.features.notifications.NotificationUtils
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
|
@ -18,13 +18,12 @@ package im.vector.riotx.features.call
|
|||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.telecom.PhoneAccount
|
||||
import android.telecom.PhoneAccountHandle
|
||||
import android.telecom.TelecomManager
|
||||
import android.telecom.VideoProfile
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.matrix.android.api.session.call.CallsListener
|
||||
import im.vector.matrix.android.api.session.call.EglUtils
|
||||
|
@ -33,6 +32,8 @@ import im.vector.matrix.android.api.session.room.model.call.CallHangupContent
|
|||
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
|
||||
import im.vector.riotx.BuildConfig
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.call.service.CallHeadsUpService
|
||||
import im.vector.riotx.features.call.telecom.VectorConnectionService
|
||||
import org.webrtc.AudioSource
|
||||
import org.webrtc.AudioTrack
|
||||
import org.webrtc.DefaultVideoDecoderFactory
|
||||
|
@ -356,6 +357,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onCallInviteReceived(signalingRoomId: String, callInviteContent: CallInviteContent) {
|
||||
val callHeadsUpServiceIntent = Intent(context, CallHeadsUpService::class.java)
|
||||
ContextCompat.startForegroundService(context, callHeadsUpServiceIntent)
|
||||
/*
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
ContextCompat.getSystemService(context, TelecomManager::class.java)?.let { telecomManager ->
|
||||
phoneAccountHandle?.let { phoneAccountHandle ->
|
||||
|
@ -372,6 +376,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.call.service
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import timber.log.Timber
|
||||
|
||||
class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
when (intent?.getIntExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, 0)) {
|
||||
CallHeadsUpService.CALL_ACTION_ANSWER -> onCallAnswerClicked()
|
||||
CallHeadsUpService.CALL_ACTION_REJECT -> onCallRejectClicked()
|
||||
}
|
||||
|
||||
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
||||
context.stopService(Intent(context, CallHeadsUpService::class.java))
|
||||
}
|
||||
|
||||
private fun onCallRejectClicked() {
|
||||
Timber.d("onCallRejectClicked")
|
||||
}
|
||||
|
||||
private fun onCallAnswerClicked() {
|
||||
Timber.d("onCallAnswerClicked")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.call.service
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import im.vector.riotx.R
|
||||
|
||||
class CallHeadsUpService : Service() {
|
||||
|
||||
private val CHANNEL_ID = "CallChannel"
|
||||
private val CHANNEL_NAME = "Call Channel"
|
||||
private val CHANNEL_DESCRIPTION = "Call Notifications"
|
||||
|
||||
private val binder: IBinder = CallHeadsUpServiceBinder()
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return binder
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
//val callHeadsUpServiceArgs: CallHeadsUpServiceArgs? = intent?.extras?.getParcelable(EXTRA_CALL_HEADS_UP_SERVICE_PARAMS)
|
||||
|
||||
val answerCallActionReceiver = Intent(applicationContext, CallHeadsUpActionReceiver::class.java).apply {
|
||||
putExtra(EXTRA_CALL_ACTION_KEY, CALL_ACTION_ANSWER)
|
||||
}
|
||||
val rejectCallActionReceiver = Intent(applicationContext, CallHeadsUpActionReceiver::class.java).apply {
|
||||
putExtra(EXTRA_CALL_ACTION_KEY, CALL_ACTION_REJECT)
|
||||
}
|
||||
|
||||
val answerCallPendingIntent = PendingIntent.getBroadcast(applicationContext, CALL_ACTION_ANSWER, answerCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val rejectCallPendingIntent = PendingIntent.getBroadcast(applicationContext, CALL_ACTION_REJECT, rejectCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
createNotificationChannel()
|
||||
|
||||
val notification = NotificationCompat
|
||||
.Builder(applicationContext, CHANNEL_ID)
|
||||
.setContentTitle("Title")
|
||||
.setContentText("Content")
|
||||
.setSmallIcon(R.drawable.ic_call_incoming)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.addAction(R.drawable.ic_call_incoming, getString(R.string.call_notification_answer), answerCallPendingIntent)
|
||||
.addAction(R.drawable.ic_call_incoming, getString(R.string.call_notification_reject), rejectCallPendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSound(Uri.parse("android.resource://" + applicationContext.packageName + "/ring.ogg"))
|
||||
.setFullScreenIntent(answerCallPendingIntent, true)
|
||||
.build()
|
||||
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
|
||||
val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = CHANNEL_DESCRIPTION
|
||||
setSound(
|
||||
Uri.parse("android.resource://" + applicationContext.packageName + "/ring.ogg"),
|
||||
AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setLegacyStreamType(AudioManager.STREAM_RING)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
applicationContext.getSystemService(NotificationManager::class.java)?.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
inner class CallHeadsUpServiceBinder : Binder() {
|
||||
|
||||
fun getService() = this@CallHeadsUpService
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_CALL_HEADS_UP_SERVICE_PARAMS = "EXTRA_CALL_PARAMS"
|
||||
|
||||
const val EXTRA_CALL_ACTION_KEY = "EXTRA_CALL_ACTION_KEY"
|
||||
const val CALL_ACTION_ANSWER = 100
|
||||
const val CALL_ACTION_REJECT = 101
|
||||
|
||||
private const val NOTIFICATION_ID = 999
|
||||
|
||||
fun newInstance(context: Context, callerDisplayName: String, isIncomingCall: Boolean, isVideoCall: Boolean): Intent {
|
||||
val args = CallHeadsUpServiceArgs(callerDisplayName, isIncomingCall, isVideoCall)
|
||||
return Intent(context, CallHeadsUpService::class.java).apply {
|
||||
putExtra(EXTRA_CALL_HEADS_UP_SERVICE_PARAMS, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.call.service
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class CallHeadsUpServiceArgs(
|
||||
val callerDisplayName: String,
|
||||
val isIncomingCall: Boolean,
|
||||
val isVideoCall: Boolean
|
||||
) : Parcelable
|
|
@ -14,13 +14,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.call
|
||||
package im.vector.riotx.features.call.telecom
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.telecom.Connection
|
||||
import android.telecom.DisconnectCause
|
||||
import androidx.annotation.RequiresApi
|
||||
import im.vector.riotx.features.call.VectorCallViewActions
|
||||
import im.vector.riotx.features.call.VectorCallViewModel
|
||||
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
|
||||
import org.webrtc.Camera1Enumerator
|
||||
import org.webrtc.Camera2Enumerator
|
||||
import org.webrtc.IceCandidate
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotx.features.call
|
||||
package im.vector.riotx.features.call.telecom
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
|
@ -127,7 +127,7 @@ import im.vector.riotx.features.attachments.ContactAttachment
|
|||
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewActivity
|
||||
import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs
|
||||
import im.vector.riotx.features.attachments.toGroupedContentAttachmentData
|
||||
import im.vector.riotx.features.call.VectorCallActivity
|
||||
import im.vector.riotx.features.call.service.CallHeadsUpService
|
||||
import im.vector.riotx.features.command.Command
|
||||
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
||||
import im.vector.riotx.features.crypto.util.toImageRes
|
||||
|
@ -485,9 +485,13 @@ class RoomDetailFragment @Inject constructor(
|
|||
return true
|
||||
}
|
||||
if (item.itemId == R.id.voip_call) {
|
||||
/*
|
||||
VectorCallActivity.newIntent(requireContext(), roomDetailArgs.roomId).let {
|
||||
startActivity(it)
|
||||
}
|
||||
*/
|
||||
val callHeadsUpServiceIntent = Intent(requireContext(), CallHeadsUpService::class.java)
|
||||
ContextCompat.startForegroundService(requireContext(), callHeadsUpServiceIntent)
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M20.01,15.38c-1.23,0 -2.42,-0.2 -3.53,-0.56 -0.35,-0.12 -0.74,-0.03 -1.01,0.24l-1.57,1.97c-2.83,-1.35 -5.48,-3.9 -6.89,-6.83l1.95,-1.66c0.27,-0.28 0.35,-0.67 0.24,-1.02 -0.37,-1.11 -0.56,-2.3 -0.56,-3.53 0,-0.54 -0.45,-0.99 -0.99,-0.99H4.19C3.65,3 3,3.24 3,3.99 3,13.28 10.73,21 20.01,21c0.71,0 0.99,-0.63 0.99,-1.18v-3.45c0,-0.54 -0.45,-0.99 -0.99,-0.99z"/>
|
||||
</vector>
|
Loading…
Reference in New Issue