From c90717a2c8acecddcd8e8921287a6f44a2c67e29 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 May 2021 18:17:14 +0200 Subject: [PATCH 01/14] Asserted identity: introduce new content --- .../sdk/api/session/events/model/EventType.kt | 2 + .../model/call/CallAssertedIdentityContent.kt | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAssertedIdentityContent.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 229a53fa9d..9c3fdd57da 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -76,6 +76,8 @@ object EventType { const val CALL_NEGOTIATE = "m.call.negotiate" const val CALL_REJECT = "m.call.reject" const val CALL_HANGUP = "m.call.hangup" + const val CALL_ASSERTED_IDENTITY = "m.call.asserted_identity" + const val CALL_ASSERTED_IDENTITY_PREFIX = "org.matrix.call.asserted_identity" // This type is not processed by the client, just sent to the server const val CALL_REPLACES = "m.call.replaces" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAssertedIdentityContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAssertedIdentityContent.kt new file mode 100644 index 0000000000..4c5413425f --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAssertedIdentityContent.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2021 The Matrix.org Foundation C.I.C. + * + * 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 org.matrix.android.sdk.api.session.room.model.call + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * This event is sent by the callee when they wish to answer the call. + */ +@JsonClass(generateAdapter = true) +data class CallAssertedIdentityContent( + /** + * Required. The ID of the call this event relates to. + */ + @Json(name = "call_id") override val callId: String, + /** + * Required. ID to let user identify remote echo of their own events + */ + @Json(name = "party_id") override val partyId: String? = null, + /** + * Required. The version of the VoIP specification this messages adheres to. + */ + @Json(name = "version") override val version: String?, + + /** + * Optional. Used to inform the transferee who they're now speaking to. + */ + @Json(name = "asserted_identity") val assertedIdentity: AssertedIdentity? = null +) : CallSignalingContent { + + /** + * A user ID may be included if relevant, but unlike target_user, it is purely informational. + * The asserted identity may not represent a matrix user at all, + * in which case just a display_name may be given, or a perhaps a display_name and avatar_url. + */ + @JsonClass(generateAdapter = true) + data class AssertedIdentity( + @Json(name = "id") val id: String? = null, + @Json(name = "display_name") val displayName: String? = null, + @Json(name = "avatar_url") val avatarUrl: String? = null + ) +} From 0098d435b39d8d4e2109c3318fe1f542af8fb9d5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 28 May 2021 18:42:29 +0200 Subject: [PATCH 02/14] Asserted identity: process event --- .../sdk/api/session/call/CallListener.kt | 8 +++++ .../session/call/CallEventProcessor.kt | 4 ++- .../session/call/CallListenersDispatcher.kt | 5 ++++ .../session/call/CallSignalingHandler.kt | 29 ++++++++++++++----- .../app/features/call/webrtc/WebRtcCall.kt | 12 ++++++++ .../features/call/webrtc/WebRtcCallManager.kt | 9 ++++++ 6 files changed, 59 insertions(+), 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt index 303add747f..4704a657df 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.session.call import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallAssertedIdentityContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -61,4 +62,11 @@ interface CallListener { * Called when the call has been managed by an other session */ fun onCallManagedByOtherSession(callId: String) + + /** + * Called when an asserted identity event is received + */ + fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) + + } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt index a190ff62ac..8bf2014639 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt @@ -37,7 +37,9 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH EventType.CALL_CANDIDATES, EventType.CALL_INVITE, EventType.CALL_HANGUP, - EventType.ENCRYPTED + EventType.ENCRYPTED, + EventType.CALL_ASSERTED_IDENTITY, + EventType.CALL_ASSERTED_IDENTITY_PREFIX ) private val eventsToPostProcess = mutableListOf() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt index 1de2d8a106..dad17f4ac9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallListenersDispatcher.kt @@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallAssertedIdentityContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -64,6 +65,10 @@ internal class CallListenersDispatcher(private val listeners: Set) it.onCallNegotiateReceived(callNegotiateContent) } + override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) = dispatch { + it.onCallAssertedIdentityReceived(callAssertedIdentityContent) + } + private fun dispatch(lambda: (CallListener) -> Unit) { listeners.toList().forEach { tryOrNull { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt index 61ea660b60..d18737d31b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallSignalingHandler.kt @@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallAssertedIdentityContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -53,30 +54,44 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa fun onCallEvent(event: Event) { when (event.getClearType()) { - EventType.CALL_ANSWER -> { + EventType.CALL_ANSWER -> { handleCallAnswerEvent(event) } - EventType.CALL_INVITE -> { + EventType.CALL_INVITE -> { handleCallInviteEvent(event) } - EventType.CALL_HANGUP -> { + EventType.CALL_HANGUP -> { handleCallHangupEvent(event) } - EventType.CALL_REJECT -> { + EventType.CALL_REJECT -> { handleCallRejectEvent(event) } - EventType.CALL_CANDIDATES -> { + EventType.CALL_CANDIDATES -> { handleCallCandidatesEvent(event) } - EventType.CALL_SELECT_ANSWER -> { + EventType.CALL_SELECT_ANSWER -> { handleCallSelectAnswerEvent(event) } - EventType.CALL_NEGOTIATE -> { + EventType.CALL_NEGOTIATE -> { handleCallNegotiateEvent(event) } + EventType.CALL_ASSERTED_IDENTITY, + EventType.CALL_ASSERTED_IDENTITY_PREFIX -> { + handleCallAssertedIdentityEvent(event) + } } } + private fun handleCallAssertedIdentityEvent(event: Event) { + val content = event.getClearContent().toModel() ?: return + val call = content.getCall() ?: return + if (call.ourPartyId == content.partyId) { + // Ignore remote echo (not that we send asserted identity, but still...) + return + } + callListenersDispatcher.onCallAssertedIdentityReceived(content) + } + private fun handleCallNegotiateEvent(event: Event) { val content = event.getClearContent().toModel() ?: return val call = content.getCall() ?: return diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index f2a008feb7..319fef87f9 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxPeerConnectionState import org.matrix.android.sdk.api.session.call.TurnServerResponse import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallAssertedIdentityContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -104,6 +105,7 @@ class WebRtcCall( fun onCaptureStateChanged() {} fun onCameraChanged() {} fun onHoldUnhold() {} + fun assertedIdentityChanged() {} fun onTick(formattedDuration: String) {} override fun onStateUpdate(call: MxCall) {} } @@ -168,6 +170,8 @@ class WebRtcCall( // This value is used to track localOnHold when changing remoteOnHold value private var wasLocalOnHold = false + var remoteAssertedIdentity: CallAssertedIdentityContent.AssertedIdentity? = null + private set var offerSdp: CallInviteContent.Offer? = null @@ -877,6 +881,14 @@ class WebRtcCall( } } + fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { + if (callAssertedIdentityContent.assertedIdentity == null) return + remoteAssertedIdentity = callAssertedIdentityContent.assertedIdentity + listeners.forEach { + tryOrNull { it.assertedIdentityChanged() } + } + } + // MxCall.StateListener override fun onStateUpdate(call: MxCall) { diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index 3c18d97937..a333d5e81b 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.session.call.CallListener import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.room.model.call.CallAnswerContent +import org.matrix.android.sdk.api.session.room.model.call.CallAssertedIdentityContent import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent @@ -420,4 +421,12 @@ class WebRtcCallManager @Inject constructor( Timber.v("## VOIP onCallManagedByOtherSession: $callId") onCallEnded(callId) } + + override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { + val call = callsByCallId[callAssertedIdentityContent.callId] + ?: return Unit.also { + Timber.w("onCallAssertedIdentityReceived for non active call? ${callAssertedIdentityContent.callId}") + } + call.onCallAssertedIdentityReceived(callAssertedIdentityContent) + } } From 0eca809b83fc3bf9def1a48bcdae7f4a65cc28b6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 1 Jun 2021 18:12:10 +0200 Subject: [PATCH 03/14] Asserted identity: handle if needed and use the data --- vector/build.gradle | 4 +++ .../app/features/call/VectorCallActivity.kt | 8 ++--- .../app/features/call/VectorCallViewModel.kt | 30 ++++++++++++++--- .../app/features/call/VectorCallViewState.kt | 5 +-- .../app/features/call/webrtc/WebRtcCall.kt | 33 ++++++++++++++++--- .../features/call/webrtc/WebRtcCallManager.kt | 4 +++ 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 8af6e80479..c8399b2fb9 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -144,6 +144,10 @@ android { buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping" + // If set, MSC3086 asserted identity messages sent on VoIP calls will cause the call to appear in the room corresponding to the asserted identity. + // This *must* only be set in trusted environments. + buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // Keep abiFilter for the universalApk diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index ad04e33414..a18f5bb918 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -221,7 +221,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callStatusText.setText(R.string.call_held_by_you) } else { views.callActionText.isInvisible = true - state.callInfo.otherUserItem?.let { + state.callInfo?.opponentUserItem?.let { views.callStatusText.text = getString(R.string.call_held_by_user, it.getBestName()) } } @@ -255,7 +255,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } private fun configureCallInfo(state: VectorCallViewState, blurAvatar: Boolean = false) { - state.callInfo.otherUserItem?.let { + state.callInfo?.opponentUserItem?.let { val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen) avatarRenderer.renderBlur(it, views.bgCallView, sampling = 20, rounded = false, colorFilter = colorFilter) if (state.transferee is VectorCallViewState.TransfereeState.NoTransferee) { @@ -269,13 +269,13 @@ class VectorCallActivity : VectorBaseActivity(), CallContro avatarRenderer.render(it, views.otherMemberAvatar) } } - if (state.otherKnownCallInfo?.otherUserItem == null) { + if (state.otherKnownCallInfo?.opponentUserItem == null) { views.otherKnownCallLayout.isVisible = false } else { val otherCall = callManager.getCallById(state.otherKnownCallInfo.callId) val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen) avatarRenderer.renderBlur( - matrixItem = state.otherKnownCallInfo.otherUserItem, + matrixItem = state.otherKnownCallInfo.opponentUserItem, imageView = views.otherKnownCallAvatarView, sampling = 20, rounded = false, diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 18eda0fd6f..68905b6c49 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -34,11 +34,13 @@ import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.MxCall import org.matrix.android.sdk.api.session.call.MxPeerConnectionState import org.matrix.android.sdk.api.session.room.model.call.supportCallTransfer +import org.matrix.android.sdk.api.util.MatrixItem class VectorCallViewModel @AssistedInject constructor( @Assisted initialState: VectorCallViewState, @@ -87,6 +89,12 @@ class VectorCallViewModel @AssistedInject constructor( } } + override fun assertedIdentityChanged(){ + setState { + copy(callInfo = call?.extractCallInfo()) + } + } + override fun onStateUpdate(call: MxCall) { val callState = call.state if (callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { @@ -160,8 +168,7 @@ class VectorCallViewModel @AssistedInject constructor( if (otherCall == null) { copy(otherKnownCallInfo = null) } else { - val otherUserItem = otherCall.getOpponentAsMatrixItem(session) - copy(otherKnownCallInfo = VectorCallViewState.CallInfo(otherCall.callId, otherUserItem)) + copy(otherKnownCallInfo = otherCall.extractCallInfo()) } } } @@ -175,7 +182,6 @@ class VectorCallViewModel @AssistedInject constructor( } else { call = webRtcCall callManager.addCurrentCallListener(currentCallListener) - val item = webRtcCall.getOpponentAsMatrixItem(session) webRtcCall.addListener(callListener) val currentSoundDevice = callManager.audioManager.selectedDevice if (currentSoundDevice == CallAudioManager.Device.PHONE) { @@ -185,7 +191,7 @@ class VectorCallViewModel @AssistedInject constructor( copy( isVideoCall = webRtcCall.mxCall.isVideoCall, callState = Success(webRtcCall.mxCall.state), - callInfo = VectorCallViewState.CallInfo(callId, item), + callInfo = webRtcCall.extractCallInfo(), device = currentSoundDevice ?: CallAudioManager.Device.PHONE, isLocalOnHold = webRtcCall.isLocalOnHold, isRemoteOnHold = webRtcCall.remoteOnHold, @@ -202,6 +208,22 @@ class VectorCallViewModel @AssistedInject constructor( } } + private fun WebRtcCall.extractCallInfo(): VectorCallViewState.CallInfo{ + val assertedIdentity = this.remoteAssertedIdentity + val matrixItem = if(assertedIdentity != null){ + val userId = if (MatrixPatterns.isUserId(assertedIdentity.id)) { + assertedIdentity.id!! + } else { + // Need an id starting with @ + "@${assertedIdentity.displayName}" + } + MatrixItem.UserItem(userId,assertedIdentity.displayName, assertedIdentity.avatarUrl) + }else { + getOpponentAsMatrixItem(session) + } + return VectorCallViewState.CallInfo(callId, matrixItem) + } + override fun onCleared() { callManager.removeCurrentCallListener(currentCallListener) call?.removeListener(callListener) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt index c5ae61cf60..b6c226564e 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.call.audio.CallAudioManager +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.util.MatrixItem @@ -39,7 +40,7 @@ data class VectorCallViewState( val availableDevices: Set = emptySet(), val callState: Async = Uninitialized, val otherKnownCallInfo: CallInfo? = null, - val callInfo: CallInfo = CallInfo(callId), + val callInfo: CallInfo? = null, val formattedDuration: String = "", val canOpponentBeTransferred: Boolean = false, val transferee: TransfereeState = TransfereeState.NoTransferee @@ -53,7 +54,7 @@ data class VectorCallViewState( data class CallInfo( val callId: String, - val otherUserItem: MatrixItem? = null + val opponentUserItem: MatrixItem? = null, ) constructor(callArgs: CallArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index 319fef87f9..4212a4e789 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -27,6 +27,7 @@ import im.vector.app.features.call.CameraProxy import im.vector.app.features.call.CameraType import im.vector.app.features.call.CaptureFormat import im.vector.app.features.call.VectorCallActivity +import im.vector.app.features.call.lookup.sipNativeLookup import im.vector.app.features.call.utils.asWebRTC import im.vector.app.features.call.utils.awaitCreateAnswer import im.vector.app.features.call.utils.awaitCreateOffer @@ -882,10 +883,34 @@ class WebRtcCall( } fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { - if (callAssertedIdentityContent.assertedIdentity == null) return - remoteAssertedIdentity = callAssertedIdentityContent.assertedIdentity - listeners.forEach { - tryOrNull { it.assertedIdentityChanged() } + sessionScope?.launch(dispatcher) { + val session = sessionProvider.get() ?: return@launch + val newAssertedIdentity = callAssertedIdentityContent.assertedIdentity ?: return@launch + if (newAssertedIdentity.id == null && newAssertedIdentity.displayName == null) { + Timber.v("Asserted identity received with no relevant information, skip") + return@launch + } + remoteAssertedIdentity = newAssertedIdentity + if (newAssertedIdentity.id != null) { + val nativeUserId = session.sipNativeLookup(newAssertedIdentity.id!!).firstOrNull()?.userId + if (nativeUserId != null) { + val resolvedUser = tryOrNull { + session.resolveUser(nativeUserId) + } + if (resolvedUser != null) { + remoteAssertedIdentity = newAssertedIdentity.copy( + id = nativeUserId, + avatarUrl = resolvedUser.avatarUrl, + displayName = resolvedUser.displayName + ) + } else { + remoteAssertedIdentity = newAssertedIdentity.copy(id = nativeUserId) + } + } + } + listeners.forEach { + tryOrNull { it.assertedIdentityChanged() } + } } } diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index a333d5e81b..d56f1aa015 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -21,6 +21,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import im.vector.app.ActiveSessionDataSource +import im.vector.app.BuildConfig import im.vector.app.core.services.CallService import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.audio.CallAudioManager @@ -423,6 +424,9 @@ class WebRtcCallManager @Inject constructor( } override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { + if(!BuildConfig.handleCallAssertedIdentityEvents){ + return + } val call = callsByCallId[callAssertedIdentityContent.callId] ?: return Unit.also { Timber.w("onCallAssertedIdentityReceived for non active call? ${callAssertedIdentityContent.callId}") From 74497dadd2d4dc599ad9b5f06819745cdd379cd5 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Jun 2021 17:06:10 +0200 Subject: [PATCH 04/14] Dial pad tab: first step, remove current way --- .../features/call/DialerChoiceBottomSheet.kt | 46 --------------- .../home/room/detail/RoomDetailAction.kt | 1 - .../home/room/detail/RoomDetailFragment.kt | 2 +- .../home/room/detail/RoomDetailViewModel.kt | 23 -------- .../home/room/detail/RoomDetailViewState.kt | 1 - .../room/detail/StartCallActionsHandler.kt | 56 ++----------------- 6 files changed, 6 insertions(+), 123 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/features/call/DialerChoiceBottomSheet.kt diff --git a/vector/src/main/java/im/vector/app/features/call/DialerChoiceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/call/DialerChoiceBottomSheet.kt deleted file mode 100644 index 401b3e23d7..0000000000 --- a/vector/src/main/java/im/vector/app/features/call/DialerChoiceBottomSheet.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021 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.app.features.call - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment -import im.vector.app.databinding.BottomSheetCallDialerChoiceBinding - -class DialerChoiceBottomSheet : VectorBaseBottomSheetDialogFragment() { - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetCallDialerChoiceBinding { - return BottomSheetCallDialerChoiceBinding.inflate(inflater, container, false) - } - - var onDialPadClicked: (() -> Unit)? = null - var onVoiceCallClicked: (() -> Unit)? = null - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - views.dialerChoiceDialPad.views.bottomSheetActionClickableZone.debouncedClicks { - onDialPadClicked?.invoke() - dismiss() - } - - views.dialerChoiceVoiceCall.views.bottomSheetActionClickableZone.debouncedClicks { - onVoiceCallClicked?.invoke() - dismiss() - } - } -} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 72e614c18c..c0e73823e4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -73,7 +73,6 @@ sealed class RoomDetailAction : VectorViewModelAction { object ResendAll : RoomDetailAction() - data class StartCallWithPhoneNumber(val phoneNumber: String, val videoCall: Boolean): RoomDetailAction() data class StartCall(val isVideo: Boolean) : RoomDetailAction() data class AcceptCall(val callId: String): RoomDetailAction() object EndCall : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 7fa36a39d7..b42ec38a3a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -320,7 +320,7 @@ class RoomDetailFragment @Inject constructor( startCallActivityResultLauncher = startCallActivityResultLauncher, showDialogWithMessage = ::showDialogWithMessage, onTapToReturnToCall = ::onTapToReturnToCall - ).register() + ) keyboardStateUtils = KeyboardStateUtils(requireActivity()) setupToolbar(views.roomToolbar) setupRecyclerView() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index a2041c0a80..2be96e07e9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -176,7 +176,6 @@ class RoomDetailViewModel @AssistedInject constructor( observeMyRoomMember() observeActiveRoomWidgets() observePowerLevel() - updateShowDialerOptionState() room.getRoomSummaryLive() viewModelScope.launch(Dispatchers.IO) { tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) } @@ -301,7 +300,6 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action) is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager() - is RoomDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action) is RoomDetailAction.StartCall -> handleStartCall(action) is RoomDetailAction.AcceptCall -> handleAcceptCall(action) is RoomDetailAction.EndCall -> handleEndCall() @@ -327,17 +325,6 @@ class RoomDetailViewModel @AssistedInject constructor( }.exhaustive } - private fun handleStartCallWithPhoneNumber(action: RoomDetailAction.StartCallWithPhoneNumber) { - viewModelScope.launch { - try { - val result = DialPadLookup(session, callManager, directRoomHelper).lookupPhoneNumber(action.phoneNumber) - callManager.startOutgoingCall(result.roomId, result.userId, action.videoCall) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure)) - } - } - } - private fun handleAcceptCall(action: RoomDetailAction.AcceptCall) { callManager.getCallById(action.callId)?.also { _viewEvents.post(RoomDetailViewEvents.DisplayAndAcceptCall(it)) @@ -1491,16 +1478,6 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.OnNewTimelineEvents(eventIds)) } - override fun onPSTNSupportUpdated() { - updateShowDialerOptionState() - } - - private fun updateShowDialerOptionState() { - setState { - copy(showDialerOption = callManager.supportsPSTNProtocol) - } - } - override fun onCleared() { roomSummariesHolder.remove(room.roomId) timeline.dispose() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 965733c424..1ead34fadd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -75,7 +75,6 @@ data class RoomDetailViewState( val canInvite: Boolean = true, val isAllowedToManageWidgets: Boolean = false, val isAllowedToStartWebRTCCall: Boolean = true, - val showDialerOption: Boolean = false, val hasFailedSending: Boolean = false ) : MvRxState { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt index cf508a2dab..e777f6d628 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt @@ -22,19 +22,13 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import com.airbnb.mvrx.withState import im.vector.app.R -import im.vector.app.core.platform.Restorable import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL import im.vector.app.core.utils.checkPermissions -import im.vector.app.features.call.DialerChoiceBottomSheet -import im.vector.app.features.call.dialpad.CallDialPadBottomSheet -import im.vector.app.features.call.dialpad.DialPadFragment import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.session.widgets.model.WidgetType -private const val DIALER_OPTION_TAG = "DIALER_OPTION_TAG" -private const val DIAL_PAD_TAG = "DIAL_PAD_TAG" class StartCallActionsHandler( private val roomId: String, @@ -44,52 +38,20 @@ class StartCallActionsHandler( private val roomDetailViewModel: RoomDetailViewModel, private val startCallActivityResultLauncher: ActivityResultLauncher>, private val showDialogWithMessage: (String) -> Unit, - private val onTapToReturnToCall: () -> Unit): Restorable { + private val onTapToReturnToCall: () -> Unit) { fun onVideoCallClicked() { handleCallRequest(true) } - fun onVoiceCallClicked() = withState(roomDetailViewModel) { - if (it.showDialerOption) { - displayDialerChoiceBottomSheet() - } else { - handleCallRequest(false) - } - } - - private fun DialerChoiceBottomSheet.applyListeners(): DialerChoiceBottomSheet { - onDialPadClicked = ::displayDialPadBottomSheet - onVoiceCallClicked = { handleCallRequest(false) } - return this - } - - private fun CallDialPadBottomSheet.applyCallback(): CallDialPadBottomSheet { - callback = object : DialPadFragment.Callback { - override fun onOkClicked(formatted: String?, raw: String?) { - if (raw.isNullOrEmpty()) return - roomDetailViewModel.handle(RoomDetailAction.StartCallWithPhoneNumber(raw, false)) - } - } - return this - } - - private fun displayDialerChoiceBottomSheet() { - DialerChoiceBottomSheet() - .applyListeners() - .show(fragment.parentFragmentManager, DIALER_OPTION_TAG) - } - - private fun displayDialPadBottomSheet() { - CallDialPadBottomSheet.newInstance(true) - .applyCallback() - .show(fragment.parentFragmentManager, DIAL_PAD_TAG) + fun onVoiceCallClicked() { + handleCallRequest(false) } private fun handleCallRequest(isVideoCall: Boolean) = withState(roomDetailViewModel) { state -> val roomSummary = state.asyncRoomSummary.invoke() ?: return@withState when (roomSummary.joinedMembersCount) { - 1 -> { + 1 -> { val pendingInvite = roomSummary.invitedMembersCount ?: 0 > 0 if (pendingInvite) { // wait for other to join @@ -99,7 +61,7 @@ class StartCallActionsHandler( showDialogWithMessage(fragment.getString(R.string.cannot_call_yourself)) } } - 2 -> { + 2 -> { val currentCall = callManager.getCurrentCall() if (currentCall != null) { // resume existing if same room, if not prompt to kill and then restart new call? @@ -191,12 +153,4 @@ class StartCallActionsHandler( } } - override fun onSaveInstanceState(outState: Bundle) = Unit - - override fun onRestoreInstanceState(savedInstanceState: Bundle?) { - if (savedInstanceState != null) { - (fragment.parentFragmentManager.findFragmentByTag(DIALER_OPTION_TAG) as? DialerChoiceBottomSheet)?.applyListeners() - (fragment.parentFragmentManager.findFragmentByTag(DIAL_PAD_TAG) as? CallDialPadBottomSheet)?.applyCallback() - } - } } From 771b9b8d78d2b2693eed62baed10fcc71b07b6f1 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Jun 2021 19:43:39 +0200 Subject: [PATCH 05/14] Dial pad tab: second step, add to home --- .../android/sdk/internal/SessionManager.kt | 2 +- .../app/features/home/HomeDetailAction.kt | 3 +- .../app/features/home/HomeDetailFragment.kt | 120 ++++++++++++------ .../app/features/home/HomeDetailViewModel.kt | 58 +++++++-- .../app/features/home/HomeDetailViewState.kt | 13 +- .../main/res/menu/home_bottom_navigation.xml | 7 + 6 files changed, 153 insertions(+), 50 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/SessionManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/SessionManager.kt index 441232f57f..c746ad863a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/SessionManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/SessionManager.kt @@ -51,7 +51,7 @@ internal class SessionManager @Inject constructor(private val matrixComponent: M } } - private fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent { + fun getOrCreateSessionComponent(sessionParams: SessionParams): SessionComponent { return sessionComponents.getOrPut(sessionParams.credentials.sessionId()) { DaggerSessionComponent .factory() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt index c64f9d453d..b466f204ec 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt @@ -19,6 +19,7 @@ package im.vector.app.features.home import im.vector.app.core.platform.VectorViewModelAction sealed class HomeDetailAction : VectorViewModelAction { - data class SwitchDisplayMode(val displayMode: RoomListDisplayMode) : HomeDetailAction() + data class SwitchTab(val tab: HomeTab) : HomeDetailAction() object MarkAllRoomsRead : HomeDetailAction() + data class StartCallWithPhoneNumber(val phoneNumber: String): HomeDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 291a4218b9..28a0ef6d7d 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -24,6 +24,8 @@ import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.view.isVisible +import androidx.core.view.iterator +import androidx.fragment.app.Fragment import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState @@ -41,12 +43,14 @@ import im.vector.app.core.ui.views.KnownCallsViewHolder import im.vector.app.databinding.FragmentHomeDetailBinding import im.vector.app.features.call.SharedKnownCallsViewModel import im.vector.app.features.call.VectorCallActivity +import im.vector.app.features.call.dialpad.DialPadFragment import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListParams import im.vector.app.features.home.room.list.UnreadCounterBadgeView import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.VerificationVectorAlert +import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity.Companion.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS import im.vector.app.features.themes.ThemeUtils @@ -100,6 +104,9 @@ class HomeDetailFragment @Inject constructor( } override fun onPrepareOptionsMenu(menu: Menu) { + withState(viewModel) { state -> + menu.iterator().forEach { it.isVisible = state.currentTab is HomeTab.RoomList } + } menu.findItem(R.id.menu_home_mark_all_as_read).isVisible = hasUnreadRooms super.onPrepareOptionsMenu(menu) } @@ -122,7 +129,7 @@ class HomeDetailFragment @Inject constructor( withState(viewModel) { // Update the navigation view if needed (for when we restore the tabs) - views.bottomNavigationView.selectedItemId = it.displayMode.toMenuId() + views.bottomNavigationView.selectedItemId = it.currentTab.toMenuId() } viewModel.selectSubscribe(this, HomeDetailViewState::roomGroupingMethod) { roomGroupingMethod -> @@ -130,14 +137,18 @@ class HomeDetailFragment @Inject constructor( is RoomGroupingMethod.ByLegacyGroup -> { onGroupChange(roomGroupingMethod.groupSummary) } - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { onSpaceChange(roomGroupingMethod.spaceSummary) } } } - viewModel.selectSubscribe(this, HomeDetailViewState::displayMode) { displayMode -> - switchDisplayMode(displayMode) + viewModel.selectSubscribe(this, HomeDetailViewState::currentTab) { currentTab -> + updateUIForTab(currentTab) + } + + viewModel.selectSubscribe(this, HomeDetailViewState::showDialPadTab) { showDialPadTab -> + updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab) } unknownDeviceDetectorSharedViewModel.subscribe { state -> @@ -178,21 +189,11 @@ class HomeDetailFragment @Inject constructor( override fun onResume() { super.onResume() // update notification tab if needed - checkNotificationTabStatus() + updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab()) + callManager.checkForProtocolsSupportIfNeeded() } - private fun checkNotificationTabStatus() { - val wasVisible = views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible - views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab() - if (wasVisible && !vectorPreferences.labAddNotificationTab()) { - // As we hide it check if it's not the current item! - withState(viewModel) { - if (it.displayMode.toMenuId() == R.id.bottom_action_notification) { - viewModel.handle(HomeDetailAction.SwitchDisplayMode(RoomListDisplayMode.PEOPLE)) - } - } - } - } + private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) { val user = state.myMatrixItem @@ -273,10 +274,10 @@ class HomeDetailFragment @Inject constructor( serverBackupStatusViewModel .subscribe(this) { when (val banState = it.bannerState.invoke()) { - is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false) + is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false) BannerState.BackingUp -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.BackingUp, false) null, - BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false) + BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false) } } views.homeKeysBackupBanner.delegate = this @@ -307,7 +308,7 @@ class HomeDetailFragment @Inject constructor( is RoomGroupingMethod.ByLegacyGroup -> { // nothing do far } - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { it.roomGroupingMethod.spaceSummary?.let { sharedActionViewModel.post(HomeActivitySharedAction.ShowSpaceSettings(it.roomId)) } @@ -320,12 +321,13 @@ class HomeDetailFragment @Inject constructor( private fun setupBottomNavigationView() { views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab() views.bottomNavigationView.setOnNavigationItemSelectedListener { - val displayMode = when (it.itemId) { - R.id.bottom_action_people -> RoomListDisplayMode.PEOPLE - R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS - else -> RoomListDisplayMode.NOTIFICATIONS + val tab = when (it.itemId) { + R.id.bottom_action_people -> HomeTab.RoomList(RoomListDisplayMode.PEOPLE) + R.id.bottom_action_rooms -> HomeTab.RoomList(RoomListDisplayMode.ROOMS) + R.id.bottom_action_notification -> HomeTab.RoomList(RoomListDisplayMode.NOTIFICATIONS) + else -> HomeTab.DialPad } - viewModel.handle(HomeDetailAction.SwitchDisplayMode(displayMode)) + viewModel.handle(HomeDetailAction.SwitchTab(tab)) true } @@ -341,13 +343,14 @@ class HomeDetailFragment @Inject constructor( // } } - private fun switchDisplayMode(displayMode: RoomListDisplayMode) { - views.groupToolbarTitleView.setText(displayMode.titleRes) - updateSelectedFragment(displayMode) + private fun updateUIForTab(tab: HomeTab) { + views.groupToolbarTitleView.setText(tab.titleRes) + updateSelectedFragment(tab) + invalidateOptionsMenu() } - private fun updateSelectedFragment(displayMode: RoomListDisplayMode) { - val fragmentTag = "FRAGMENT_TAG_${displayMode.name}" + private fun updateSelectedFragment(tab: HomeTab) { + val fragmentTag = "FRAGMENT_TAG_$tab" val fragmentToShow = childFragmentManager.findFragmentByTag(fragmentTag) childFragmentManager.commitTransaction { childFragmentManager.fragments @@ -356,14 +359,46 @@ class HomeDetailFragment @Inject constructor( detach(it) } if (fragmentToShow == null) { - val params = RoomListParams(displayMode) - add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag) + if (tab is HomeTab.RoomList) { + val params = RoomListParams(tab.displayMode) + add(R.id.roomListContainer, RoomListFragment::class.java, params.toMvRxBundle(), fragmentTag) + } else { + add(R.id.roomListContainer, createDialPadFragment()) + } } else { + if (tab is HomeTab.DialPad) { + (fragmentToShow as? DialPadFragment)?.applyCallback() + } attach(fragmentToShow) } } } + private fun createDialPadFragment(): Fragment { + val fragment = childFragmentManager.fragmentFactory.instantiate(vectorBaseActivity.classLoader, DialPadFragment::class.java.name) + return (fragment as DialPadFragment).apply { + arguments = Bundle().apply { + putBoolean(DialPadFragment.EXTRA_ENABLE_DELETE, true) + putBoolean(DialPadFragment.EXTRA_ENABLE_OK, true) + putString(DialPadFragment.EXTRA_REGION_CODE, VectorLocale.applicationLocale.country) + } + applyCallback() + } + } + + private fun updateTabVisibilitySafely(tabId: Int, isVisible: Boolean) { + val wasVisible = views.bottomNavigationView.menu.findItem(tabId).isVisible + views.bottomNavigationView.menu.findItem(tabId).isVisible = isVisible + if (wasVisible && !isVisible) { + // As we hide it check if it's not the current item! + withState(viewModel) { + if (it.currentTab.toMenuId() == tabId) { + viewModel.handle(HomeDetailAction.SwitchTab(HomeTab.RoomList(RoomListDisplayMode.PEOPLE))) + } + } + } + } + /* ========================================================================================== * KeysBackupBanner Listener * ========================================================================================== */ @@ -398,10 +433,13 @@ class HomeDetailFragment @Inject constructor( } } - private fun RoomListDisplayMode.toMenuId() = when (this) { - RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people - RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms - else -> R.id.bottom_action_notification + private fun HomeTab.toMenuId() = when (this) { + is HomeTab.DialPad -> R.id.bottom_action_dial_pad + is HomeTab.RoomList -> when (displayMode) { + RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people + RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms + else -> R.id.bottom_action_notification + } } override fun onTapToReturnToCall() { @@ -420,6 +458,16 @@ class HomeDetailFragment @Inject constructor( } } + private fun DialPadFragment.applyCallback(): DialPadFragment { + callback = object : DialPadFragment.Callback { + override fun onOkClicked(formatted: String?, raw: String?) { + if (raw.isNullOrEmpty()) return + viewModel.handle(HomeDetailAction.StartCallWithPhoneNumber(raw)) + } + } + return this + } + override fun create(initialState: ServerBackupStatusViewState): ServerBackupStatusViewModel { return serverBackupStatusViewModelFactory.create(initialState) } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 836c63e85b..004f0909f4 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -28,6 +28,11 @@ import im.vector.app.RoomGroupingMethod import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.call.dialpad.DialPadLookup +import im.vector.app.features.call.lookup.CallProtocolsChecker +import im.vector.app.features.call.webrtc.WebRtcCallManager +import im.vector.app.features.createdirect.DirectRoomHelper +import im.vector.app.features.home.room.detail.RoomDetailViewEvents import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers @@ -51,8 +56,11 @@ import java.util.concurrent.TimeUnit class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState, private val session: Session, private val uiStateRepository: UiStateRepository, + private val callManager: WebRtcCallManager, + private val directRoomHelper: DirectRoomHelper, private val appStateHandler: AppStateHandler) - : VectorViewModel(initialState) { + : VectorViewModel(initialState), + CallProtocolsChecker.Listener { @AssistedFactory interface Factory { @@ -64,7 +72,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho override fun initialState(viewModelContext: ViewModelContext): HomeDetailViewState? { val uiStateRepository = (viewModelContext.activity as HasScreenInjector).injector().uiStateRepository() return HomeDetailViewState( - displayMode = uiStateRepository.getDisplayMode() + currentTab = HomeTab.RoomList(uiStateRepository.getDisplayMode()) ) } @@ -79,7 +87,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho observeSyncState() observeRoomGroupingMethod() observeRoomSummaries() - + updateShowDialPadTab() + callManager.addProtocolsCheckerListener(this) session.rx().liveUser(session.myUserId).execute { copy( myMatrixItem = it.invoke()?.getOrNull()?.toMatrixItem() @@ -89,21 +98,50 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho override fun handle(action: HomeDetailAction) { when (action) { - is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(action) + is HomeDetailAction.SwitchTab -> handleSwitchTab(action) HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() + is HomeDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action) } } - private fun handleSwitchDisplayMode(action: HomeDetailAction.SwitchDisplayMode) = withState { state -> - if (state.displayMode != action.displayMode) { - setState { - copy(displayMode = action.displayMode) + private fun handleStartCallWithPhoneNumber(action: HomeDetailAction.StartCallWithPhoneNumber) { + viewModelScope.launch { + try { + val result = DialPadLookup(session, callManager, directRoomHelper).lookupPhoneNumber(action.phoneNumber) + callManager.startOutgoingCall(result.roomId, result.userId, isVideoCall = false) + } catch (failure: Throwable) { + Timber.v(failure) } - - uiStateRepository.storeDisplayMode(action.displayMode) } } + private fun handleSwitchTab(action: HomeDetailAction.SwitchTab) = withState { state -> + if (state.currentTab != action.tab) { + setState { + copy(currentTab = action.tab) + } + if(action.tab is HomeTab.RoomList) { + uiStateRepository.storeDisplayMode(action.tab.displayMode) + } + } + } + + override fun onCleared() { + super.onCleared() + callManager.removeProtocolsCheckerListener(this) + } + + override fun onPSTNSupportUpdated() { + updateShowDialPadTab() + } + + private fun updateShowDialPadTab() { + setState { + copy(showDialPadTab = callManager.supportsPSTNProtocol) + } + } + + // PRIVATE METHODS ***************************************************************************** private fun handleMarkAllRoomsRead() = withState { _ -> diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 5aa9612a7a..4bf1be4b36 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -16,9 +16,11 @@ package im.vector.app.features.home +import androidx.annotation.StringRes import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized +import im.vector.app.R import im.vector.app.RoomGroupingMethod import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.sync.SyncState @@ -28,7 +30,7 @@ data class HomeDetailViewState( val roomGroupingMethod: RoomGroupingMethod = RoomGroupingMethod.BySpace(null), val myMatrixItem: MatrixItem? = null, val asyncRooms: Async> = Uninitialized, - val displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE, + val currentTab: HomeTab = HomeTab.RoomList(RoomListDisplayMode.PEOPLE), val notificationCountCatchup: Int = 0, val notificationHighlightCatchup: Boolean = false, val notificationCountPeople: Int = 0, @@ -36,5 +38,12 @@ data class HomeDetailViewState( val notificationCountRooms: Int = 0, val notificationHighlightRooms: Boolean = false, val hasUnreadMessages: Boolean = false, - val syncState: SyncState = SyncState.Idle + val syncState: SyncState = SyncState.Idle, + val showDialPadTab: Boolean = false ) : MvRxState + +sealed class HomeTab(@StringRes val titleRes: Int) { + data class RoomList(val displayMode: RoomListDisplayMode) : HomeTab(displayMode.titleRes) + object DialPad : HomeTab(R.string.call_dial_pad_title) +} + diff --git a/vector/src/main/res/menu/home_bottom_navigation.xml b/vector/src/main/res/menu/home_bottom_navigation.xml index d52c8afab3..f9f966003a 100644 --- a/vector/src/main/res/menu/home_bottom_navigation.xml +++ b/vector/src/main/res/menu/home_bottom_navigation.xml @@ -20,4 +20,11 @@ android:title="@string/bottom_action_notification" android:visible="false" /> + + From b268d23e5879c324c41d89daf5f37a2e30724ff6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Jun 2021 20:36:58 +0200 Subject: [PATCH 06/14] Dial pad: fix dial pad lookup when result is virtual user --- .../app/features/call/dialpad/DialPadLookup.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index 4ed1e4a0db..d8421ee0c7 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -17,10 +17,11 @@ package im.vector.app.features.call.dialpad import im.vector.app.features.call.lookup.pstnLookup +import im.vector.app.features.call.lookup.sipNativeLookup +import im.vector.app.features.call.vectorCallService import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import org.matrix.android.sdk.api.session.Session -import java.lang.IllegalStateException import javax.inject.Inject class DialPadLookup @Inject constructor( @@ -33,8 +34,16 @@ class DialPadLookup @Inject constructor( data class Result(val userId: String, val roomId: String) suspend fun lookupPhoneNumber(phoneNumber: String): Result { + session.vectorCallService.protocolChecker.awaitCheckProtocols() val thirdPartyUser = session.pstnLookup(phoneNumber, webRtcCallManager.supportedPSTNProtocol).firstOrNull() ?: throw IllegalStateException() - val roomId = directRoomHelper.ensureDMExists(thirdPartyUser.userId) - return Result(userId = thirdPartyUser.userId, roomId = roomId) + // check to see if this is a virtual user, in which case we should find the native user + val nativeUserId = if (webRtcCallManager.supportsVirtualRooms) { + val nativeLookupResults = session.sipNativeLookup(thirdPartyUser.userId) + nativeLookupResults.firstOrNull()?.userId ?: thirdPartyUser.userId + } else { + thirdPartyUser.userId + } + val roomId = directRoomHelper.ensureDMExists(nativeUserId) + return Result(userId = nativeUserId, roomId = roomId) } } From ed56d12b72d0c272e92f900e9cf4eb456f47f881 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Jun 2021 12:58:21 +0200 Subject: [PATCH 07/14] Dialpad: fix some UI --- .../features/call/dialpad/DialPadFragment.kt | 4 ++-- .../main/res/values-land/styles_dial_pad.xml | 22 +++++++++++++++++++ .../src/main/res/values/styles_dial_pad.xml | 12 +++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 vector/src/main/res/values-land/styles_dial_pad.xml diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt index 11c3af9394..9c5b1f5d46 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadFragment.kt @@ -26,9 +26,9 @@ import androidx.core.widget.ImageViewCompat import androidx.fragment.app.Fragment import com.android.dialer.dialpadview.DialpadView import com.android.dialer.dialpadview.DigitsEditText -import com.android.dialer.dialpadview.R import com.google.i18n.phonenumbers.AsYouTypeFormatter import com.google.i18n.phonenumbers.PhoneNumberUtil +import im.vector.app.R import im.vector.app.features.themes.ThemeUtils class DialPadFragment : Fragment() { @@ -57,7 +57,7 @@ class DialPadFragment : Fragment() { dialpadView.findViewById(R.id.dialpad_key_voicemail).isVisible = false digits = dialpadView.digits as? DigitsEditText digits?.isCursorVisible = cursorVisible - digits?.setTextColor(ThemeUtils.getColor(requireContext(), im.vector.app.R.attr.riotx_text_primary)) + digits?.setTextColor(ThemeUtils.getColor(requireContext(), R.attr.riotx_text_primary)) dialpadView.findViewById(R.id.zero).setOnClickListener { append('0') } if (enablePlus) { dialpadView.findViewById(R.id.zero).setOnLongClickListener { diff --git a/vector/src/main/res/values-land/styles_dial_pad.xml b/vector/src/main/res/values-land/styles_dial_pad.xml new file mode 100644 index 0000000000..81841b3f11 --- /dev/null +++ b/vector/src/main/res/values-land/styles_dial_pad.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/values/styles_dial_pad.xml b/vector/src/main/res/values/styles_dial_pad.xml index 614923caad..81841b3f11 100644 --- a/vector/src/main/res/values/styles_dial_pad.xml +++ b/vector/src/main/res/values/styles_dial_pad.xml @@ -3,10 +3,20 @@ + + + \ No newline at end of file From a95eed716313a8f8e3c32e75d9c83f5fbe640b1b Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Jun 2021 15:21:57 +0200 Subject: [PATCH 08/14] Dial pad: add more loading/error --- .../vector/app/core/error/ErrorFormatter.kt | 4 ++- .../features/call/dialpad/DialPadLookup.kt | 8 ++++-- .../features/call/lookup/ThirdPartyLookup.kt | 15 +++++++++-- .../app/features/home/HomeDetailFragment.kt | 10 ++++++-- .../app/features/home/HomeDetailViewEvents.kt | 25 +++++++++++++++++++ .../app/features/home/HomeDetailViewModel.kt | 25 ++++++++++--------- 6 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/HomeDetailViewEvents.kt diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt index b5f45e6586..c0219b26e2 100644 --- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt @@ -116,7 +116,9 @@ class DefaultErrorFormatter @Inject constructor( throwable.localizedMessage } } - is DialPadLookup.Failure -> + is DialPadLookup.Failure.NumberIsYours -> + stringProvider.getString(R.string.cannot_call_yourself) + is DialPadLookup.Failure.NoResult -> stringProvider.getString(R.string.call_dial_pad_lookup_error) else -> throwable.localizedMessage } diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index d8421ee0c7..fd1dfba2a4 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -29,13 +29,16 @@ class DialPadLookup @Inject constructor( private val webRtcCallManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper ) { - class Failure : Throwable() + sealed class Failure : Throwable(){ + object NoResult: Failure() + object NumberIsYours: Failure() + } data class Result(val userId: String, val roomId: String) suspend fun lookupPhoneNumber(phoneNumber: String): Result { session.vectorCallService.protocolChecker.awaitCheckProtocols() - val thirdPartyUser = session.pstnLookup(phoneNumber, webRtcCallManager.supportedPSTNProtocol).firstOrNull() ?: throw IllegalStateException() + val thirdPartyUser = session.pstnLookup(phoneNumber, webRtcCallManager.supportedPSTNProtocol).firstOrNull() ?: throw Failure.NoResult // check to see if this is a virtual user, in which case we should find the native user val nativeUserId = if (webRtcCallManager.supportsVirtualRooms) { val nativeLookupResults = session.sipNativeLookup(thirdPartyUser.userId) @@ -43,6 +46,7 @@ class DialPadLookup @Inject constructor( } else { thirdPartyUser.userId } + if(nativeUserId == session.myUserId) throw Failure.NumberIsYours val roomId = directRoomHelper.ensureDMExists(nativeUserId) return Result(userId = nativeUserId, roomId = roomId) } diff --git a/vector/src/main/java/im/vector/app/features/call/lookup/ThirdPartyLookup.kt b/vector/src/main/java/im/vector/app/features/call/lookup/ThirdPartyLookup.kt index 1e9834059f..dfdc58f78f 100644 --- a/vector/src/main/java/im/vector/app/features/call/lookup/ThirdPartyLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/lookup/ThirdPartyLookup.kt @@ -16,10 +16,13 @@ package im.vector.app.features.call.lookup +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser +private const val LOOKUP_SUCCESS_FIELD = "lookup_success" + suspend fun Session.pstnLookup(phoneNumber: String, protocol: String?): List { if (protocol == null) return emptyList() return tryOrNull { @@ -36,7 +39,11 @@ suspend fun Session.sipVirtualLookup(nativeMxid: String): List { protocol = PROTOCOL_SIP_VIRTUAL, fields = mapOf("native_mxid" to nativeMxid) ) - }.orEmpty() + } + .orEmpty() + .filter { + (it.fields[LOOKUP_SUCCESS_FIELD] as? Boolean).orFalse() + } } suspend fun Session.sipNativeLookup(virtualMxid: String): List { @@ -45,5 +52,9 @@ suspend fun Session.sipNativeLookup(virtualMxid: String): List { protocol = PROTOCOL_SIP_NATIVE, fields = mapOf("virtual_mxid" to virtualMxid) ) - }.orEmpty() + } + .orEmpty() + .filter { + (it.fields[LOOKUP_SUCCESS_FIELD] as? Boolean).orFalse() + } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 28a0ef6d7d..5cdaf12889 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -151,6 +151,14 @@ class HomeDetailFragment @Inject constructor( updateTabVisibilitySafely(R.id.bottom_action_dial_pad, showDialPadTab) } + viewModel.observeViewEvents { viewEvent -> + when (viewEvent) { + HomeDetailViewEvents.CallStarted -> dismissLoadingDialog() + is HomeDetailViewEvents.FailToCall -> showFailure(viewEvent.failure) + HomeDetailViewEvents.Loading -> showLoadingDialog() + } + } + unknownDeviceDetectorSharedViewModel.subscribe { state -> state.unknownSessions.invoke()?.let { unknownDevices -> // Timber.v("## Detector Triggerred in fragment - ${unknownDevices.firstOrNull()}") @@ -193,8 +201,6 @@ class HomeDetailFragment @Inject constructor( callManager.checkForProtocolsSupportIfNeeded() } - - private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) { val user = state.myMatrixItem alertManager.postVectorAlert( diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewEvents.kt new file mode 100644 index 0000000000..a0ff67dc0a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewEvents.kt @@ -0,0 +1,25 @@ +/* + * 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.app.features.home + +import im.vector.app.core.platform.VectorViewEvents + +sealed class HomeDetailViewEvents : VectorViewEvents { + object Loading : HomeDetailViewEvents() + object CallStarted : HomeDetailViewEvents() + data class FailToCall(val failure: Throwable) : HomeDetailViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index 004f0909f4..c367ca936a 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -59,7 +59,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private val callManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper, private val appStateHandler: AppStateHandler) - : VectorViewModel(initialState), + : VectorViewModel(initialState), CallProtocolsChecker.Listener { @AssistedFactory @@ -98,8 +98,8 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho override fun handle(action: HomeDetailAction) { when (action) { - is HomeDetailAction.SwitchTab -> handleSwitchTab(action) - HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() + is HomeDetailAction.SwitchTab -> handleSwitchTab(action) + HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() is HomeDetailAction.StartCallWithPhoneNumber -> handleStartCallWithPhoneNumber(action) } } @@ -107,10 +107,12 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private fun handleStartCallWithPhoneNumber(action: HomeDetailAction.StartCallWithPhoneNumber) { viewModelScope.launch { try { + _viewEvents.post(HomeDetailViewEvents.Loading) val result = DialPadLookup(session, callManager, directRoomHelper).lookupPhoneNumber(action.phoneNumber) callManager.startOutgoingCall(result.roomId, result.userId, isVideoCall = false) + _viewEvents.post(HomeDetailViewEvents.CallStarted) } catch (failure: Throwable) { - Timber.v(failure) + _viewEvents.post(HomeDetailViewEvents.FailToCall(failure)) } } } @@ -120,7 +122,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho setState { copy(currentTab = action.tab) } - if(action.tab is HomeTab.RoomList) { + if (action.tab is HomeTab.RoomList) { uiStateRepository.storeDisplayMode(action.tab.displayMode) } } @@ -141,7 +143,6 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } } - // PRIVATE METHODS ***************************************************************************** private fun handleMarkAllRoomsRead() = withState { _ -> @@ -176,11 +177,11 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private fun observeRoomGroupingMethod() { appStateHandler.selectedRoomGroupingObservable .subscribe { - setState { - copy( - roomGroupingMethod = it.orNull() ?: RoomGroupingMethod.BySpace(null) - ) - } + setState { + copy( + roomGroupingMethod = it.orNull() ?: RoomGroupingMethod.BySpace(null) + ) + } } .disposeOnClear() } @@ -203,7 +204,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho is RoomGroupingMethod.ByLegacyGroup -> { // TODO!! } - is RoomGroupingMethod.BySpace -> { + is RoomGroupingMethod.BySpace -> { val activeSpaceRoomId = groupingMethod.spaceSummary?.roomId val dmInvites = session.getRoomSummaries( roomSummaryQueryParams { From d520dfe10869472de7697773dbd55c79b13fd3b8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Jun 2021 15:57:02 +0200 Subject: [PATCH 09/14] DialPad: clean code --- .../java/im/vector/app/features/call/dialpad/DialPadLookup.kt | 4 ++-- .../java/im/vector/app/features/home/HomeDetailViewModel.kt | 2 -- .../java/im/vector/app/features/home/HomeDetailViewState.kt | 1 - .../app/features/home/room/detail/RoomDetailViewModel.kt | 1 - .../app/features/home/room/detail/StartCallActionsHandler.kt | 3 --- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt index fd1dfba2a4..4f025dc239 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/DialPadLookup.kt @@ -29,7 +29,7 @@ class DialPadLookup @Inject constructor( private val webRtcCallManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper ) { - sealed class Failure : Throwable(){ + sealed class Failure : Throwable() { object NoResult: Failure() object NumberIsYours: Failure() } @@ -46,7 +46,7 @@ class DialPadLookup @Inject constructor( } else { thirdPartyUser.userId } - if(nativeUserId == session.myUserId) throw Failure.NumberIsYours + if (nativeUserId == session.myUserId) throw Failure.NumberIsYours val roomId = directRoomHelper.ensureDMExists(nativeUserId) return Result(userId = nativeUserId, roomId = roomId) } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index c367ca936a..b6210ae019 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -26,13 +26,11 @@ import dagger.assisted.AssistedInject import im.vector.app.AppStateHandler import im.vector.app.RoomGroupingMethod import im.vector.app.core.di.HasScreenInjector -import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.call.dialpad.DialPadLookup import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper -import im.vector.app.features.home.room.detail.RoomDetailViewEvents import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 4bf1be4b36..304444abdd 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -46,4 +46,3 @@ sealed class HomeTab(@StringRes val titleRes: Int) { data class RoomList(val displayMode: RoomListDisplayMode) : HomeTab(displayMode.titleRes) object DialPad : HomeTab(R.string.call_dial_pad_title) } - diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 2be96e07e9..751114c2d9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -39,7 +39,6 @@ import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.call.conference.JitsiService -import im.vector.app.features.call.dialpad.DialPadLookup import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.CommandParser diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt index e777f6d628..64544855bd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/StartCallActionsHandler.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail -import android.os.Bundle import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment @@ -29,7 +28,6 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.settings.VectorPreferences import org.matrix.android.sdk.api.session.widgets.model.WidgetType - class StartCallActionsHandler( private val roomId: String, private val fragment: Fragment, @@ -152,5 +150,4 @@ class StartCallActionsHandler( } } } - } From 1b8b59076c84f8f8011ffb13c27cbc8c77730e6c Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Jun 2021 19:33:28 +0200 Subject: [PATCH 10/14] Should fix issue with DM creation where we can ends up with DM in rooms --- .../internal/session/user/accountdata/DirectChatsHelper.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt index e297f59b96..a9e5089774 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt @@ -31,9 +31,11 @@ internal class DirectChatsHelper @Inject constructor(@SessionDatabase */ fun getLocalUserAccount(filterRoomId: String? = null): MutableMap> { return Realm.getInstance(realmConfiguration).use { realm -> + // Makes sure we have the latest realm updates, this is important as we sent this information to the server. + realm.refresh() RoomSummaryEntity.getDirectRooms(realm) .asSequence() - .filter { it.roomId != filterRoomId && it.directUserId != null } + .filter { it.roomId != filterRoomId && it.directUserId != null && it.membership.isActive() } .groupByTo(mutableMapOf(), { it.directUserId!! }, { it.roomId }) } } From 2c767e734544a5f4dfa9591b7839e2d1e0364d28 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 3 Jun 2021 19:48:53 +0200 Subject: [PATCH 11/14] Add towncrier files --- newsfragment/3333.bugfix | 1 + newsfragment/3457.misc | 1 + 2 files changed, 2 insertions(+) create mode 100644 newsfragment/3333.bugfix create mode 100644 newsfragment/3457.misc diff --git a/newsfragment/3333.bugfix b/newsfragment/3333.bugfix new file mode 100644 index 0000000000..d60f725498 --- /dev/null +++ b/newsfragment/3333.bugfix @@ -0,0 +1 @@ +Fix new DMs not always marked as such \ No newline at end of file diff --git a/newsfragment/3457.misc b/newsfragment/3457.misc new file mode 100644 index 0000000000..6a70641cfd --- /dev/null +++ b/newsfragment/3457.misc @@ -0,0 +1 @@ +Move the ability to start a call from dialpad directly to a dedicated tab in the home screen. \ No newline at end of file From a421f37e70804930dfaae51036bd368b679cb9e3 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 2 Jun 2021 11:07:05 +0200 Subject: [PATCH 12/14] VoIP : clean and add changelog --- .../sdk/api/session/call/CallListener.kt | 2 - newsfragment/3451.feature | 1 + .../app/core/glide/AvatarPlaceholder.kt | 82 +++++++++++++++++++ .../vector/app/core/glide/MyAppGlideModule.kt | 6 ++ .../app/features/call/VectorCallActivity.kt | 39 ++++----- .../app/features/call/VectorCallViewModel.kt | 10 +-- .../app/features/call/VectorCallViewState.kt | 3 +- .../features/call/webrtc/WebRtcCallManager.kt | 2 +- .../app/features/home/AvatarRenderer.kt | 32 ++++++-- vector/src/main/res/layout/activity_call.xml | 17 ++-- vector/src/main/res/values/colors.xml | 3 +- 11 files changed, 153 insertions(+), 44 deletions(-) create mode 100644 newsfragment/3451.feature create mode 100644 vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt index 4704a657df..d17be59cd4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallListener.kt @@ -67,6 +67,4 @@ interface CallListener { * Called when an asserted identity event is received */ fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) - - } diff --git a/newsfragment/3451.feature b/newsfragment/3451.feature new file mode 100644 index 0000000000..dbf1a9b02c --- /dev/null +++ b/newsfragment/3451.feature @@ -0,0 +1 @@ +Adds support for receiving MSC3086 Asserted Identity events. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt new file mode 100644 index 0000000000..32968c4f80 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 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.app.core.glide + +import android.content.Context +import android.graphics.drawable.Drawable +import com.bumptech.glide.Priority +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.Options +import com.bumptech.glide.load.data.DataFetcher +import com.bumptech.glide.load.model.ModelLoader +import com.bumptech.glide.load.model.ModelLoaderFactory +import com.bumptech.glide.load.model.MultiModelLoaderFactory +import com.bumptech.glide.signature.ObjectKey +import im.vector.app.core.extensions.vectorComponent +import org.matrix.android.sdk.api.util.MatrixItem + +data class AvatarPlaceholder(val matrixItem: MatrixItem) + +class AvatarPlaceholderModelLoaderFactory(private val context: Context) : ModelLoaderFactory { + + override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader { + return AvatarPlaceholderModelLoader(context) + } + + override fun teardown() { + // Is there something to do here? + } +} + +class AvatarPlaceholderModelLoader(private val context: Context) + : ModelLoader { + + override fun buildLoadData(model: AvatarPlaceholder, width: Int, height: Int, options: Options): ModelLoader.LoadData? { + return ModelLoader.LoadData(ObjectKey(model), AvatarPlaceholderDataFetcher(context, model)) + } + + override fun handles(model: AvatarPlaceholder): Boolean { + return true + } +} + +class AvatarPlaceholderDataFetcher(context: Context, private val data: AvatarPlaceholder) + : DataFetcher { + + private val avatarRenderer = context.vectorComponent().avatarRenderer() + + override fun loadData(priority: Priority, callback: DataFetcher.DataCallback) { + val avatarPlaceholder = avatarRenderer.getPlaceholderDrawable(data.matrixItem) + callback.onDataReady(avatarPlaceholder) + } + + override fun cleanup() { + // NOOP + } + + override fun cancel() { + // NOOP + } + + override fun getDataClass(): Class { + return Drawable::class.java + } + + override fun getDataSource(): DataSource { + return DataSource.LOCAL + } +} diff --git a/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt b/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt index 6ded33f823..74c9d4f0f6 100644 --- a/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt +++ b/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt @@ -17,6 +17,7 @@ package im.vector.app.core.glide import android.content.Context +import android.graphics.drawable.Drawable import android.util.Log import com.bumptech.glide.Glide @@ -40,5 +41,10 @@ class MyAppGlideModule : AppGlideModule() { InputStream::class.java, VectorGlideModelLoaderFactory(context) ) + registry.append( + AvatarPlaceholder::class.java, + Drawable::class.java, + AvatarPlaceholderModelLoaderFactory(context) + ) } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index a18f5bb918..c76bf615ff 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -198,18 +198,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } is CallState.Connected -> { if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { - if (state.transferee !is VectorCallViewState.TransfereeState.NoTransferee) { - val transfereeName = if (state.transferee is VectorCallViewState.TransfereeState.KnownTransferee) { - state.transferee.name - } else { - getString(R.string.call_transfer_unknown_person) - } - views.callActionText.text = getString(R.string.call_transfer_transfer_to_title, transfereeName) - views.callActionText.isVisible = true - views.callActionText.setOnClickListener { callViewModel.handle(VectorCallViewActions.TransferCall) } - views.callStatusText.text = state.formattedDuration - configureCallInfo(state) - } else if (state.isLocalOnHold || state.isRemoteOnHold) { + if (state.isLocalOnHold || state.isRemoteOnHold) { views.smallIsHeldIcon.isVisible = true views.callVideoGroup.isInvisible = true views.callInfoGroup.isVisible = true @@ -225,6 +214,17 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callStatusText.text = getString(R.string.call_held_by_user, it.getBestName()) } } + } else if (state.transferee !is VectorCallViewState.TransfereeState.NoTransferee) { + val transfereeName = if (state.transferee is VectorCallViewState.TransfereeState.KnownTransferee) { + state.transferee.name + } else { + getString(R.string.call_transfer_unknown_person) + } + views.callActionText.text = getString(R.string.call_transfer_transfer_to_title, transfereeName) + views.callActionText.isVisible = true + views.callActionText.setOnClickListener { callViewModel.handle(VectorCallViewActions.TransferCall) } + views.callStatusText.text = state.formattedDuration + configureCallInfo(state) } else { views.callStatusText.text = state.formattedDuration configureCallInfo(state) @@ -256,15 +256,15 @@ class VectorCallActivity : VectorBaseActivity(), CallContro private fun configureCallInfo(state: VectorCallViewState, blurAvatar: Boolean = false) { state.callInfo?.opponentUserItem?.let { - val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen) - avatarRenderer.renderBlur(it, views.bgCallView, sampling = 20, rounded = false, colorFilter = colorFilter) + val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen_blur) + avatarRenderer.renderBlur(it, views.bgCallView, sampling = 20, rounded = false, colorFilter = colorFilter, addPlaceholder = false) if (state.transferee is VectorCallViewState.TransfereeState.NoTransferee) { views.participantNameText.text = it.getBestName() } else { views.participantNameText.text = getString(R.string.call_transfer_consulting_with, it.getBestName()) } if (blurAvatar) { - avatarRenderer.renderBlur(it, views.otherMemberAvatar, sampling = 2, rounded = true, colorFilter = colorFilter) + avatarRenderer.renderBlur(it, views.otherMemberAvatar, sampling = 2, rounded = true, colorFilter = colorFilter, addPlaceholder = true) } else { avatarRenderer.render(it, views.otherMemberAvatar) } @@ -273,13 +273,14 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.otherKnownCallLayout.isVisible = false } else { val otherCall = callManager.getCallById(state.otherKnownCallInfo.callId) - val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen) + val colorFilter = ContextCompat.getColor(this, R.color.bg_call_screen_blur) avatarRenderer.renderBlur( matrixItem = state.otherKnownCallInfo.opponentUserItem, imageView = views.otherKnownCallAvatarView, sampling = 20, - rounded = false, - colorFilter = colorFilter + rounded = true, + colorFilter = colorFilter, + addPlaceholder = true ) views.otherKnownCallLayout.isVisible = true views.otherSmallIsHeldIcon.isVisible = otherCall?.let { it.isLocalOnHold || it.remoteOnHold }.orFalse() @@ -288,7 +289,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro private fun configureCallViews() { views.callControlsView.interactionListener = this - views.otherKnownCallAvatarView.setOnClickListener { + views.otherKnownCallLayout.setOnClickListener { withState(callViewModel) { val otherCall = callManager.getCallById(it.otherKnownCallInfo?.callId ?: "") ?: return@withState startActivity(newIntent(this, otherCall, null)) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 68905b6c49..e7b2b629e1 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -89,7 +89,7 @@ class VectorCallViewModel @AssistedInject constructor( } } - override fun assertedIdentityChanged(){ + override fun assertedIdentityChanged() { setState { copy(callInfo = call?.extractCallInfo()) } @@ -208,17 +208,17 @@ class VectorCallViewModel @AssistedInject constructor( } } - private fun WebRtcCall.extractCallInfo(): VectorCallViewState.CallInfo{ + private fun WebRtcCall.extractCallInfo(): VectorCallViewState.CallInfo { val assertedIdentity = this.remoteAssertedIdentity - val matrixItem = if(assertedIdentity != null){ + val matrixItem = if (assertedIdentity != null) { val userId = if (MatrixPatterns.isUserId(assertedIdentity.id)) { assertedIdentity.id!! } else { // Need an id starting with @ "@${assertedIdentity.displayName}" } - MatrixItem.UserItem(userId,assertedIdentity.displayName, assertedIdentity.avatarUrl) - }else { + MatrixItem.UserItem(userId, assertedIdentity.displayName, assertedIdentity.avatarUrl) + } else { getOpponentAsMatrixItem(session) } return VectorCallViewState.CallInfo(callId, matrixItem) diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt index b6c226564e..3e7791cc08 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewState.kt @@ -20,7 +20,6 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.features.call.audio.CallAudioManager -import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.util.MatrixItem @@ -54,7 +53,7 @@ data class VectorCallViewState( data class CallInfo( val callId: String, - val opponentUserItem: MatrixItem? = null, + val opponentUserItem: MatrixItem? = null ) constructor(callArgs: CallArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index d56f1aa015..25463428e9 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -424,7 +424,7 @@ class WebRtcCallManager @Inject constructor( } override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { - if(!BuildConfig.handleCallAssertedIdentityEvents){ + if (!BuildConfig.handleCallAssertedIdentityEvents) { return } val call = callsByCallId[callAssertedIdentityContent.callId] diff --git a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt index c6cceee3b9..787027e0e2 100644 --- a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt @@ -34,6 +34,7 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget import com.bumptech.glide.request.target.Target import im.vector.app.core.contacts.MappedContact import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.core.glide.AvatarPlaceholder import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideRequest import im.vector.app.core.glide.GlideRequests @@ -136,7 +137,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active matrixItem: MatrixItem, target: Target) { val placeholder = getPlaceholderDrawable(matrixItem) - buildGlideRequest(glideRequests, matrixItem.avatarUrl) + glideRequests.loadResolvedUrl(matrixItem.avatarUrl) .apply { when (matrixItem) { is MatrixItem.SpaceItem -> { @@ -175,7 +176,12 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active } @UiThread - fun renderBlur(matrixItem: MatrixItem, imageView: ImageView, sampling: Int, rounded: Boolean, @ColorInt colorFilter: Int? = null) { + fun renderBlur(matrixItem: MatrixItem, + imageView: ImageView, + sampling: Int, + rounded: Boolean, + @ColorInt colorFilter: Int? = null, + addPlaceholder: Boolean) { val transformations = mutableListOf>( BlurTransformation(20, sampling) ) @@ -185,14 +191,26 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active if (rounded) { transformations.add(CircleCrop()) } - buildGlideRequest(GlideApp.with(imageView), matrixItem.avatarUrl) - .apply(RequestOptions.bitmapTransform(MultiTransformation(transformations))) + val bitmapTransform = RequestOptions.bitmapTransform(MultiTransformation(transformations)) + val glideRequests = GlideApp.with(imageView) + val placeholderRequest = if (addPlaceholder) { + glideRequests + .load(AvatarPlaceholder(matrixItem)) + .apply(bitmapTransform) + } else { + null + } + glideRequests.loadResolvedUrl(matrixItem.avatarUrl) + .apply(bitmapTransform) + // We are using thumbnail and error API so we can have blur transformation on it... + .thumbnail(placeholderRequest) + .error(placeholderRequest) .into(imageView) } @AnyThread fun getCachedDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem): Drawable { - return buildGlideRequest(glideRequests, matrixItem.avatarUrl) + return glideRequests.loadResolvedUrl(matrixItem.avatarUrl) .onlyRetrieveFromCache(true) .apply(RequestOptions.circleCropTransform()) .submit() @@ -220,9 +238,9 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active // PRIVATE API ********************************************************************************* - private fun buildGlideRequest(glideRequests: GlideRequests, avatarUrl: String?): GlideRequest { + private fun GlideRequests.loadResolvedUrl(avatarUrl: String?): GlideRequest { val resolvedUrl = resolvedUrl(avatarUrl) - return glideRequests.load(resolvedUrl) + return load(resolvedUrl) } private fun resolvedUrl(avatarUrl: String?): String? { diff --git a/vector/src/main/res/layout/activity_call.xml b/vector/src/main/res/layout/activity_call.xml index c4bba45ebf..1c9401b5ab 100644 --- a/vector/src/main/res/layout/activity_call.xml +++ b/vector/src/main/res/layout/activity_call.xml @@ -9,7 +9,7 @@ android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/bg_call_screen" + android:background="@color/bg_call_screen_blur" tools:ignore="MergeRootFrame"> - @@ -66,7 +69,7 @@ android:importantForAccessibility="no" android:src="@drawable/ic_call_small_pause" /> - + #1E0DBD8B #1E61708B - #99000000 + #99000000 + #27303A #FFFF4B55 #FF61708B From cc67e83b21854827be25fbf6ceba39d449e5db09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 05:54:29 +0000 Subject: [PATCH 13/14] Bump daggerVersion from 2.36 to 2.37 Bumps `daggerVersion` from 2.36 to 2.37. Updates `dagger` from 2.36 to 2.37 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.36...dagger-2.37) Updates `dagger-compiler` from 2.36 to 2.37 - [Release notes](https://github.com/google/dagger/releases) - [Changelog](https://github.com/google/dagger/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/dagger/compare/dagger-2.36...dagger-2.37) --- updated-dependencies: - dependency-name: com.google.dagger:dagger dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.google.dagger:dagger-compiler dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- matrix-sdk-android/build.gradle | 2 +- vector/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 875662759a..60352e4a97 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -112,7 +112,7 @@ dependencies { def lifecycle_version = '2.2.0' def arch_version = '2.1.0' def markwon_version = '3.1.0' - def daggerVersion = '2.36' + def daggerVersion = '2.37' def work_version = '2.5.0' def retrofit_version = '2.9.0' diff --git a/vector/build.gradle b/vector/build.gradle index a94f796a90..1ca50e4b10 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -302,7 +302,7 @@ dependencies { def big_image_viewer_version = '1.8.0' def glide_version = '4.12.0' def moshi_version = '1.12.0' - def daggerVersion = '2.36' + def daggerVersion = '2.37' def autofill_version = "1.1.0" def work_version = '2.5.0' def arch_version = '2.1.0' From aa3bdd6044cc3bb4b7869650be9803793d2f6c8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 05:55:24 +0000 Subject: [PATCH 14/14] Bump sonarqube-gradle-plugin from 3.2.0 to 3.3 Bumps sonarqube-gradle-plugin from 3.2.0 to 3.3. --- updated-dependencies: - dependency-name: org.sonarsource.scanner.gradle:sonarqube-gradle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 881cd340f1..b213066423 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.2.1' classpath 'com.google.gms:google-services:4.3.8' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.2.0' + classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4' classpath "com.likethesalad.android:string-reference:1.2.2"