diff --git a/CHANGES.md b/CHANGES.md index 76da7609e1..79ea40de59 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Improvements 🙌: - Bugfix 🐛: - - + - Fix issue when updating the avatar of a room Translations 🗣: - diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index 75c7ad4d53..a81f503e77 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -555,7 +555,7 @@ class SASTest : InstrumentedTest { mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull() + val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() requestID = prAlicePOV?.transactionId Log.v("TEST", "== alicePOV is $prAlicePOV") prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId @@ -566,7 +566,7 @@ class SASTest : InstrumentedTest { mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull() + val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull() Log.v("TEST", "== prBobPOV is $prBobPOV") prBobPOV?.transactionId == requestID } @@ -581,7 +581,7 @@ class SASTest : InstrumentedTest { // wait for alice to get the ready mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { - val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull() + val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull() Log.v("TEST", "== prAlicePOV is $prAlicePOV") prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt index 65c6a1f1f7..2413786ea9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/VerificationService.kt @@ -41,7 +41,7 @@ interface VerificationService { fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction? - fun getExistingVerificationRequest(otherUserId: String): List? + fun getExistingVerificationRequests(otherUserId: String): List fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt index 7f02750359..29ddd92213 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt @@ -537,11 +537,10 @@ internal class DefaultVerificationService @Inject constructor( // If there is a corresponding request, we can auto accept // as we are the one requesting in first place (or we accepted the request) // I need to check if the pending request was related to this device also - val autoAccept = getExistingVerificationRequest(otherUserId)?.any { + val autoAccept = getExistingVerificationRequests(otherUserId).any { it.transactionId == startReq.transactionId && (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId) } - ?: false val tx = DefaultIncomingSASDefaultVerificationTransaction( // this, setDeviceVerificationAction, @@ -837,8 +836,8 @@ internal class DefaultVerificationService @Inject constructor( // SAS do not care for now? } - // Now transactions are udated, let's also update Requests - val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId } + // Now transactions are updated, let's also update Requests + val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == doneReq.transactionId } if (existingRequest == null) { Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}") return @@ -892,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor( private fun handleReadyReceived(senderId: String, readyReq: ValidVerificationInfoReady, transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) { - val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionId } + val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == readyReq.transactionId } if (existingRequest == null) { Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}") return @@ -1041,9 +1040,9 @@ internal class DefaultVerificationService @Inject constructor( } } - override fun getExistingVerificationRequest(otherUserId: String): List? { + override fun getExistingVerificationRequests(otherUserId: String): List { synchronized(lock = pendingRequests) { - return pendingRequests[otherUserId] + return pendingRequests[otherUserId].orEmpty() } } diff --git a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt index 3f24a28c28..64df788e53 100644 --- a/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt +++ b/multipicker/src/main/java/im/vector/lib/multipicker/CameraPicker.kt @@ -94,19 +94,21 @@ class CameraPicker { return Intent(MediaStore.ACTION_IMAGE_CAPTURE) } - private fun createPhotoUri(context: Context): Uri { - val file = createImageFile(context) - val authority = context.packageName + ".multipicker.fileprovider" - return FileProvider.getUriForFile(context, authority, file) - } + companion object { + fun createPhotoUri(context: Context): Uri { + val file = createImageFile(context) + val authority = context.packageName + ".multipicker.fileprovider" + return FileProvider.getUriForFile(context, authority, file) + } - private fun createImageFile(context: Context): File { - val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) - val storageDir: File = context.filesDir - return File.createTempFile( - "${timeStamp}_", /* prefix */ - ".jpg", /* suffix */ - storageDir /* directory */ - ) + private fun createImageFile(context: Context): File { + val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val storageDir: File = context.filesDir + return File.createTempFile( + "${timeStamp}_", /* prefix */ + ".jpg", /* suffix */ + storageDir /* directory */ + ) + } } } diff --git a/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt b/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt index 7198cdb4a2..8cb122d5bb 100644 --- a/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt +++ b/vector/src/main/java/im/vector/app/core/dialogs/GalleryOrCameraDialogHelper.kt @@ -23,6 +23,7 @@ import androidx.core.net.toUri import androidx.fragment.app.Fragment import com.yalantis.ucrop.UCrop import im.vector.app.R +import im.vector.app.core.extensions.insertBeforeLast import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO @@ -86,7 +87,7 @@ class GalleryOrCameraDialogHelper( } private fun startUCrop(image: MultiPickerImageType) { - val destinationFile = File(activity.cacheDir, "${image.displayName}_e_${System.currentTimeMillis()}") + val destinationFile = File(activity.cacheDir, image.displayName.insertBeforeLast("_e_${System.currentTimeMillis()}")) val uri = image.contentUri createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title)) .withAspectRatio(1f, 1f) @@ -116,7 +117,7 @@ class GalleryOrCameraDialogHelper( when (type) { Type.Camera -> if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, activity, takePhotoPermissionActivityResultLauncher)) { - avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(activity, takePhotoActivityResultLauncher) + doOpenCamera() } Type.Gallery -> MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher) diff --git a/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt index c3e6520a46..07a684abef 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt @@ -48,3 +48,21 @@ fun CharSequence.isMsisdn(): Boolean { false } } + +/** + * Useful to append a String at the end of a filename but before the extension if any + * Ex: + * - "file.txt".insertBeforeLast("_foo") will return "file_foo.txt" + * - "file".insertBeforeLast("_foo") will return "file_foo" + * - "fi.le.txt".insertBeforeLast("_foo") will return "fi.le_foo.txt" + * - null.insertBeforeLast("_foo") will return "_foo" + */ +fun String?.insertBeforeLast(insert: String, delimiter: String = ".") : String { + if (this == null) return insert + val idx = lastIndexOf(delimiter) + return if (idx == -1) { + this + insert + } else { + replaceRange(idx, idx, insert) + } +} diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index 9f3ba39bbe..ba0250724c 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -38,6 +38,7 @@ import com.airbnb.mvrx.withState import com.yalantis.ucrop.UCrop import im.vector.app.R import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.insertBeforeLast import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider @@ -170,7 +171,7 @@ class AttachmentsPreviewFragment @Inject constructor( private fun handleEditAction() = withState(viewModel) { val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState - val destinationFile = File(requireContext().cacheDir, "${currentAttachment.name}_edited_image_${System.currentTimeMillis()}") + val destinationFile = File(requireContext().cacheDir, currentAttachment.name.insertBeforeLast("_edited_image_${System.currentTimeMillis()}")) val uri = currentAttachment.queryUri createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name) .getIntent(requireContext()) diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 951ff4ede6..c7533cd3df 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -140,6 +140,12 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } private fun handleDoResetAll() { + // as we are going to reset, we'd better cancel all outgoing requests + // if not they could be accepted in the middle of the reset process + // and cause strange use cases + session.cryptoService().verificationService().getExistingVerificationRequests(session.myUserId).forEach { + session.cryptoService().verificationService().cancelVerificationRequest(it) + } _viewEvents.post(SharedSecureStorageViewEvent.ShowResetBottomSheet) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt index b7c689f41f..47e373ed0a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -237,7 +237,7 @@ class BootstrapCrossSigningTask @Inject constructor( Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup") // First ensure that in sync - val serverVersion = awaitCallback { + var serverVersion = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) } @@ -247,6 +247,16 @@ class BootstrapCrossSigningTask @Inject constructor( || (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown) || (params.setupMode == SetupMode.HARD_RESET) if (shouldCreateKeyBackup) { + // clear all existing backups + while (serverVersion != null) { + awaitCallback { + session.cryptoService().keysBackupService().deleteBackup(serverVersion!!.version, it) + } + serverVersion = awaitCallback { + session.cryptoService().keysBackupService().getCurrentVersion(it) + } + } + Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup") val creationInfo = awaitCallback { session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt index e42eb6de6f..7d98b7c2a5 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -164,7 +164,7 @@ class IncomingVerificationRequestHandler @Inject constructor( override fun verificationRequestUpdated(pr: PendingVerificationRequest) { // If an incoming request is readied (by another device?) we should discard the alert - if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) { + if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession || pr.cancelConclusion != null)) { popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr)) } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 2d09974687..aa20a9a992 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -99,8 +99,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( val pr = if (selfVerificationMode) { // See if active tx for this user and take it - session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId) - ?.lastOrNull { !it.isFinished } + session.cryptoService().verificationService().getExistingVerificationRequests(args.otherUserId) + .lastOrNull { !it.isFinished } ?.also { verificationRequest -> if (verificationRequest.isIncoming && !verificationRequest.isReady) { // auto ready in this case, as we are waiting diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 32d8f043c3..6bf88e755e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -153,20 +153,20 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: } private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) { - deletePendingAvatar() - setState { copy(avatarAction = action.avatarAction) } - } - - private fun deletePendingAvatar() { - // Maybe delete the pending avatar - withState { - (it.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar) - ?.let { tryOrNull { it.newAvatarUri.toFile().delete() } } + setState { + deletePendingAvatar(this) + copy(avatarAction = action.avatarAction) } } + private fun deletePendingAvatar(state: RoomSettingsViewState) { + // Maybe delete the pending avatar + (state.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar) + ?.let { tryOrNull { it.newAvatarUri.toFile().delete() } } + } + private fun cancel() { - deletePendingAvatar() + withState { deletePendingAvatar(it) } _viewEvents.post(RoomSettingsViewEvents.GoBack) } @@ -180,7 +180,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: when (val avatarAction = state.avatarAction) { RoomSettingsViewState.AvatarAction.None -> Unit - RoomSettingsViewState.AvatarAction.DeleteAvatar -> { + RoomSettingsViewState.AvatarAction.DeleteAvatar -> { operationList.add(room.rx().deleteAvatar()) } is RoomSettingsViewState.AvatarAction.UpdateAvatar -> { @@ -209,8 +209,10 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: .subscribe( { postLoading(false) - setState { copy(newHistoryVisibility = null) } - deletePendingAvatar() + setState { + deletePendingAvatar(this) + copy(newHistoryVisibility = null) + } _viewEvents.post(RoomSettingsViewEvents.Success) }, { diff --git a/vector/src/main/res/xml/vector_settings_security_privacy.xml b/vector/src/main/res/xml/vector_settings_security_privacy.xml index fa26107edb..5dfde2d1df 100644 --- a/vector/src/main/res/xml/vector_settings_security_privacy.xml +++ b/vector/src/main/res/xml/vector_settings_security_privacy.xml @@ -115,7 +115,9 @@ - +