mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
Ask for permission before starting call
This commit is contained in:
parent
b27eead016
commit
25fe56116c
4 changed files with 63 additions and 17 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue