diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt
index 37ce1e62d6..a3bbcd6444 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/DefaultCallSignalingService.kt
@@ -38,6 +38,7 @@ import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
import im.vector.matrix.android.internal.session.room.send.RoomEventSender
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
+import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
@@ -100,8 +101,8 @@ internal class DefaultCallSignalingService @Inject constructor(
override fun removeCallListener(listener: CallsListener) {
callListeners.remove(listener)
}
-
override fun getCallWithId(callId: String): MxCall? {
+ Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}")
return activeCalls.find { it.callId == callId }
}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt
index 53c075579a..90b475d5b8 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/call/model/MxCallImpl.kt
@@ -83,6 +83,7 @@ internal class MxCallImpl(
override fun offerSdp(sdp: SessionDescription) {
if (!isOutgoing) return
+ Timber.v("## VOIP offerSdp $callId")
state = CallState.DIALING
CallInviteContent(
callId = callId,
@@ -113,6 +114,7 @@ internal class MxCallImpl(
}
override fun hangUp() {
+ Timber.v("## VOIP hangup $callId")
CallHangupContent(
callId = callId
)
@@ -122,6 +124,7 @@ internal class MxCallImpl(
}
override fun accept(sdp: SessionDescription) {
+ Timber.v("## VOIP accept $callId")
if (isOutgoing) return
state = CallState.ANSWERING
CallAnswerContent(
diff --git a/matrix-sdk-android/src/main/res/values/strings.xml b/matrix-sdk-android/src/main/res/values/strings.xml
index bab4f6c622..a992b11c06 100644
--- a/matrix-sdk-android/src/main/res/values/strings.xml
+++ b/matrix-sdk-android/src/main/res/values/strings.xml
@@ -365,5 +365,6 @@
Accept
Decline
+ Hang Up
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 6b0253c5fc..64299ff1ae 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
+
diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt b/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt
index caf1bf90f8..83ac540830 100644
--- a/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt
+++ b/vector/src/main/java/im/vector/riotx/core/extensions/FragmentManager.kt
@@ -17,9 +17,14 @@
package im.vector.riotx.core.extensions
import androidx.fragment.app.FragmentTransaction
+import im.vector.matrix.android.api.extensions.tryThis
inline fun androidx.fragment.app.FragmentManager.commitTransactionNow(func: FragmentTransaction.() -> FragmentTransaction) {
- beginTransaction().func().commitNow()
+ // Could throw and make the app crash
+ // e.g sharedActionViewModel.observe()
+ tryThis("Failed to commitTransactionNow") {
+ beginTransaction().func().commitNow()
+ }
}
inline fun androidx.fragment.app.FragmentManager.commitTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
index 270e67cf34..ba9e7320d2 100644
--- a/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorBaseActivity.kt
@@ -165,6 +165,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
}
override fun onCreate(savedInstanceState: Bundle?) {
+ Timber.i("onCreate Activity ${this.javaClass.simpleName}")
val vectorComponent = getVectorComponent()
screenComponent = DaggerScreenComponent.factory().create(vectorComponent, this)
val timeForInjection = measureTimeMillis {
@@ -252,6 +253,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onDestroy() {
super.onDestroy()
+ Timber.i("onDestroy Activity ${this.javaClass.simpleName}")
unBinder?.unbind()
unBinder = null
@@ -279,6 +281,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
override fun onPause() {
super.onPause()
+ Timber.i("onPause Activity ${this.javaClass.simpleName}")
rageShake.stop()
diff --git a/vector/src/main/java/im/vector/riotx/core/services/CallRingPlayer.kt b/vector/src/main/java/im/vector/riotx/core/services/CallRingPlayer.kt
new file mode 100644
index 0000000000..f7f64d65f5
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/core/services/CallRingPlayer.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.core.services
+
+import android.content.Context
+import android.media.AudioAttributes
+import android.media.AudioManager
+import android.media.MediaPlayer
+import android.os.Build
+import im.vector.riotx.R
+import timber.log.Timber
+
+class CallRingPlayer(
+ context: Context
+) {
+
+ private val applicationContext = context.applicationContext
+
+ private var player: MediaPlayer? = null
+
+ fun start() {
+ val audioManager: AudioManager = applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+ player?.release()
+ player = createPlayer()
+
+ // Check if sound is enabled
+ val ringerMode = audioManager.ringerMode
+ if (player != null && ringerMode == AudioManager.RINGER_MODE_NORMAL) {
+ try {
+ if (player?.isPlaying == false) {
+ player?.start()
+ Timber.v("## VOIP Starting ringing")
+ } else {
+ Timber.v("## VOIP already playing")
+ }
+ } catch (failure: Throwable) {
+ Timber.e(failure, "## VOIP Failed to start ringing")
+ player = null
+ }
+ } else {
+ Timber.v("## VOIP Can't play $player ode $ringerMode")
+ }
+ }
+
+ fun stop() {
+ player?.release()
+ player = null
+ }
+
+ private fun createPlayer(): MediaPlayer? {
+ try {
+ val mediaPlayer = MediaPlayer.create(applicationContext, R.raw.ring)
+
+ mediaPlayer.setOnErrorListener(MediaPlayerErrorListener())
+ mediaPlayer.isLooping = true
+ if (Build.VERSION.SDK_INT <= 21) {
+ @Suppress("DEPRECATION")
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING)
+ } else {
+ mediaPlayer.setAudioAttributes(AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ .build())
+ }
+ return mediaPlayer
+ } catch (failure: Throwable) {
+ Timber.e(failure, "Failed to create Call ring player")
+ return null
+ }
+ }
+
+ inner class MediaPlayerErrorListener : MediaPlayer.OnErrorListener {
+ override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
+ Timber.w("onError($mp, $what, $extra")
+ player = null
+ return false
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/riotx/core/services/CallService.kt b/vector/src/main/java/im/vector/riotx/core/services/CallService.kt
index 8337e56403..f10bbd908d 100644
--- a/vector/src/main/java/im/vector/riotx/core/services/CallService.kt
+++ b/vector/src/main/java/im/vector/riotx/core/services/CallService.kt
@@ -1,6 +1,6 @@
-
/*
* Copyright 2019 New Vector Ltd
+ * Copyright 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.
@@ -20,7 +20,6 @@ package im.vector.riotx.core.services
import android.content.Context
import android.content.Intent
import android.os.Binder
-import android.text.TextUtils
import androidx.core.content.ContextCompat
import im.vector.riotx.core.extensions.vectorComponent
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
@@ -38,7 +37,7 @@ class CallService : VectorService() {
/**
* call in progress (foreground notification)
*/
- private var mCallIdInProgress: String? = null
+// private var mCallIdInProgress: String? = null
private lateinit var notificationUtils: NotificationUtils
private lateinit var webRtcPeerConnectionManager: WebRtcPeerConnectionManager
@@ -46,15 +45,19 @@ class CallService : VectorService() {
/**
* incoming (foreground notification)
*/
- private var mIncomingCallId: String? = null
+// private var mIncomingCallId: String? = null
+
+ private var callRingPlayer: CallRingPlayer? = null
override fun onCreate() {
super.onCreate()
notificationUtils = vectorComponent().notificationUtils()
webRtcPeerConnectionManager = vectorComponent().webRtcPeerConnectionManager()
+ callRingPlayer = CallRingPlayer(applicationContext)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Timber.v("## VOIP onStartCommand $intent")
if (intent == null) {
// Service started again by the system.
// TODO What do we do here?
@@ -62,12 +65,34 @@ class CallService : VectorService() {
}
when (intent.action) {
- ACTION_INCOMING_CALL -> displayIncomingCallNotification(intent)
- ACTION_PENDING_CALL -> displayCallInProgressNotification(intent)
- ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
- else ->
+ ACTION_INCOMING_RINGING_CALL -> {
+ callRingPlayer?.start()
+ displayIncomingCallNotification(intent)
+ }
+ ACTION_OUTGOING_RINGING_CALL -> {
+ callRingPlayer?.start()
+ displayOutgoingRingingCallNotification(intent)
+ }
+ ACTION_ONGOING_CALL -> {
+ callRingPlayer?.stop()
+ displayCallInProgressNotification(intent)
+ }
+ ACTION_NO_ACTIVE_CALL -> hideCallNotifications()
+ ACTION_CALL_CONNECTING -> {
+ // lower notification priority
+ displayCallInProgressNotification(intent)
+ // stop ringing
+ callRingPlayer?.stop()
+ }
+ ACTION_ONGOING_CALL_BG -> {
+ // there is an ongoing call but call activity is in background
+ displayCallOnGoingInBackground(intent)
+ }
+ else -> {
// Should not happen
+ callRingPlayer?.stop()
myStopSelf()
+ }
}
// We want the system to restore the service if killed
@@ -87,29 +112,29 @@ class CallService : VectorService() {
* @param callId the callId
*/
private fun displayIncomingCallNotification(intent: Intent) {
- Timber.v("displayIncomingCallNotification")
+ Timber.v("## VOIP displayIncomingCallNotification $intent")
// the incoming call in progress is already displayed
- if (!TextUtils.isEmpty(mIncomingCallId)) {
- Timber.v("displayIncomingCallNotification : the incoming call in progress is already displayed")
- } else if (!TextUtils.isEmpty(mCallIdInProgress)) {
- Timber.v("displayIncomingCallNotification : a 'call in progress' notification is displayed")
- } else
-// if (null == webRtcPeerConnectionManager.currentCall)
- {
- val callId = intent.getStringExtra(EXTRA_CALL_ID)
+// if (!TextUtils.isEmpty(mIncomingCallId)) {
+// Timber.v("displayIncomingCallNotification : the incoming call in progress is already displayed")
+// } else if (!TextUtils.isEmpty(mCallIdInProgress)) {
+// Timber.v("displayIncomingCallNotification : a 'call in progress' notification is displayed")
+// } else
+// // if (null == webRtcPeerConnectionManager.currentCall)
+// {
+ val callId = intent.getStringExtra(EXTRA_CALL_ID)
- Timber.v("displayIncomingCallNotification : display the dedicated notification")
- val notification = notificationUtils.buildIncomingCallNotification(
- intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
- intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
- intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
- callId ?: "")
- startForeground(NOTIFICATION_ID, notification)
+ Timber.v("displayIncomingCallNotification : display the dedicated notification")
+ val notification = notificationUtils.buildIncomingCallNotification(
+ intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
+ intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
+ intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
+ callId ?: "")
+ startForeground(NOTIFICATION_ID, notification)
- mIncomingCallId = callId
+// mIncomingCallId = callId
- // turn the screen on for 3 seconds
+ // turn the screen on for 3 seconds
// if (Matrix.getInstance(VectorApp.getInstance())!!.pushManager.isScreenTurnedOn) {
// try {
// val pm = getSystemService(Context.POWER_SERVICE) as PowerManager
@@ -123,16 +148,29 @@ class CallService : VectorService() {
// }
//
// }
- }
+// }
// else {
// Timber.i("displayIncomingCallNotification : do not display the incoming call notification because there is a pending call")
// }
}
+ private fun displayOutgoingRingingCallNotification(intent: Intent) {
+ val callId = intent.getStringExtra(EXTRA_CALL_ID)
+
+ Timber.v("displayOutgoingCallNotification : display the dedicated notification")
+ val notification = notificationUtils.buildOutgoingRingingCallNotification(
+ intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
+ intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
+ intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
+ callId ?: "")
+ startForeground(NOTIFICATION_ID, notification)
+ }
+
/**
* Display a call in progress notification.
*/
private fun displayCallInProgressNotification(intent: Intent) {
+ Timber.v("## VOIP displayCallInProgressNotification")
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
val notification = notificationUtils.buildPendingCallNotification(
@@ -144,7 +182,27 @@ class CallService : VectorService() {
startForeground(NOTIFICATION_ID, notification)
- mCallIdInProgress = callId
+ // mCallIdInProgress = callId
+ }
+
+ /**
+ * Display a call in progress notification.
+ */
+ private fun displayCallOnGoingInBackground(intent: Intent) {
+ Timber.v("## VOIP displayCallInProgressNotification")
+ val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
+
+ val notification = notificationUtils.buildPendingCallNotification(
+ isVideo = intent.getBooleanExtra(EXTRA_IS_VIDEO, false),
+ roomName = intent.getStringExtra(EXTRA_ROOM_NAME) ?: "",
+ roomId = intent.getStringExtra(EXTRA_ROOM_ID) ?: "",
+ matrixId = intent.getStringExtra(EXTRA_MATRIX_ID) ?: "",
+ callId = callId,
+ fromBg = true)
+
+ startForeground(NOTIFICATION_ID, notification)
+
+ // mCallIdInProgress = callId
}
/**
@@ -159,6 +217,11 @@ class CallService : VectorService() {
myStopSelf()
}
+ override fun onDestroy() {
+ super.onDestroy()
+ callRingPlayer?.stop()
+ }
+
fun addConnection(callConnection: CallConnection) {
connections[callConnection.callId] = callConnection
}
@@ -166,10 +229,14 @@ class CallService : VectorService() {
companion object {
private const val NOTIFICATION_ID = 6480
- private const val ACTION_INCOMING_CALL = "im.vector.riotx.core.services.CallService.INCOMING_CALL"
- private const val ACTION_PENDING_CALL = "im.vector.riotx.core.services.CallService.PENDING_CALL"
+ private const val ACTION_INCOMING_RINGING_CALL = "im.vector.riotx.core.services.CallService.ACTION_INCOMING_RINGING_CALL"
+ private const val ACTION_OUTGOING_RINGING_CALL = "im.vector.riotx.core.services.CallService.ACTION_OUTGOING_RINGING_CALL"
+ private const val ACTION_CALL_CONNECTING = "im.vector.riotx.core.services.CallService.ACTION_CALL_CONNECTING"
+ private const val ACTION_ONGOING_CALL = "im.vector.riotx.core.services.CallService.ACTION_ONGOING_CALL"
+ private const val ACTION_ONGOING_CALL_BG = "im.vector.riotx.core.services.CallService.ACTION_ONGOING_CALL_BG"
private const val ACTION_NO_ACTIVE_CALL = "im.vector.riotx.core.services.CallService.NO_ACTIVE_CALL"
-// private const val ACTION_ON_ACTIVE_CALL = "im.vector.riotx.core.services.CallService.ACTIVE_CALL"
+// private const val ACTION_ACTIVITY_VISIBLE = "im.vector.riotx.core.services.CallService.ACTION_ACTIVITY_VISIBLE"
+// private const val ACTION_STOP_RINGING = "im.vector.riotx.core.services.CallService.ACTION_STOP_RINGING"
private const val EXTRA_IS_VIDEO = "EXTRA_IS_VIDEO"
private const val EXTRA_ROOM_NAME = "EXTRA_ROOM_NAME"
@@ -177,15 +244,15 @@ class CallService : VectorService() {
private const val EXTRA_MATRIX_ID = "EXTRA_MATRIX_ID"
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
- fun onIncomingCall(context: Context,
- isVideo: Boolean,
- roomName: String,
- roomId: String,
- matrixId: String,
- callId: String) {
+ fun onIncomingCallRinging(context: Context,
+ isVideo: Boolean,
+ roomName: String,
+ roomId: String,
+ matrixId: String,
+ callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
- action = ACTION_INCOMING_CALL
+ action = ACTION_INCOMING_RINGING_CALL
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)
@@ -196,24 +263,43 @@ class CallService : VectorService() {
ContextCompat.startForegroundService(context, intent)
}
-// fun onActiveCall(context: Context,
-// isVideo: Boolean,
-// roomName: String,
-// roomId: String,
-// matrixId: String,
-// callId: String) {
-// val intent = Intent(context, CallService::class.java)
-// .apply {
-// action = ACTION_ON_ACTIVE_CALL
-// putExtra(EXTRA_IS_VIDEO, isVideo)
-// putExtra(EXTRA_ROOM_NAME, roomName)
-// putExtra(EXTRA_ROOM_ID, roomId)
-// putExtra(EXTRA_MATRIX_ID, matrixId)
-// putExtra(EXTRA_CALL_ID, callId)
-// }
-//
-// ContextCompat.startForegroundService(context, intent)
-// }
+ fun onOnGoingCallBackground(context: Context,
+ isVideo: Boolean,
+ roomName: String,
+ roomId: String,
+ matrixId: String,
+ callId: String) {
+ val intent = Intent(context, CallService::class.java)
+ .apply {
+ action = ACTION_ONGOING_CALL_BG
+ putExtra(EXTRA_IS_VIDEO, isVideo)
+ putExtra(EXTRA_ROOM_NAME, roomName)
+ putExtra(EXTRA_ROOM_ID, roomId)
+ putExtra(EXTRA_MATRIX_ID, matrixId)
+ putExtra(EXTRA_CALL_ID, callId)
+ }
+
+ ContextCompat.startForegroundService(context, intent)
+ }
+
+ fun onOutgoingCallRinging(context: Context,
+ isVideo: Boolean,
+ roomName: String,
+ roomId: String,
+ matrixId: String,
+ callId: String) {
+ val intent = Intent(context, CallService::class.java)
+ .apply {
+ action = ACTION_OUTGOING_RINGING_CALL
+ putExtra(EXTRA_IS_VIDEO, isVideo)
+ putExtra(EXTRA_ROOM_NAME, roomName)
+ putExtra(EXTRA_ROOM_ID, roomId)
+ putExtra(EXTRA_MATRIX_ID, matrixId)
+ putExtra(EXTRA_CALL_ID, callId)
+ }
+
+ ContextCompat.startForegroundService(context, intent)
+ }
fun onPendingCall(context: Context,
isVideo: Boolean,
@@ -223,7 +309,7 @@ class CallService : VectorService() {
callId: String) {
val intent = Intent(context, CallService::class.java)
.apply {
- action = ACTION_PENDING_CALL
+ action = ACTION_ONGOING_CALL
putExtra(EXTRA_IS_VIDEO, isVideo)
putExtra(EXTRA_ROOM_NAME, roomName)
putExtra(EXTRA_ROOM_ID, roomId)
diff --git a/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt
index 256592656b..a6aede7f48 100644
--- a/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/call/VectorCallActivity.kt
@@ -20,6 +20,7 @@ package im.vector.riotx.features.call
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
@@ -31,6 +32,7 @@ import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import butterknife.BindView
+import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.viewModel
import com.jakewharton.rxbinding3.view.clicks
@@ -46,10 +48,11 @@ import im.vector.riotx.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
import im.vector.riotx.core.utils.allGranted
import im.vector.riotx.core.utils.checkPermissions
import im.vector.riotx.features.home.AvatarRenderer
+import im.vector.riotx.features.home.room.detail.RoomDetailActivity
+import im.vector.riotx.features.home.room.detail.RoomDetailArgs
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_call.*
-import kotlinx.android.synthetic.main.fragment_attachments_preview.*
import org.webrtc.EglBase
import org.webrtc.RendererCommon
import org.webrtc.SurfaceViewRenderer
@@ -101,19 +104,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
override fun doBeforeSetContentView() {
// Set window styles for fullscreen-window size. Needs to be done before adding content.
requestWindowFeature(Window.FEATURE_NO_TITLE)
- window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
- setTurnScreenOn(true)
- setShowWhenLocked(true)
- getSystemService(KeyguardManager::class.java)?.requestDismissKeyguard(this, null)
- } else {
- @Suppress("DEPRECATION")
- window.addFlags(
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- )
- }
hideSystemUI()
setContentView(R.layout.activity_call)
@@ -179,31 +169,22 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
if (intent.hasExtra(MvRx.KEY_ARG)) {
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
} else {
+ Timber.e("## VOIP missing callArgs for VectorCall Activity")
+ CallService.onNoActiveCall(this)
finish()
}
+ Timber.v("## VOIP EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}")
+ if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) {
+ turnScreenOnAndKeyguardOff()
+ }
+
constraintLayout.clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { toggleUiSystemVisibility() }
.disposeOnDestroy()
- if (isFirstCreation()) {
- // Reduce priority of notification as the activity is on screen
- CallService.onPendingCall(
- this,
- callArgs.isVideoCall,
- callArgs.participantUserId,
- callArgs.roomId,
- "",
- callArgs.callId ?: ""
- )
- }
-
- rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
- finish()
- }
-
configureCallViews()
callViewModel.subscribe(this) {
@@ -229,8 +210,68 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
}
+ override fun onDestroy() {
+ super.onDestroy()
+ peerConnectionManager.detachRenderers()
+ turnScreenOffAndKeyguardOn()
+ }
+
+// override fun onResume() {
+// super.onResume()
+// }
+//
+// override fun onStop() {
+// super.onStop()
+// when(callViewModel.call?.state) {
+// CallState.DIALING -> {
+// CallService.onIncomingCall(
+// this,
+// callArgs.isVideoCall,
+// callArgs.participantUserId,
+// callArgs.roomId,
+// "",
+// callArgs.callId ?: ""
+// )
+// }
+// CallState.LOCAL_RINGING -> {
+// CallService.onIncomingCall(
+// this,
+// callArgs.isVideoCall,
+// callArgs.participantUserId,
+// callArgs.roomId,
+// "",
+// callArgs.callId ?: ""
+// )
+// }
+// CallState.ANSWERING,
+// CallState.CONNECTING,
+// CallState.CONNECTED -> {
+// CallService.onPendingCall(
+// this,
+// callArgs.isVideoCall,
+// callArgs.participantUserId,
+// callArgs.roomId,
+// "",
+// callArgs.callId ?: ""
+// )
+// }
+// CallState.TERMINATED ,
+// CallState.IDLE ,
+// null -> {
+//
+// }
+// }
+// }
+
private fun renderState(state: VectorCallViewState) {
Timber.v("## VOIP renderState call $state")
+ if (state.callState is Fail) {
+ // be sure to clear notification
+ CallService.onNoActiveCall(this)
+ finish()
+ return
+ }
+
callControlsView.updateForState(state)
when (state.callState.invoke()) {
CallState.IDLE,
@@ -271,6 +312,8 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
configureCallInfo(state)
callStatusText.text = null
}
+ // ensure all attached?
+ peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer, null)
}
CallState.TERMINATED -> {
finish()
@@ -290,20 +333,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
private fun configureCallViews() {
callControlsView.interactionListener = this
-// if (callArgs.isVideoCall) {
-// iv_call_speaker.isVisible = false
-// iv_call_flip_camera.isVisible = true
-// iv_call_videocam_off.isVisible = true
-// } else {
-// iv_call_speaker.isVisible = true
-// iv_call_flip_camera.isVisible = false
-// iv_call_videocam_off.isVisible = false
-// }
-//
-// iv_end_call.setOnClickListener {
-// callViewModel.handle(VectorCallViewActions.EndCall)
-// finish()
-// }
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
@@ -315,7 +344,12 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
}
- private fun start(): Boolean {
+ private fun start() {
+ rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
+ Timber.v("## VOIP rootEglBase is null")
+ finish()
+ }
+
// Init Picture in Picture renderer
pipRenderer.init(rootEglBase!!.eglBaseContext, null)
pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
@@ -330,16 +364,31 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer,
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
- return false
}
- override fun onPause() {
- peerConnectionManager.detachRenderers()
- super.onPause()
- }
+// override fun onResume() {
+// super.onResume()
+// withState(callViewModel) {
+// if(it.callState.invoke() == CallState.CONNECTED) {
+// peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer)
+// }
+// }
+// }
+// override fun onPause() {
+// peerConnectionManager.detachRenderers()
+// super.onPause()
+// }
private fun handleViewEvents(event: VectorCallViewEvents?) {
- Timber.v("handleViewEvents $event")
+ Timber.v("## VOIP handleViewEvents $event")
+ when (event) {
+ VectorCallViewEvents.DismissNoCall -> {
+ CallService.onNoActiveCall(this)
+ finish()
+ }
+ null -> {
+ }
+ }
// when (event) {
// is VectorCallViewEvents.CallAnswered -> {
// }
@@ -360,6 +409,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
fun newIntent(context: Context, mxCall: MxCallDetail): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
+ // what could be the best flags?
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra(MvRx.KEY_ARG, CallArgs(mxCall.roomId, mxCall.callId, mxCall.otherUserId, !mxCall.isOutgoing, mxCall.isVideoCall, false))
putExtra(EXTRA_MODE, OUTGOING_CREATED)
@@ -375,7 +425,8 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
accept: Boolean,
mode: String?): Intent {
return Intent(context, VectorCallActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ // what could be the best flags?
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtra(MvRx.KEY_ARG, CallArgs(roomId, callId, otherUserId, isIncomingCall, isVideoCall, accept))
putExtra(EXTRA_MODE, mode)
}
@@ -403,11 +454,48 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
}
override fun returnToChat() {
- // TODO, what if the room is not in backstack??
+ val args = RoomDetailArgs(callArgs.roomId)
+ val intent = RoomDetailActivity.newIntent(this, args).apply {
+ flags = FLAG_ACTIVITY_CLEAR_TOP
+ }
+ startActivity(intent)
+ // is it needed?
finish()
}
override fun didTapMore() {
CallControlsBottomSheet().show(supportFragmentManager, "Controls")
}
+
+ // Needed to let you answer call when phone is locked
+ private fun turnScreenOnAndKeyguardOff() {
+ Timber.v("## VOIP turnScreenOnAndKeyguardOff")
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ setShowWhenLocked(true)
+ setTurnScreenOn(true)
+ } else {
+ window.addFlags(
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ )
+ }
+
+ with(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ requestDismissKeyguard(this@VectorCallActivity, null)
+ }
+ }
+ }
+
+ private fun turnScreenOffAndKeyguardOn() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ setShowWhenLocked(false)
+ setTurnScreenOn(false)
+ } else {
+ window.clearFlags(
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+ )
+ }
+ }
}
diff --git a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt
index 47888dfe76..b8af552dc4 100644
--- a/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/call/VectorCallViewModel.kt
@@ -17,6 +17,7 @@
package im.vector.riotx.features.call
import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
@@ -58,6 +59,7 @@ sealed class VectorCallViewActions : VectorViewModelAction {
sealed class VectorCallViewEvents : VectorViewEvents {
+ object DismissNoCall : VectorCallViewEvents()
// data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents()
// data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents()
// object CallAccepted : VectorCallViewEvents()
@@ -117,6 +119,12 @@ class VectorCallViewModel @AssistedInject constructor(
soundDevice = webRtcPeerConnectionManager.audioManager.getCurrentSoundDevice()
)
}
+ } ?: run {
+ setState {
+ copy(
+ callState = Fail(IllegalArgumentException("No call"))
+ )
+ }
}
}
}
diff --git a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt
index ecbad9e269..0c7f2be691 100644
--- a/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt
+++ b/vector/src/main/java/im/vector/riotx/features/call/WebRtcPeerConnectionManager.kt
@@ -266,6 +266,23 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP attachViewRenderers localRendeder $localViewRenderer / $remoteViewRenderer")
this.localSurfaceRenderer = WeakReference(localViewRenderer)
this.remoteSurfaceRenderer = WeakReference(remoteViewRenderer)
+
+ // The call is going to resume from background, we can reduce notif
+ currentCall?.mxCall
+ ?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED }
+ ?.let { mxCall ->
+ val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
+ ?: mxCall.roomId
+ // Start background service with notification
+ CallService.onPendingCall(
+ context = context,
+ isVideo = mxCall.isVideoCall,
+ roomName = name,
+ roomId = mxCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = mxCall.callId)
+ }
+
getTurnServer { turnServer ->
val call = currentCall ?: return@getTurnServer
when (mode) {
@@ -314,6 +331,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
private fun internalAcceptIncomingCall(callContext: CallContext, turnServerResponse: TurnServerResponse?) {
+ val mxCall = callContext.mxCall
+ // Update service state
+
+ val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
+ ?: mxCall.roomId
+ CallService.onPendingCall(
+ context = context,
+ isVideo = mxCall.isVideoCall,
+ roomName = name,
+ roomId = mxCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = mxCall.callId
+ )
executor.execute {
// 1) create peer connection
createPeerConnection(callContext, turnServerResponse)
@@ -435,7 +465,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
fun acceptIncomingCall() {
Timber.v("## VOIP acceptIncomingCall from state ${currentCall?.mxCall?.state}")
- if (currentCall?.mxCall?.state == CallState.LOCAL_RINGING) {
+ val mxCall = currentCall?.mxCall
+ if (mxCall?.state == CallState.LOCAL_RINGING) {
getTurnServer { turnServer ->
internalAcceptIncomingCall(currentCall!!, turnServer)
}
@@ -443,6 +474,24 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
fun detachRenderers() {
+ // The call is going to continue in background, so ensure notification is visible
+ currentCall?.mxCall
+ ?.takeIf { it.state == CallState.CONNECTING || it.state == CallState.CONNECTED }
+ ?.let { mxCall ->
+ // Start background service with notification
+
+ val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
+ ?: mxCall.otherUserId
+ CallService.onOnGoingCallBackground(
+ context = context,
+ isVideo = mxCall.isVideoCall,
+ roomName = name,
+ roomId = mxCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = mxCall.callId
+ )
+ }
+
Timber.v("## VOIP detachRenderers")
// currentCall?.localMediaStream?.let { currentCall?.peerConnection?.removeStream(it) }
localSurfaceRenderer?.get()?.let {
@@ -496,6 +545,16 @@ class WebRtcPeerConnectionManager @Inject constructor(
audioManager.startForCall(createdCall)
currentCall = callContext
+ val name = sessionHolder.getSafeActiveSession()?.getUser(createdCall.otherUserId)?.getBestName()
+ ?: createdCall.otherUserId
+ CallService.onOutgoingCallRinging(
+ context = context,
+ isVideo = createdCall.isVideoCall,
+ roomName = name,
+ roomId = createdCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = createdCall.callId)
+
executor.execute {
callContext.remoteCandidateSource = ReplaySubject.create()
}
@@ -524,9 +583,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
// TODO What if a call is currently active?
if (currentCall != null) {
- Timber.w("## VOIP TODO: Automatically reject incoming call?")
- mxCall.hangUp()
- audioManager.stop()
+ Timber.w("## VOIP receiving incoming call while already in call?")
+ // Just ignore, maybe we could answer from other session?
return
}
@@ -537,12 +595,17 @@ class WebRtcPeerConnectionManager @Inject constructor(
callContext.remoteCandidateSource = ReplaySubject.create()
}
- CallService.onIncomingCall(context,
- mxCall.isVideoCall,
- mxCall.otherUserId,
- mxCall.roomId,
- sessionHolder.getSafeActiveSession()?.myUserId ?: "",
- mxCall.callId)
+ // Start background service with notification
+ val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
+ ?: mxCall.otherUserId
+ CallService.onIncomingCallRinging(
+ context = context,
+ isVideo = mxCall.isVideoCall,
+ roomName = name,
+ roomId = mxCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = mxCall.callId
+ )
callContext.offerSdp = callInviteContent.offer
}
@@ -575,12 +638,19 @@ class WebRtcPeerConnectionManager @Inject constructor(
}
fun endCall() {
+ // Update service state
+ CallService.onNoActiveCall(context)
+ // close tracks ASAP
+ currentCall?.localVideoTrack?.setEnabled(false)
+ currentCall?.localVideoTrack?.setEnabled(false)
+
currentCall?.cameraAvailabilityCallback?.let { cameraAvailabilityCallback ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
}
}
+
currentCall?.mxCall?.hangUp()
currentCall = null
audioManager.stop()
@@ -592,6 +662,18 @@ class WebRtcPeerConnectionManager @Inject constructor(
if (call.mxCall.callId != callAnswerContent.callId) return Unit.also {
Timber.w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
}
+ val mxCall = call.mxCall
+ // Update service state
+ val name = sessionHolder.getSafeActiveSession()?.getUser(mxCall.otherUserId)?.getBestName()
+ ?: mxCall.otherUserId
+ CallService.onPendingCall(
+ context = context,
+ isVideo = mxCall.isVideoCall,
+ roomName = name,
+ roomId = mxCall.roomId,
+ matrixId = sessionHolder.getSafeActiveSession()?.myUserId ?: "",
+ callId = mxCall.callId
+ )
executor.execute {
Timber.v("## VOIP onCallAnswerReceived ${callAnswerContent.callId}")
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
@@ -626,7 +708,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
* One or more of the ICE transports on the connection is in the "failed" state.
*/
PeerConnection.PeerConnectionState.FAILED -> {
- endCall()
+ // This can be temporary, e.g when other ice not yet received...
+ // callContext.mxCall.state = CallState.ERROR
}
/**
* At least one of the connection's ICE transports (RTCIceTransports or RTCDtlsTransports) are in the "new" state,
@@ -711,7 +794,9 @@ class WebRtcPeerConnectionManager @Inject constructor(
* It is, however, possible that the ICE agent did find compatible connections for some components.
*/
PeerConnection.IceConnectionState.FAILED -> {
- callContext.mxCall.hangUp()
+ // I should not hangup here..
+ // because new candidates could arrive
+ // callContext.mxCall.hangUp()
}
/**
* The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components.
@@ -742,7 +827,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
remoteVideoTrack.setEnabled(true)
callContext.remoteVideoTrack = remoteVideoTrack
// sink to renderer if attached
- remoteSurfaceRenderer?.get().let { remoteVideoTrack.addSink(it) }
+ remoteSurfaceRenderer?.get()?.let { remoteVideoTrack.addSink(it) }
}
}
}
diff --git a/vector/src/main/java/im/vector/riotx/features/call/service/CallHeadsUpActionReceiver.kt b/vector/src/main/java/im/vector/riotx/features/call/service/CallHeadsUpActionReceiver.kt
index 6b38a272a2..199dcd3b14 100644
--- a/vector/src/main/java/im/vector/riotx/features/call/service/CallHeadsUpActionReceiver.kt
+++ b/vector/src/main/java/im/vector/riotx/features/call/service/CallHeadsUpActionReceiver.kt
@@ -23,9 +23,15 @@ import im.vector.riotx.core.di.HasVectorInjector
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
import im.vector.riotx.features.notifications.NotificationUtils
import im.vector.riotx.features.settings.VectorLocale.context
+import timber.log.Timber
class CallHeadsUpActionReceiver : BroadcastReceiver() {
+ companion object {
+ const val EXTRA_CALL_ACTION_KEY = "EXTRA_CALL_ACTION_KEY"
+ const val CALL_ACTION_REJECT = 0
+ }
+
private lateinit var peerConnectionManager: WebRtcPeerConnectionManager
private lateinit var notificationUtils: NotificationUtils
@@ -38,10 +44,9 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
}
override fun onReceive(context: Context, intent: Intent?) {
-// when (intent?.getIntExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, 0)) {
-// CallHeadsUpService.CALL_ACTION_ANSWER -> onCallAnswerClicked(context)
-// CallHeadsUpService.CALL_ACTION_REJECT -> onCallRejectClicked()
-// }
+ when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) {
+ CALL_ACTION_REJECT -> onCallRejectClicked()
+ }
// Not sure why this should be needed
// val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
@@ -51,10 +56,10 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
// context.stopService(Intent(context, CallHeadsUpService::class.java))
}
-// private fun onCallRejectClicked() {
-// Timber.d("onCallRejectClicked")
-// peerConnectionManager.endCall()
-// }
+ private fun onCallRejectClicked() {
+ Timber.d("onCallRejectClicked")
+ peerConnectionManager.endCall()
+ }
// private fun onCallAnswerClicked(context: Context) {
// Timber.d("onCallAnswerClicked")
diff --git a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt
index 72471ccf1d..88a19fff89 100755
--- a/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/riotx/features/notifications/NotificationUtils.kt
@@ -283,31 +283,29 @@ class NotificationUtils @Inject constructor(private val context: Context,
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setLights(accentColor, 500, 500)
+ .setOngoing(true)
// Compat: Display the incoming call notification on the lock screen
builder.priority = NotificationCompat.PRIORITY_HIGH
- // clear the activity stack to home activity
- // val intent = Intent(context, HomeActivity::class.java)
- // .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
- // TODO .putExtra(VectorHomeActivity.EXTRA_CALL_SESSION_ID, matrixId)
- // TODO .putExtra(VectorHomeActivity.EXTRA_CALL_ID, callId)
-
- // Recreate the back stack
-// val stackBuilder = TaskStackBuilder.create(context)
-// .addParentStack(HomeActivity::class.java)
-// .addNextIntent(intent)
-
- // android 4.3 issue
- // use a generator for the private requestCode.
- // When using 0, the intent is not created/launched when the user taps on the notification.
//
val requestId = Random.nextInt(1000)
// val pendingIntent = stackBuilder.getPendingIntent(requestId, PendingIntent.FLAG_UPDATE_CURRENT)
- val contentPendingIntent = TaskStackBuilder.create(context)
- .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
- .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, false, VectorCallActivity.INCOMING_RINGING))
- .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
+
+ val contentIntent = VectorCallActivity.newIntent(
+ context, callId, roomId, otherUserId, true, isVideo,
+ false, VectorCallActivity.INCOMING_RINGING
+ ).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ data = Uri.parse("foobar://$callId")
+ }
+ val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
+
+// val contentPendingIntent = TaskStackBuilder.create(context)
+// .addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
+// .addNextIntent(RoomDetailActivity.newIntent(context, RoomDetailArgs(roomId = roomId))
+// .addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, otherUserId, true, isVideo, false, VectorCallActivity.INCOMING_RINGING))
+// .getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
val answerCallPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
@@ -321,7 +319,12 @@ class NotificationUtils @Inject constructor(private val context: Context,
// putExtra(CallHeadsUpService.EXTRA_CALL_ACTION_KEY, CallHeadsUpService.CALL_ACTION_REJECT)
}
// val answerCallPendingIntent = PendingIntent.getBroadcast(context, requestId, answerCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
- val rejectCallPendingIntent = PendingIntent.getBroadcast(context, requestId + 1, rejectCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
+ val rejectCallPendingIntent = PendingIntent.getBroadcast(
+ context,
+ requestId + 1,
+ rejectCallActionReceiver,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
builder.addAction(
NotificationCompat.Action(
@@ -340,6 +343,54 @@ class NotificationUtils @Inject constructor(private val context: Context,
rejectCallPendingIntent)
)
+ builder.setFullScreenIntent(contentPendingIntent, true)
+
+ return builder.build()
+ }
+
+ fun buildOutgoingRingingCallNotification(isVideo: Boolean,
+ otherUserId: String,
+ roomId: String,
+ callId: String): Notification {
+ val accentColor = ContextCompat.getColor(context, R.color.notification_accent_color)
+
+ val builder = NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
+ .setContentTitle(ensureTitleNotEmpty(otherUserId))
+ .apply {
+ setContentText(stringProvider.getString(R.string.call_ring))
+ }
+ .setSmallIcon(R.drawable.incoming_call_notification_transparent)
+ .setCategory(NotificationCompat.CATEGORY_CALL)
+ .setLights(accentColor, 500, 500)
+ .setOngoing(true)
+
+ val requestId = Random.nextInt(1000)
+
+ val contentIntent = VectorCallActivity.newIntent(
+ context, callId, roomId, otherUserId, true, isVideo,
+ false, null).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ data = Uri.parse("foobar://$callId")
+ }
+ val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
+
+ val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
+ putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
+ }
+
+ val rejectCallPendingIntent = PendingIntent.getBroadcast(
+ context,
+ requestId + 1,
+ rejectCallActionReceiver,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
+ builder.addAction(
+ NotificationCompat.Action(
+ IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+ context.getString(R.string.call_notification_hangup),
+ rejectCallPendingIntent)
+ )
builder.setContentIntent(contentPendingIntent)
return builder.build()
@@ -360,8 +411,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
roomName: String,
roomId: String,
matrixId: String,
- callId: String): Notification {
- val builder = NotificationCompat.Builder(context, CALL_NOTIFICATION_CHANNEL_ID)
+ callId: String, fromBg: Boolean = false): Notification {
+ val builder = NotificationCompat.Builder(context, if (fromBg) CALL_NOTIFICATION_CHANNEL_ID else SILENT_NOTIFICATION_CHANNEL_ID)
.setContentTitle(ensureTitleNotEmpty(roomName))
.apply {
if (isVideo) {
@@ -373,7 +424,29 @@ class NotificationUtils @Inject constructor(private val context: Context,
.setSmallIcon(R.drawable.incoming_call_notification_transparent)
.setCategory(NotificationCompat.CATEGORY_CALL)
- builder.priority = NotificationCompat.PRIORITY_DEFAULT
+ if (fromBg) {
+ builder.priority = NotificationCompat.PRIORITY_LOW
+ builder.setOngoing(true)
+ }
+
+ val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
+ data = Uri.parse("mxcall://end?$callId")
+ putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
+ }
+
+ val rejectCallPendingIntent = PendingIntent.getBroadcast(
+ context,
+ System.currentTimeMillis().toInt(),
+ rejectCallActionReceiver,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ )
+
+ builder.addAction(
+ NotificationCompat.Action(
+ IconCompat.createWithResource(context, R.drawable.ic_call_end).setTint(ContextCompat.getColor(context, R.color.riotx_notice)),
+ context.getString(R.string.call_notification_hangup),
+ rejectCallPendingIntent)
+ )
val contentPendingIntent = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(Intent(context, HomeActivity::class.java))
@@ -381,32 +454,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
.addNextIntent(VectorCallActivity.newIntent(context, callId, roomId, "otherUserId", true, isVideo, false, null))
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
- // android 4.3 issue
- // use a generator for the private requestCode.
- // When using 0, the intent is not created/launched when the user taps on the notification.
builder.setContentIntent(contentPendingIntent)
- /* TODO
- // Build the pending intent for when the notification is clicked
- val roomIntent = Intent(context, VectorRoomActivity::class.java)
- .putExtra(VectorRoomActivity.EXTRA_ROOM_ID, roomId)
- .putExtra(VectorRoomActivity.EXTRA_MATRIX_ID, matrixId)
- .putExtra(VectorRoomActivity.EXTRA_START_CALL_ID, callId)
-
- // Recreate the back stack
- val stackBuilder = TaskStackBuilder.create(context)
- .addParentStack(VectorRoomActivity::class.java)
- .addNextIntent(roomIntent)
-
- // android 4.3 issue
- // use a generator for the private requestCode.
- // When using 0, the intent is not created/launched when the user taps on the notification.
- //
- val pendingIntent = stackBuilder.getPendingIntent(Random().nextInt(1000), PendingIntent.FLAG_UPDATE_CURRENT)
-
- builder.setContentIntent(pendingIntent)
- */
-
return builder.build()
}