commit
b4f9d40767
@ -8,7 +8,7 @@ Improvements 🙌:
|
|||||||
-
|
-
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
-
|
- Fix issue when updating the avatar of a room
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
@ -555,7 +555,7 @@ class SASTest : InstrumentedTest {
|
|||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
requestID = prAlicePOV?.transactionId
|
requestID = prAlicePOV?.transactionId
|
||||||
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
Log.v("TEST", "== alicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
prAlicePOV?.transactionId != null && prAlicePOV.localId == req.localId
|
||||||
@ -566,7 +566,7 @@ class SASTest : InstrumentedTest {
|
|||||||
|
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prBobPOV = bobVerificationService.getExistingVerificationRequest(aliceSession.myUserId)?.firstOrNull()
|
val prBobPOV = bobVerificationService.getExistingVerificationRequests(aliceSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
Log.v("TEST", "== prBobPOV is $prBobPOV")
|
||||||
prBobPOV?.transactionId == requestID
|
prBobPOV?.transactionId == requestID
|
||||||
}
|
}
|
||||||
@ -581,7 +581,7 @@ class SASTest : InstrumentedTest {
|
|||||||
// wait for alice to get the ready
|
// wait for alice to get the ready
|
||||||
mTestHelper.waitWithLatch {
|
mTestHelper.waitWithLatch {
|
||||||
mTestHelper.retryPeriodicallyWithLatch(it) {
|
mTestHelper.retryPeriodicallyWithLatch(it) {
|
||||||
val prAlicePOV = aliceVerificationService.getExistingVerificationRequest(bobSession.myUserId)?.firstOrNull()
|
val prAlicePOV = aliceVerificationService.getExistingVerificationRequests(bobSession.myUserId).firstOrNull()
|
||||||
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
Log.v("TEST", "== prAlicePOV is $prAlicePOV")
|
||||||
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
prAlicePOV?.transactionId == requestID && prAlicePOV?.isReady != null
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ interface VerificationService {
|
|||||||
|
|
||||||
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction?
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>?
|
fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest>
|
||||||
|
|
||||||
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
fun getExistingVerificationRequest(otherUserId: String, tid: String?): PendingVerificationRequest?
|
||||||
|
|
||||||
|
@ -537,11 +537,10 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
// If there is a corresponding request, we can auto accept
|
// If there is a corresponding request, we can auto accept
|
||||||
// as we are the one requesting in first place (or we accepted the request)
|
// 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
|
// 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.transactionId == startReq.transactionId
|
||||||
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
&& (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId)
|
||||||
}
|
}
|
||||||
?: false
|
|
||||||
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
val tx = DefaultIncomingSASDefaultVerificationTransaction(
|
||||||
// this,
|
// this,
|
||||||
setDeviceVerificationAction,
|
setDeviceVerificationAction,
|
||||||
@ -837,8 +836,8 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
// SAS do not care for now?
|
// SAS do not care for now?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now transactions are udated, let's also update Requests
|
// Now transactions are updated, let's also update Requests
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == doneReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
|
||||||
return
|
return
|
||||||
@ -892,7 +891,7 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
private fun handleReadyReceived(senderId: String,
|
private fun handleReadyReceived(senderId: String,
|
||||||
readyReq: ValidVerificationInfoReady,
|
readyReq: ValidVerificationInfoReady,
|
||||||
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
transportCreator: (DefaultVerificationTransaction) -> VerificationTransport) {
|
||||||
val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionId }
|
val existingRequest = getExistingVerificationRequests(senderId).find { it.transactionId == readyReq.transactionId }
|
||||||
if (existingRequest == null) {
|
if (existingRequest == null) {
|
||||||
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionId} fromDevice ${readyReq.fromDevice}")
|
||||||
return
|
return
|
||||||
@ -1041,9 +1040,9 @@ internal class DefaultVerificationService @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getExistingVerificationRequest(otherUserId: String): List<PendingVerificationRequest>? {
|
override fun getExistingVerificationRequests(otherUserId: String): List<PendingVerificationRequest> {
|
||||||
synchronized(lock = pendingRequests) {
|
synchronized(lock = pendingRequests) {
|
||||||
return pendingRequests[otherUserId]
|
return pendingRequests[otherUserId].orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,8 @@ class CameraPicker {
|
|||||||
return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPhotoUri(context: Context): Uri {
|
companion object {
|
||||||
|
fun createPhotoUri(context: Context): Uri {
|
||||||
val file = createImageFile(context)
|
val file = createImageFile(context)
|
||||||
val authority = context.packageName + ".multipicker.fileprovider"
|
val authority = context.packageName + ".multipicker.fileprovider"
|
||||||
return FileProvider.getUriForFile(context, authority, file)
|
return FileProvider.getUriForFile(context, authority, file)
|
||||||
@ -110,3 +111,4 @@ class CameraPicker {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ import androidx.core.net.toUri
|
|||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.insertBeforeLast
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||||
@ -86,7 +87,7 @@ class GalleryOrCameraDialogHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startUCrop(image: MultiPickerImageType) {
|
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
|
val uri = image.contentUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), fragment.getString(R.string.rotate_and_crop_screen_title))
|
||||||
.withAspectRatio(1f, 1f)
|
.withAspectRatio(1f, 1f)
|
||||||
@ -116,7 +117,7 @@ class GalleryOrCameraDialogHelper(
|
|||||||
when (type) {
|
when (type) {
|
||||||
Type.Camera ->
|
Type.Camera ->
|
||||||
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, activity, takePhotoPermissionActivityResultLauncher)) {
|
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, activity, takePhotoPermissionActivityResultLauncher)) {
|
||||||
avatarCameraUri = MultiPicker.get(MultiPicker.CAMERA).startWithExpectingFile(activity, takePhotoActivityResultLauncher)
|
doOpenCamera()
|
||||||
}
|
}
|
||||||
Type.Gallery ->
|
Type.Gallery ->
|
||||||
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
MultiPicker.get(MultiPicker.IMAGE).single().startWith(pickImageActivityResultLauncher)
|
||||||
|
@ -48,3 +48,21 @@ fun CharSequence.isMsisdn(): Boolean {
|
|||||||
false
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ import com.airbnb.mvrx.withState
|
|||||||
import com.yalantis.ucrop.UCrop
|
import com.yalantis.ucrop.UCrop
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
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.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
@ -170,7 +171,7 @@ class AttachmentsPreviewFragment @Inject constructor(
|
|||||||
|
|
||||||
private fun handleEditAction() = withState(viewModel) {
|
private fun handleEditAction() = withState(viewModel) {
|
||||||
val currentAttachment = it.attachments.getOrNull(it.currentAttachmentIndex) ?: return@withState
|
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
|
val uri = currentAttachment.queryUri
|
||||||
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
createUCropWithDefaultSettings(colorProvider, uri, destinationFile.toUri(), currentAttachment.name)
|
||||||
.getIntent(requireContext())
|
.getIntent(requireContext())
|
||||||
|
@ -140,6 +140,12 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDoResetAll() {
|
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)
|
_viewEvents.post(SharedSecureStorageViewEvent.ShowResetBottomSheet)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ class BootstrapCrossSigningTask @Inject constructor(
|
|||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup")
|
||||||
|
|
||||||
// First ensure that in sync
|
// First ensure that in sync
|
||||||
val serverVersion = awaitCallback<KeysVersionResult?> {
|
var serverVersion = awaitCallback<KeysVersionResult?> {
|
||||||
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
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.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown)
|
||||||
|| (params.setupMode == SetupMode.HARD_RESET)
|
|| (params.setupMode == SetupMode.HARD_RESET)
|
||||||
if (shouldCreateKeyBackup) {
|
if (shouldCreateKeyBackup) {
|
||||||
|
// clear all existing backups
|
||||||
|
while (serverVersion != null) {
|
||||||
|
awaitCallback<Unit> {
|
||||||
|
session.cryptoService().keysBackupService().deleteBackup(serverVersion!!.version, it)
|
||||||
|
}
|
||||||
|
serverVersion = awaitCallback {
|
||||||
|
session.cryptoService().keysBackupService().getCurrentVersion(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup")
|
Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup")
|
||||||
val creationInfo = awaitCallback<MegolmBackupCreationInfo> {
|
val creationInfo = awaitCallback<MegolmBackupCreationInfo> {
|
||||||
session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
session.cryptoService().keysBackupService().prepareKeysBackupVersion(null, null, it)
|
||||||
|
@ -164,7 +164,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
|
|||||||
|
|
||||||
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
|
||||||
// If an incoming request is readied (by another device?) we should discard the alert
|
// 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))
|
popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +99,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
|
|||||||
val pr = if (selfVerificationMode) {
|
val pr = if (selfVerificationMode) {
|
||||||
// See if active tx for this user and take it
|
// See if active tx for this user and take it
|
||||||
|
|
||||||
session.cryptoService().verificationService().getExistingVerificationRequest(args.otherUserId)
|
session.cryptoService().verificationService().getExistingVerificationRequests(args.otherUserId)
|
||||||
?.lastOrNull { !it.isFinished }
|
.lastOrNull { !it.isFinished }
|
||||||
?.also { verificationRequest ->
|
?.also { verificationRequest ->
|
||||||
if (verificationRequest.isIncoming && !verificationRequest.isReady) {
|
if (verificationRequest.isIncoming && !verificationRequest.isReady) {
|
||||||
// auto ready in this case, as we are waiting
|
// auto ready in this case, as we are waiting
|
||||||
|
@ -153,20 +153,20 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) {
|
private fun handleSetAvatarAction(action: RoomSettingsAction.SetAvatarAction) {
|
||||||
deletePendingAvatar()
|
setState {
|
||||||
setState { copy(avatarAction = action.avatarAction) }
|
deletePendingAvatar(this)
|
||||||
|
copy(avatarAction = action.avatarAction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deletePendingAvatar() {
|
private fun deletePendingAvatar(state: RoomSettingsViewState) {
|
||||||
// Maybe delete the pending avatar
|
// Maybe delete the pending avatar
|
||||||
withState {
|
(state.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar)
|
||||||
(it.avatarAction as? RoomSettingsViewState.AvatarAction.UpdateAvatar)
|
|
||||||
?.let { tryOrNull { it.newAvatarUri.toFile().delete() } }
|
?.let { tryOrNull { it.newAvatarUri.toFile().delete() } }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun cancel() {
|
private fun cancel() {
|
||||||
deletePendingAvatar()
|
withState { deletePendingAvatar(it) }
|
||||||
|
|
||||||
_viewEvents.post(RoomSettingsViewEvents.GoBack)
|
_viewEvents.post(RoomSettingsViewEvents.GoBack)
|
||||||
}
|
}
|
||||||
@ -209,8 +209,10 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
|||||||
.subscribe(
|
.subscribe(
|
||||||
{
|
{
|
||||||
postLoading(false)
|
postLoading(false)
|
||||||
setState { copy(newHistoryVisibility = null) }
|
setState {
|
||||||
deletePendingAvatar()
|
deletePendingAvatar(this)
|
||||||
|
copy(newHistoryVisibility = null)
|
||||||
|
}
|
||||||
_viewEvents.post(RoomSettingsViewEvents.Success)
|
_viewEvents.post(RoomSettingsViewEvents.Success)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -115,7 +115,9 @@
|
|||||||
|
|
||||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/settings_other">
|
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||||
|
android:key="SETTINGS_SECURITY_OTHER_CATEGORY"
|
||||||
|
android:title="@string/settings_other">
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreference
|
<im.vector.app.core.preference.VectorPreference
|
||||||
android:key="SETTINGS_SECURITY_PIN"
|
android:key="SETTINGS_SECURITY_PIN"
|
||||||
|
Loading…
Reference in New Issue
Block a user