Ask for permission before starting call

This commit is contained in:
Valere 2020-06-18 23:37:54 +02:00
parent b27eead016
commit 25fe56116c
4 changed files with 63 additions and 17 deletions

View file

@ -567,7 +567,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
} }
fun startOutgoingCall(context: Context, signalingRoomId: String, otherUserId: String, isVideoCall: Boolean) { fun startOutgoingCall(signalingRoomId: String, otherUserId: String, isVideoCall: Boolean) {
executor.execute { executor.execute {
if (peerConnectionFactory == null) { if (peerConnectionFactory == null) {
createPeerConnectionFactory() createPeerConnectionFactory()
@ -584,7 +584,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
val name = sessionHolder.getSafeActiveSession()?.getUser(createdCall.otherUserId)?.getBestName() val name = sessionHolder.getSafeActiveSession()?.getUser(createdCall.otherUserId)?.getBestName()
?: createdCall.otherUserId ?: createdCall.otherUserId
CallService.onOutgoingCallRinging( CallService.onOutgoingCallRinging(
context = context, context = context.applicationContext,
isVideo = createdCall.isVideoCall, isVideo = createdCall.isVideoCall,
roomName = name, roomName = name,
roomId = createdCall.roomId, roomId = createdCall.roomId,
@ -596,7 +596,7 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
// start the activity now // start the activity now
context.startActivity(VectorCallActivity.newIntent(context, createdCall)) context.applicationContext.startActivity(VectorCallActivity.newIntent(context, createdCall))
} }
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) { override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {

View file

@ -68,7 +68,7 @@ sealed class RoomDetailAction : VectorViewModelAction {
object ClearSendQueue : RoomDetailAction() object ClearSendQueue : RoomDetailAction()
object ResendAll : RoomDetailAction() object ResendAll : RoomDetailAction()
object StartCall : RoomDetailAction() data class StartCall(val isVideo: Boolean) : RoomDetailAction()
data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction() data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction()
data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction() data class DeclineVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction()

View file

@ -107,6 +107,8 @@ import im.vector.riotx.core.ui.views.JumpToReadMarkerView
import im.vector.riotx.core.ui.views.NotificationAreaView import im.vector.riotx.core.ui.views.NotificationAreaView
import im.vector.riotx.core.utils.Debouncer import im.vector.riotx.core.utils.Debouncer
import im.vector.riotx.core.utils.KeyboardStateUtils import im.vector.riotx.core.utils.KeyboardStateUtils
import im.vector.riotx.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
import im.vector.riotx.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES import im.vector.riotx.core.utils.PERMISSIONS_FOR_WRITING_FILES
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_DOWNLOAD_FILE import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_DOWNLOAD_FILE
import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_INCOMING_URI import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_INCOMING_URI
@ -120,6 +122,8 @@ import im.vector.riotx.core.utils.createJSonViewerStyleProvider
import im.vector.riotx.core.utils.createUIHandler import im.vector.riotx.core.utils.createUIHandler
import im.vector.riotx.core.utils.getColorFromUserId import im.vector.riotx.core.utils.getColorFromUserId
import im.vector.riotx.core.utils.isValidUrl import im.vector.riotx.core.utils.isValidUrl
import im.vector.riotx.core.utils.onPermissionResultAudioIpCall
import im.vector.riotx.core.utils.onPermissionResultVideoIpCall
import im.vector.riotx.core.utils.openUrlInExternalBrowser import im.vector.riotx.core.utils.openUrlInExternalBrowser
import im.vector.riotx.core.utils.saveMedia import im.vector.riotx.core.utils.saveMedia
import im.vector.riotx.core.utils.shareMedia import im.vector.riotx.core.utils.shareMedia
@ -132,7 +136,6 @@ import im.vector.riotx.features.attachments.preview.AttachmentsPreviewArgs
import im.vector.riotx.features.attachments.toGroupedContentAttachmentData import im.vector.riotx.features.attachments.toGroupedContentAttachmentData
import im.vector.riotx.features.call.SharedActiveCallViewModel import im.vector.riotx.features.call.SharedActiveCallViewModel
import im.vector.riotx.features.call.VectorCallActivity import im.vector.riotx.features.call.VectorCallActivity
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
import im.vector.riotx.features.command.Command import im.vector.riotx.features.command.Command
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.riotx.features.crypto.util.toImageRes import im.vector.riotx.features.crypto.util.toImageRes
@ -202,8 +205,7 @@ class RoomDetailFragment @Inject constructor(
val roomDetailViewModelFactory: RoomDetailViewModel.Factory, val roomDetailViewModelFactory: RoomDetailViewModel.Factory,
private val eventHtmlRenderer: EventHtmlRenderer, private val eventHtmlRenderer: EventHtmlRenderer,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider) :
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager) :
VectorBaseFragment(), VectorBaseFragment(),
TimelineEventController.Callback, TimelineEventController.Callback,
VectorInviteView.Callback, VectorInviteView.Callback,
@ -215,6 +217,9 @@ class RoomDetailFragment @Inject constructor(
companion object { companion object {
private const val AUDIO_CALL_PERMISSION_REQUEST_CODE = 1
private const val VIDEO_CALL_PERMISSION_REQUEST_CODE = 2
/** /**
* Sanitize the display name. * Sanitize the display name.
* *
@ -503,22 +508,22 @@ class RoomDetailFragment @Inject constructor(
} }
R.id.voice_call, R.id.voice_call,
R.id.video_call -> { R.id.video_call -> {
roomDetailViewModel.getOtherUserIds()?.firstOrNull()?.let {
// TODO CALL We should check/ask for permission here first // TODO CALL We should check/ask for permission here first
val activeCall = sharedCallActionViewModel.activeCall.value val activeCall = sharedCallActionViewModel.activeCall.value
val isVideoCall = item.itemId == R.id.video_call
if (activeCall != null) { if (activeCall != null) {
// resume existing if same room, if not prompt to kill and then restart new call? // resume existing if same room, if not prompt to kill and then restart new call?
if (activeCall.roomId == roomDetailArgs.roomId) { if (activeCall.roomId == roomDetailArgs.roomId) {
onTapToReturnToCall() onTapToReturnToCall()
} else {
// TODO might not work well, and should prompt
webRtcPeerConnectionManager.endCall()
webRtcPeerConnectionManager.startOutgoingCall(requireContext(), roomDetailArgs.roomId, it, item.itemId == R.id.video_call)
} }
// else {
// TODO might not work well, and should prompt
// webRtcPeerConnectionManager.endCall()
// safeStartCall(it, isVideoCall)
// }
} else { } else {
webRtcPeerConnectionManager.startOutgoingCall(requireContext(), roomDetailArgs.roomId, it, item.itemId == R.id.video_call) safeStartCall(isVideoCall)
} }
}
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
@ -536,6 +541,22 @@ class RoomDetailFragment @Inject constructor(
.show() .show()
} }
private fun safeStartCall(isVideoCall: Boolean) {
val startCallAction = RoomDetailAction.StartCall(isVideoCall)
roomDetailViewModel.pendingAction = startCallAction
if (isVideoCall) {
if (checkPermissions(PERMISSIONS_FOR_VIDEO_IP_CALL, this, VIDEO_CALL_PERMISSION_REQUEST_CODE, R.string.permissions_rationale_msg_camera_and_audio)) {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(startCallAction)
}
} else {
if (checkPermissions(PERMISSIONS_FOR_AUDIO_IP_CALL, this, AUDIO_CALL_PERMISSION_REQUEST_CODE, R.string.permissions_rationale_msg_record_audio)) {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(startCallAction)
}
}
}
private fun renderRegularMode(text: String) { private fun renderRegularMode(text: String) {
autoCompleter.exitSpecialMode() autoCompleter.exitSpecialMode()
composerLayout.collapse() composerLayout.collapse()
@ -1130,6 +1151,22 @@ class RoomDetailFragment @Inject constructor(
launchAttachmentProcess(pendingType) launchAttachmentProcess(pendingType)
} }
} }
AUDIO_CALL_PERMISSION_REQUEST_CODE -> {
if (onPermissionResultAudioIpCall(requireContext(), grantResults)) {
(roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(it)
}
}
}
VIDEO_CALL_PERMISSION_REQUEST_CODE -> {
if (onPermissionResultVideoIpCall(requireContext(), grantResults)) {
(roomDetailViewModel.pendingAction as? RoomDetailAction.StartCall)?.let {
roomDetailViewModel.pendingAction = null
roomDetailViewModel.handle(it)
}
}
}
} }
} else { } else {
// Reset all pending data // Reset all pending data

View file

@ -67,6 +67,7 @@ import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.resources.UserPreferencesProvider import im.vector.riotx.core.resources.UserPreferencesProvider
import im.vector.riotx.core.utils.subscribeLogError import im.vector.riotx.core.utils.subscribeLogError
import im.vector.riotx.features.call.WebRtcPeerConnectionManager
import im.vector.riotx.features.command.CommandParser import im.vector.riotx.features.command.CommandParser
import im.vector.riotx.features.command.ParsedCommand import im.vector.riotx.features.command.ParsedCommand
import im.vector.riotx.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.riotx.features.crypto.verification.SupportedVerificationMethodsProvider
@ -97,7 +98,8 @@ class RoomDetailViewModel @AssistedInject constructor(
private val rainbowGenerator: RainbowGenerator, private val rainbowGenerator: RainbowGenerator,
private val session: Session, private val session: Session,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider,
private val stickerPickerActionHandler: StickerPickerActionHandler private val stickerPickerActionHandler: StickerPickerActionHandler,
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager
) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener { ) : VectorViewModel<RoomDetailViewState, RoomDetailAction, RoomDetailViewEvents>(initialState), Timeline.Listener {
private val room = session.getRoom(initialState.roomId)!! private val room = session.getRoom(initialState.roomId)!!
@ -255,13 +257,20 @@ class RoomDetailViewModel @AssistedInject constructor(
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
} is RoomDetailAction.StartCall -> handleStartCall(action)
}.exhaustive
} }
private fun handleSendSticker(action: RoomDetailAction.SendSticker) { private fun handleSendSticker(action: RoomDetailAction.SendSticker) {
room.sendEvent(EventType.STICKER, action.stickerContent.toContent()) room.sendEvent(EventType.STICKER, action.stickerContent.toContent())
} }
private fun handleStartCall(action: RoomDetailAction.StartCall) {
room.roomSummary()?.otherMemberIds?.firstOrNull()?.let {
webRtcPeerConnectionManager.startOutgoingCall(room.roomId, it, action.isVideo)
}
}
private fun handleSelectStickerAttachment() { private fun handleSelectStickerAttachment() {
viewModelScope.launch { viewModelScope.launch {
val viewEvent = stickerPickerActionHandler.handle() val viewEvent = stickerPickerActionHandler.handle()
@ -369,7 +378,7 @@ class RoomDetailViewModel @AssistedInject constructor(
} }
fun isMenuItemVisible(@IdRes itemId: Int) = when (itemId) { fun isMenuItemVisible(@IdRes itemId: Int) = when (itemId) {
R.id.clear_message_queue -> R.id.clear_message_queue ->
// For now always disable when not in developer mode, worker cancellation is not working properly // For now always disable when not in developer mode, worker cancellation is not working properly
timeline.pendingEventCount() > 0 && vectorPreferences.developerMode() timeline.pendingEventCount() > 0 && vectorPreferences.developerMode()
R.id.resend_all -> timeline.failedToDeliverEventCount() > 0 R.id.resend_all -> timeline.failedToDeliverEventCount() > 0