mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Merge branch 'release/0.18.1'
This commit is contained in:
commit
367f793929
15 changed files with 132 additions and 20 deletions
13
CHANGES.md
13
CHANGES.md
|
@ -1,3 +1,16 @@
|
|||
Changes in RiotX 0.18.1 (2020-03-17)
|
||||
===================================================
|
||||
|
||||
Improvements 🙌:
|
||||
- Implementation of /join command
|
||||
|
||||
Bugfix 🐛:
|
||||
- Message transitions in encrypted rooms are jarring #518
|
||||
- Images that failed to send are waiting to be sent forever #1145
|
||||
- Fix / Crashed when trying to send a gif from the Gboard #1136
|
||||
- Fix / Cannot click on key backup banner when new keys are available
|
||||
|
||||
|
||||
Changes in RiotX 0.18.0 (2020-03-11)
|
||||
===================================================
|
||||
|
||||
|
|
|
@ -193,6 +193,7 @@ internal class DefaultSession @Inject constructor(
|
|||
stopAnyBackgroundSync()
|
||||
liveEntityObservers.forEach { it.cancelProcess() }
|
||||
cacheService.get().clearCache(callback)
|
||||
workManagerProvider.cancelAllWorks()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
|
|
@ -77,6 +77,16 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
|||
return Result.success(inputData)
|
||||
}
|
||||
|
||||
// Just defensive code to ensure that we never have an uncaught exception that could break the queue
|
||||
return try {
|
||||
internalDoWork(params)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure)
|
||||
handleFailure(params, failure)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun internalDoWork(params: Params): Result {
|
||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||
sessionComponent.inject(this)
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ import com.squareup.moshi.JsonClass
|
|||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
||||
import im.vector.matrix.android.internal.util.awaitCallback
|
||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||
|
@ -96,6 +99,22 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
|||
type = safeResult.eventType,
|
||||
content = safeResult.eventContent
|
||||
)
|
||||
// Better handling of local echo, to avoid decrypting transition on remote echo
|
||||
// Should I only do it for text messages?
|
||||
if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||
val decryptionLocalEcho = MXEventDecryptionResult(
|
||||
clearEvent = Event(
|
||||
type = localEvent.type,
|
||||
content = localEvent.content,
|
||||
roomId = localEvent.roomId
|
||||
).toContent(),
|
||||
forwardingCurve25519KeyChain = emptyList(),
|
||||
senderCurve25519Key = result.eventContent["sender_key"] as? String,
|
||||
claimedEd25519Key = crypto.getMyDevice().fingerprint()
|
||||
)
|
||||
localEchoUpdater.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho)
|
||||
}
|
||||
|
||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent)
|
||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||
} else {
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
package im.vector.matrix.android.internal.session.room.send
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import timber.log.Timber
|
||||
|
@ -38,4 +42,15 @@ internal class LocalEchoUpdater @Inject constructor(private val monarchy: Monarc
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateEncryptedEcho(eventId: String, encryptedContent: Content, mxEventDecryptionResult: MXEventDecryptionResult) {
|
||||
monarchy.writeAsync { realm ->
|
||||
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
|
||||
if (sendingEventEntity != null) {
|
||||
sendingEventEntity.type = EventType.ENCRYPTED
|
||||
sendingEventEntity.content = ContentMapper.map(encryptedContent)
|
||||
sendingEventEntity.setDecryptionResult(mxEventDecryptionResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.work.OneTimeWorkRequest
|
|||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||
|
@ -49,6 +50,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
|||
|
||||
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
||||
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
|
||||
@Inject lateinit var localEchoUpdater: LocalEchoUpdater
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
Timber.v("Start dispatch sending multiple event work")
|
||||
|
@ -57,14 +59,17 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
|||
Timber.e("Work cancelled due to input error from parent")
|
||||
}
|
||||
|
||||
if (params.lastFailureMessage != null) {
|
||||
// Transmit the error
|
||||
return Result.success(inputData)
|
||||
}
|
||||
|
||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||
sessionComponent.inject(this)
|
||||
|
||||
if (params.lastFailureMessage != null) {
|
||||
params.events.forEach { event ->
|
||||
event.eventId?.let { localEchoUpdater.updateSendState(it, SendState.UNDELIVERED) }
|
||||
}
|
||||
// Transmit the error if needed?
|
||||
return Result.success(inputData)
|
||||
}
|
||||
|
||||
// Create a work for every event
|
||||
params.events.forEach { event ->
|
||||
if (params.isEncrypted) {
|
||||
|
|
|
@ -25,6 +25,8 @@ import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
|||
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
||||
import im.vector.matrix.android.internal.database.helper.addTimelineEvent
|
||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||
|
@ -38,6 +40,7 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo
|
|||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||
|
@ -260,6 +263,14 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
if (sendingEventEntity != null) {
|
||||
Timber.v("Remove local echo for tx:$it")
|
||||
roomEntity.sendingTimelineEvents.remove(sendingEventEntity)
|
||||
if (event.isEncrypted() && event.content?.get("algorithm") as? String == MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||
// updated with echo decryption, to avoid seeing it decrypt again
|
||||
val adapter = MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java)
|
||||
sendingEventEntity.root?.decryptionResultJson?.let { json ->
|
||||
eventEntity.decryptionResultJson = json
|
||||
event.mxDecryptionResult = adapter.fromJson(json)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.v("Can't find corresponding local echo for tx:$it")
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ androidExtensions {
|
|||
|
||||
ext.versionMajor = 0
|
||||
ext.versionMinor = 18
|
||||
ext.versionPatch = 0
|
||||
ext.versionPatch = 1
|
||||
|
||||
static def getGitTimestamp() {
|
||||
def cmd = 'git show -s --format=%ct'
|
||||
|
|
|
@ -123,6 +123,7 @@ class KeysBackupBanner @JvmOverloads constructor(
|
|||
is State.Setup -> {
|
||||
delegate?.setupKeysBackup()
|
||||
}
|
||||
is State.Update,
|
||||
is State.Recover -> {
|
||||
delegate?.recoverKeysBackup()
|
||||
}
|
||||
|
|
|
@ -177,19 +177,28 @@ class AttachmentsHelper private constructor(private val context: Context,
|
|||
fun handleShareIntent(intent: Intent): Boolean {
|
||||
val type = intent.resolveType(context) ?: return false
|
||||
if (type.startsWith("image")) {
|
||||
imagePicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
||||
imagePicker.submit(safeShareIntent(intent))
|
||||
} else if (type.startsWith("video")) {
|
||||
videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
||||
videoPicker.submit(safeShareIntent(intent))
|
||||
} else if (type.startsWith("audio")) {
|
||||
videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
||||
videoPicker.submit(safeShareIntent(intent))
|
||||
} else if (type.startsWith("application") || type.startsWith("file") || type.startsWith("*")) {
|
||||
filePicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
||||
filePicker.submit(safeShareIntent(intent))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun safeShareIntent(intent: Intent): Intent {
|
||||
// Work around for getPickerIntentForSharing doing NPE in android 10
|
||||
return try {
|
||||
IntentUtils.getPickerIntentForSharing(intent)
|
||||
} catch (failure: Throwable) {
|
||||
intent
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPickerManagerForRequestCode(requestCode: Int): PickerManager? {
|
||||
return when (requestCode) {
|
||||
PICK_IMAGE_DEVICE -> imagePicker
|
||||
|
|
|
@ -31,10 +31,12 @@ import im.vector.riotx.core.epoxy.loadingItem
|
|||
import im.vector.riotx.core.resources.StringProvider
|
||||
import im.vector.riotx.core.ui.list.GenericItem
|
||||
import im.vector.riotx.core.ui.list.genericItem
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupSettingsRecyclerViewController @Inject constructor(private val stringProvider: StringProvider,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
@ -149,7 +151,9 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(private val s
|
|||
description(keyVersionResult?.algorithm ?: "")
|
||||
}
|
||||
|
||||
buildKeysBackupTrust(data.keysBackupVersionTrust)
|
||||
if (vectorPreferences.developerMode()) {
|
||||
buildKeysBackupTrust(data.keysBackupVersionTrust)
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
|
|
|
@ -292,16 +292,23 @@ class RoomDetailFragment @Inject constructor(
|
|||
is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
|
||||
is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
|
||||
is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
|
||||
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
||||
is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
|
||||
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
||||
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
||||
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
||||
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
|
||||
is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
|
||||
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
||||
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
||||
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
||||
is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
||||
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleJoinedToAnotherRoom(action: RoomDetailViewEvents.JoinRoomCommandSuccess) {
|
||||
updateComposerText("")
|
||||
lockSendButton = false
|
||||
navigator.openRoom(vectorBaseActivity, action.roomId)
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
if (savedInstanceState == null) {
|
||||
|
|
|
@ -50,6 +50,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
|
|||
abstract class SendMessageResult : RoomDetailViewEvents()
|
||||
|
||||
object MessageSent : SendMessageResult()
|
||||
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()
|
||||
class SlashCommandError(val command: Command) : SendMessageResult()
|
||||
class SlashCommandUnknown(val command: String) : SendMessageResult()
|
||||
data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult()
|
||||
|
|
|
@ -379,8 +379,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented)
|
||||
}
|
||||
is ParsedCommand.JoinRoom -> {
|
||||
// TODO
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented)
|
||||
handleJoinToAnotherRoomSlashCommand(slashCommandResult)
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.PartRoom -> {
|
||||
// TODO
|
||||
|
@ -512,6 +512,22 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
room.deleteDraft(NoOpMatrixCallback())
|
||||
}
|
||||
|
||||
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
||||
session.joinRoom(command.roomAlias, command.reason, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
session.getRoomSummary(command.roomAlias)
|
||||
?.roomId
|
||||
?.let {
|
||||
_viewEvents.post(RoomDetailViewEvents.JoinRoomCommandSuccess(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
||||
val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
|
||||
return buildString {
|
||||
|
|
|
@ -1461,7 +1461,7 @@ Why choose Riot.im?
|
|||
<string name="keys_backup_banner_recover_line1">Never lose encrypted messages</string>
|
||||
<string name="keys_backup_banner_recover_line2">Use Key Backup</string>
|
||||
|
||||
<string name="keys_backup_banner_update_line1">New encrypted messages keys</string>
|
||||
<string name="keys_backup_banner_update_line1">New secure message keys</string>
|
||||
<string name="keys_backup_banner_update_line2">Manage in Key Backup</string>
|
||||
|
||||
<string name="keys_backup_banner_in_progress">Backing up keys…</string>
|
||||
|
|
Loading…
Reference in a new issue