From 8efd389a3cf5f2686331fafea171a35fcd53fce5 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 9 Mar 2022 13:40:05 +0100 Subject: [PATCH 001/368] Fix a case of missing read markers Case: bottom-most loaded event has read marker, but there are messages below it that haven't been loaded yet. --- .../app/features/home/room/detail/TimelineViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 3bdcbc6529..07c25ce326 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -1120,6 +1120,11 @@ class TimelineViewModel @AssistedInject constructor( } else { UnreadState.Unknown } + // If the read marker is at the bottom-most event, this doesn't mean we read all, in case we just haven't loaded more events. + // Avoid incorrectly returning HasNoUnread in this case. + if (firstDisplayableEventIndex == 0 && timeline.hasMoreToLoad(Timeline.Direction.FORWARDS)) { + return UnreadState.Unknown + } for (i in (firstDisplayableEventIndex - 1) downTo 0) { val timelineEvent = events.getOrNull(i) ?: return UnreadState.Unknown val eventId = timelineEvent.root.eventId ?: return UnreadState.Unknown From 1206c31e3ab01501b02953b28140bd25c65017ee Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 9 Mar 2022 13:43:52 +0100 Subject: [PATCH 002/368] Fix another case of missing read markers HasUnread might not be correct on the first try while loading the timeline. --- .../vector/app/features/home/room/detail/TimelineViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 07c25ce326..78e3469a58 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -1099,9 +1099,11 @@ class TimelineViewModel @AssistedInject constructor( computeUnreadState(timelineEvents, roomSummary) } // We don't want live update of unread so we skip when we already had a HasUnread or HasNoUnread + // However, we want to update an existing HasUnread, as we might get additional information during loading of events. .distinctUntilChanged { previous, current -> when { previous is UnreadState.Unknown || previous is UnreadState.ReadMarkerNotLoaded -> false + previous is UnreadState.HasUnread && current is UnreadState.HasUnread -> false current is UnreadState.HasUnread || current is UnreadState.HasNoUnread -> true else -> false } From 884ae1cedd8144e470a1055019d55a4c9eeb7c85 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 9 Mar 2022 13:50:49 +0100 Subject: [PATCH 003/368] Add changelog.d/5475.bugfix --- changelog.d/5475.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5475.bugfix diff --git a/changelog.d/5475.bugfix b/changelog.d/5475.bugfix new file mode 100644 index 0000000000..03364f6a73 --- /dev/null +++ b/changelog.d/5475.bugfix @@ -0,0 +1 @@ +Fix some cases where the read marker line would not show up From 6c4e404ba135eb755235e33d5196695dd3e55719 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 9 Mar 2022 17:56:02 +0100 Subject: [PATCH 004/368] Fix updating unread marker if not to latest chunk SetReadMarkerTask was not updating the read marker when both the old and the new fully read eventId weren't in the last chunk, even when the new one was after the first one. --- changelog.d/5481.bugfix | 1 + .../matrix/android/sdk/internal/database/query/ReadQueries.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/5481.bugfix diff --git a/changelog.d/5481.bugfix b/changelog.d/5481.bugfix new file mode 100644 index 0000000000..64891b503c --- /dev/null +++ b/changelog.d/5481.bugfix @@ -0,0 +1 @@ +Fix sometimes read marker not properly updating diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt index 8cc99c3d2f..cb98029aeb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt @@ -94,7 +94,7 @@ internal fun isReadMarkerMoreRecent(realmConfiguration: RealmConfiguration, val eventToCheckIndex = eventToCheck?.displayIndex ?: Int.MAX_VALUE eventToCheckIndex <= readMarkerIndex } else { - eventToCheckChunk?.isLastForward == false + eventToCheckChunk?.isLastForward == false && readMarkerChunk?.isLastForward == true } } } From 0564942b0cdf7699421cc9ba9583a9460d8bcaa1 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Thu, 10 Mar 2022 20:22:38 +0100 Subject: [PATCH 005/368] isReadMarkerMoreRecent(): use helper to properly compare chunks --- .../matrix/android/sdk/internal/database/query/ReadQueries.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt index cb98029aeb..6c587dfcae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt @@ -94,7 +94,7 @@ internal fun isReadMarkerMoreRecent(realmConfiguration: RealmConfiguration, val eventToCheckIndex = eventToCheck?.displayIndex ?: Int.MAX_VALUE eventToCheckIndex <= readMarkerIndex } else { - eventToCheckChunk?.isLastForward == false && readMarkerChunk?.isLastForward == true + eventToCheckChunk != null && readMarkerChunk?.isMoreRecentThan(eventToCheckChunk) == true } } } From 6ba02629ec461c949d9e7400a630af703d82e6a8 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Fri, 11 Mar 2022 11:22:26 +0100 Subject: [PATCH 006/368] Fix ChunkEntity.isMoreRecentThan() if both chunks linked to last forward Imagine scenario: [this] -> [chunkToCheck] -> [lastForwardChunk] Then, both `isLastForward` checks will not return, and also the `chunkToCheck.doesNextChunksVerifyCondition { it == this }` will return false. Since both chunks are connected to the last forward chunk, `isMoreRecent()` will still return `true`, which is wrong in this case. So do not only check if chunkToCheck has this as any of the next chunks, but also the other way round. --- .../android/sdk/internal/database/helper/ChunkEntityHelper.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index 289db9fa15..8bec2a443c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -223,6 +223,9 @@ internal fun ChunkEntity.isMoreRecentThan(chunkToCheck: ChunkEntity): Boolean { if (chunkToCheck.doesNextChunksVerifyCondition { it == this }) { return true } + if (this.doesNextChunksVerifyCondition { it == chunkToCheck }) { + return false + } // Otherwise check if this chunk is linked to last forward if (this.doesNextChunksVerifyCondition { it.isLastForward }) { return true From 682f4c35d2956efe1e1827ee6834466494ca4b71 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 16 Mar 2022 13:39:19 +0100 Subject: [PATCH 007/368] Fix endless loading timeline due to conflicting chunks --- changelog.d/5554.bugfix | 1 + .../database/query/ChunkEntityQueries.kt | 11 ++++++++++ .../room/timeline/TokenChunkEventPersistor.kt | 22 ++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog.d/5554.bugfix diff --git a/changelog.d/5554.bugfix b/changelog.d/5554.bugfix new file mode 100644 index 0000000000..ee69f0dbfe --- /dev/null +++ b/changelog.d/5554.bugfix @@ -0,0 +1 @@ +Fix sometimes endless loading timeline diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt index ece46555a7..a33ba82f7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt @@ -40,6 +40,17 @@ internal fun ChunkEntity.Companion.find(realm: Realm, roomId: String, prevToken: return query.findFirst() } +internal fun ChunkEntity.Companion.findAll(realm: Realm, roomId: String, prevToken: String? = null, nextToken: String? = null): RealmResults? { + val query = where(realm, roomId) + if (prevToken != null) { + query.equalTo(ChunkEntityFields.PREV_TOKEN, prevToken) + } + if (nextToken != null) { + query.equalTo(ChunkEntityFields.NEXT_TOKEN, nextToken) + } + return query.findAll() +} + internal fun ChunkEntity.Companion.findLastForwardChunkOfRoom(realm: Realm, roomId: String): ChunkEntity? { return where(realm, roomId) .equalTo(ChunkEntityFields.IS_LAST_FORWARD, true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 63383a99b3..874915a6f0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -38,6 +38,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.find +import org.matrix.android.sdk.internal.database.query.findAll import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId @@ -80,7 +81,26 @@ internal class TokenChunkEventPersistor @Inject constructor( val existingChunk = ChunkEntity.find(realm, roomId, prevToken = prevToken, nextToken = nextToken) if (existingChunk != null) { - Timber.v("This chunk is already in the db, returns") + Timber.v("This chunk is already in the db, checking if this might be caused by broken links") + if (direction == PaginationDirection.FORWARDS) { + val prevChunks = ChunkEntity.findAll(realm, roomId, nextToken = prevToken) + Timber.v("Found ${prevChunks?.size} prevChunks") + prevChunks?.forEach { + if (it.nextChunk != existingChunk) { + Timber.i("Set nextChunk for ${it.identifier()} from ${it.nextChunk?.identifier()} to ${existingChunk.identifier()}") + it.nextChunk = existingChunk + } + } + } else { + val nextChunks = ChunkEntity.findAll(realm, roomId, prevToken = nextToken) + Timber.v("Found ${nextChunks?.size} nextChunks") + nextChunks?.forEach { + if (it.prevChunk != existingChunk) { + Timber.i("Set prevChunk for ${it.identifier()} from ${it.prevChunk?.identifier()} to ${existingChunk.identifier()}") + it.prevChunk = existingChunk + } + } + } return@awaitTransaction } val prevChunk = ChunkEntity.find(realm, roomId, nextToken = prevToken) From 00bced95000b80b7d5e8d01f18c298be9a0ee64c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 17 Mar 2022 16:40:35 +0100 Subject: [PATCH 008/368] Changes unTrack to untrack --- .../im/vector/app/features/home/room/detail/TimelineFragment.kt | 2 +- .../room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt | 2 +- .../features/home/room/detail/timeline/item/MessageVoiceItem.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 4c53c32d57..c2ab12595f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1244,7 +1244,7 @@ class TimelineFragment @Inject constructor( override fun onPause() { super.onPause() notificationDrawerManager.setCurrentRoom(null) - voiceMessagePlaybackTracker.unTrack(VoiceMessagePlaybackTracker.RECORDING_ID) + voiceMessagePlaybackTracker.untrack(VoiceMessagePlaybackTracker.RECORDING_ID) if (withState(messageComposerViewModel) { it.isVoiceRecording } && requireActivity().isChangingConfigurations) { // we're rotating, maintain any active recordings diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt index c6204bff1c..c033af1ef5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt @@ -46,7 +46,7 @@ class VoiceMessagePlaybackTracker @Inject constructor() { } } - fun unTrack(id: String) { + fun untrack(id: String) { listeners.remove(id) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index e9f728d976..7a7fd67209 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -128,7 +128,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { super.unbind(holder) contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) contentDownloadStateTrackerBinder.unbind(mxcUrl) - voiceMessagePlaybackTracker.unTrack(attributes.informationData.eventId) + voiceMessagePlaybackTracker.untrack(attributes.informationData.eventId) } override fun getViewStubId() = STUB_ID From cde10ca0325322ee351b540d99c4657a6a4f6476 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 17 Mar 2022 17:58:11 +0100 Subject: [PATCH 009/368] Fixes wrong spellings in Message file and voice items --- .../room/detail/timeline/item/MessageFileItem.kt | 8 ++++---- .../room/detail/timeline/item/MessageVoiceItem.kt | 13 +++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt index 8b6899daee..17d4a87ac6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt @@ -51,10 +51,10 @@ abstract class MessageFileItem : AbsMessageItem() { // var clickListener: ClickListener? = null @EpoxyAttribute - var izLocalFile = false + var isLocalFile = false @EpoxyAttribute - var izDownloaded = false + var isDownloaded = false @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder @@ -66,7 +66,7 @@ abstract class MessageFileItem : AbsMessageItem() { super.bind(holder) renderSendState(holder.fileLayout, holder.filenameView) if (!attributes.informationData.sendState.hasFailed()) { - contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, izLocalFile, holder.progressLayout) + contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { holder.fileImageView.setImageResource(R.drawable.ic_cross) holder.progressLayout.isVisible = false @@ -75,7 +75,7 @@ abstract class MessageFileItem : AbsMessageItem() { if (attributes.informationData.sendState.isSending()) { holder.fileImageView.setImageResource(iconRes) } else { - if (izDownloaded) { + if (isDownloaded) { holder.fileImageView.setImageResource(iconRes) holder.fileDownloadProgress.progress = 0 } else { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index 7a7fd67209..c44ddcd843 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -48,10 +48,10 @@ abstract class MessageVoiceItem : AbsMessageItem() { var waveform: List = emptyList() @EpoxyAttribute - var izLocalFile = false + var isLocalFile = false @EpoxyAttribute - var izDownloaded = false + var isDownloaded = false @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder @@ -69,7 +69,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { super.bind(holder) renderSendState(holder.voiceLayout, null) if (!attributes.informationData.sendState.hasFailed()) { - contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, izLocalFile, holder.progressLayout) + contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_cross) holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_voice_message_unable_to_play) @@ -85,11 +85,11 @@ abstract class MessageVoiceItem : AbsMessageItem() { } } - val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) { + val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) Color.TRANSPARENT - } else { + else ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) - } + holder.voicePlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) holder.voicePlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } @@ -99,6 +99,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { is VoiceMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder) is VoiceMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state) is VoiceMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state) + is VoiceMessagePlaybackTracker.Listener.State.Recording -> Unit } } }) From 6878a973ed48056b4982cea5232ab129f7a49810 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Fri, 18 Mar 2022 07:59:32 +0100 Subject: [PATCH 010/368] TokenChunkEventPersistor: always link all matching chunks The previous fix only works around the issue when it is detected. This may require re-entering the room once when it gets stuck. If we ensure proper linking from the beginning, hopefully we don't run into any issues at all. --- .../session/room/timeline/TokenChunkEventPersistor.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 874915a6f0..7aceeb4a49 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -109,8 +109,14 @@ internal class TokenChunkEventPersistor @Inject constructor( this.nextChunk = nextChunk this.prevChunk = prevChunk } - nextChunk?.prevChunk = currentChunk - prevChunk?.nextChunk = currentChunk + val allNextChunks = ChunkEntity.findAll(realm, roomId, prevToken = nextToken) + val allPrevChunks = ChunkEntity.findAll(realm, roomId, nextToken = prevToken) + allNextChunks?.forEach { + it.prevChunk = currentChunk + } + allPrevChunks?.forEach { + it.nextChunk = currentChunk + } if (receivedChunk.events.isEmpty() && !receivedChunk.hasMore()) { handleReachEnd(roomId, direction, currentChunk) } else { From 26176328c460086dd3d1fb7179c20f209aa1e501 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 18 Mar 2022 11:58:23 +0100 Subject: [PATCH 011/368] Fixes epoxy errors with renamed epoxy attributes --- .../detail/timeline/factory/MessageItemFactory.kt | 12 ++++++------ .../room/detail/timeline/item/MessageFileItem.kt | 2 ++ .../room/detail/timeline/item/MessageVoiceItem.kt | 2 ++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 3189954e20..e70da45216 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -326,8 +326,8 @@ class MessageItemFactory @Inject constructor( } ?: "" return MessageFileItem_() .attributes(attributes) - .izLocalFile(localFilesHelper.isLocalFile(fileUrl)) - .izDownloaded(session.fileService().isFileInCache( + .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) + .isDownloaded(session.fileService().isFileInCache( fileUrl, messageContent.getFileName(), messageContent.mimeType, @@ -368,8 +368,8 @@ class MessageItemFactory @Inject constructor( .waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty()) .playbackControlButtonClickListener(playbackControlButtonClickListener) .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) - .izLocalFile(localFilesHelper.isLocalFile(fileUrl)) - .izDownloaded(session.fileService().isFileInCache( + .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) + .isDownloaded(session.fileService().isFileInCache( fileUrl, messageContent.getFileName(), messageContent.mimeType, @@ -431,8 +431,8 @@ class MessageItemFactory @Inject constructor( return MessageFileItem_() .attributes(attributes) .leftGuideline(avatarSizeProvider.leftGuideline) - .izLocalFile(localFilesHelper.isLocalFile(messageContent.getFileUrl())) - .izDownloaded(session.fileService().isFileInCache(messageContent)) + .isLocalFile(localFilesHelper.isLocalFile(messageContent.getFileUrl())) + .isDownloaded(session.fileService().isFileInCache(messageContent)) .mxcUrl(mxcUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt index 17d4a87ac6..dc39fbfdd7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt @@ -51,9 +51,11 @@ abstract class MessageFileItem : AbsMessageItem() { // var clickListener: ClickListener? = null @EpoxyAttribute + @JvmField var isLocalFile = false @EpoxyAttribute + @JvmField var isDownloaded = false @EpoxyAttribute diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index c44ddcd843..6e176a8fa2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -48,9 +48,11 @@ abstract class MessageVoiceItem : AbsMessageItem() { var waveform: List = emptyList() @EpoxyAttribute + @JvmField var isLocalFile = false @EpoxyAttribute + @JvmField var isDownloaded = false @EpoxyAttribute From d54b465b30994b9a16de7aa2a690d83d81c9a70e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 18 Mar 2022 13:22:49 +0100 Subject: [PATCH 012/368] Uses messageVoiceItem for audio files --- .../timeline/factory/MessageItemFactory.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index e70da45216..9daec9a4c9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -192,7 +192,7 @@ class MessageItemFactory @Inject constructor( if (messageContent.voiceMessageIndicator != null) { buildVoiceMessageItem(params, messageContent, informationData, highlight, attributes) } else { - buildAudioMessageItem(messageContent, informationData, highlight, attributes) + buildAudioMessageItem(params, messageContent, informationData, highlight, attributes) } } is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) @@ -312,11 +312,12 @@ class MessageItemFactory @Inject constructor( .callback(callback) } - private fun buildAudioMessageItem(messageContent: MessageAudioContent, + private fun buildAudioMessageItem(params: TimelineItemFactoryParams, + messageContent: MessageAudioContent, @Suppress("UNUSED_PARAMETER") informationData: MessageInformationData, highlight: Boolean, - attributes: AbsMessageItem.Attributes): MessageFileItem? { + attributes: AbsMessageItem.Attributes): MessageVoiceItem? { val fileUrl = messageContent.getFileUrl()?.let { if (informationData.sentByMe && !informationData.sendState.isSent()) { it @@ -324,8 +325,18 @@ class MessageItemFactory @Inject constructor( it.takeIf { it.isMxcUrl() } } } ?: "" - return MessageFileItem_() + + val playbackControlButtonClickListener: ClickListener = object : ClickListener { + override fun invoke(view: View) { + params.callback?.onVoiceControlButtonClicked(informationData.eventId, messageContent) + } + } + + return MessageVoiceItem_() .attributes(attributes) + .duration(messageContent.audioInfo?.duration ?: 0) + .playbackControlButtonClickListener(playbackControlButtonClickListener) + .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) .isDownloaded(session.fileService().isFileInCache( fileUrl, @@ -338,8 +349,6 @@ class MessageItemFactory @Inject constructor( .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) - .filename(messageContent.body) - .iconRes(R.drawable.ic_headphones) } private fun buildVoiceMessageItem(params: TimelineItemFactoryParams, From fab78c9a6eec4ee28b03e55b7b678e4f0593ceef Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Sat, 19 Mar 2022 18:16:51 +0100 Subject: [PATCH 013/368] Refactors MessageAudioItem to work for both audio files and voice messages --- .../home/room/detail/TimelineFragment.kt | 23 ++++-- .../timeline/factory/MessageItemFactory.kt | 82 ++++++++----------- ...essageVoiceItem.kt => MessageAudioItem.kt} | 67 +++++++++------ .../detail/timeline/item/MessageFileItem.kt | 2 +- ...xml => item_timeline_event_audio_stub.xml} | 20 ++--- .../layout/item_timeline_event_file_stub.xml | 2 +- ...em_timeline_event_view_stubs_container.xml | 4 +- vector/src/main/res/values/strings.xml | 6 ++ 8 files changed, 106 insertions(+), 100 deletions(-) rename vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/{MessageVoiceItem.kt => MessageAudioItem.kt} (64%) rename vector/src/main/res/layout/{item_timeline_event_voice_stub.xml => item_timeline_event_audio_stub.xml} (82%) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index c2ab12595f..a558a1cc87 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -163,7 +163,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem -import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem +import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever @@ -1163,6 +1163,7 @@ class TimelineFragment @Inject constructor( views.composerLayout.views.sendButton.contentDescription = getString(R.string.action_send) } + // TODO: Test this private fun renderSpecialMode(event: TimelineEvent, @DrawableRes iconRes: Int, @StringRes descriptionRes: Int, @@ -1175,13 +1176,17 @@ class TimelineFragment @Inject constructor( } val messageContent: MessageContent? = event.getLastMessageContent() - val nonFormattedBody = if (messageContent is MessageAudioContent && messageContent.voiceMessageIndicator != null) { - val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) - getString(R.string.voice_message_reply_content, formattedDuration) - } else if (messageContent is MessagePollContent) { - messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() - } else { - messageContent?.body ?: "" + val nonFormattedBody = when { + messageContent is MessageAudioContent && messageContent.voiceMessageIndicator != null -> { + val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) + getString(R.string.voice_message_reply_content, formattedDuration) + } + messageContent is MessagePollContent -> { + messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() + } + else -> { + messageContent?.body ?: "" + } } var formattedBody: CharSequence? = null if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { @@ -1372,7 +1377,7 @@ class TimelineFragment @Inject constructor( } return when (model) { is MessageFileItem, - is MessageVoiceItem, + is MessageAudioItem, is MessageImageVideoItem, is MessageTextItem -> { return (model as AbsMessageItem).attributes.informationData.sendState == SendState.SYNCED diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 9daec9a4c9..71e546e5f0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -43,6 +43,8 @@ import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttrib import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem +import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem +import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem @@ -52,8 +54,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_ -import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem -import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem_ import im.vector.app.features.home.room.detail.timeline.item.PollItem import im.vector.app.features.home.room.detail.timeline.item.PollItem_ import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState @@ -96,7 +96,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.session.room.model.message.PollType -import org.matrix.android.sdk.api.session.room.model.message.getFileName import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent @@ -314,36 +313,18 @@ class MessageItemFactory @Inject constructor( private fun buildAudioMessageItem(params: TimelineItemFactoryParams, messageContent: MessageAudioContent, - @Suppress("UNUSED_PARAMETER") informationData: MessageInformationData, highlight: Boolean, - attributes: AbsMessageItem.Attributes): MessageVoiceItem? { - val fileUrl = messageContent.getFileUrl()?.let { - if (informationData.sentByMe && !informationData.sendState.isSent()) { - it - } else { - it.takeIf { it.isMxcUrl() } - } - } ?: "" + attributes: AbsMessageItem.Attributes): MessageAudioItem { + val fileUrl = getAudioFileUrl(messageContent, informationData) + val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params) - val playbackControlButtonClickListener: ClickListener = object : ClickListener { - override fun invoke(view: View) { - params.callback?.onVoiceControlButtonClicked(informationData.eventId, messageContent) - } - } - - return MessageVoiceItem_() + return MessageAudioItem_() .attributes(attributes) .duration(messageContent.audioInfo?.duration ?: 0) .playbackControlButtonClickListener(playbackControlButtonClickListener) .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) - .isDownloaded(session.fileService().isFileInCache( - fileUrl, - messageContent.getFileName(), - messageContent.mimeType, - messageContent.encryptedFileInfo?.toElementToDecrypt()) - ) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) @@ -351,39 +332,42 @@ class MessageItemFactory @Inject constructor( .leftGuideline(avatarSizeProvider.leftGuideline) } + private fun getAudioFileUrl( + messageContent: MessageAudioContent, + informationData: MessageInformationData, + ) = messageContent.getFileUrl()?.let { + if (informationData.sentByMe && !informationData.sendState.isSent()) { + it + } else { + it.takeIf { it.isMxcUrl() } + } + } ?: "" + + private fun createOnPlaybackButtonClickListener( + messageContent: MessageAudioContent, + informationData: MessageInformationData, + params: TimelineItemFactoryParams, + ) = object : ClickListener { + override fun invoke(view: View) { + params.callback?.onVoiceControlButtonClicked(informationData.eventId, messageContent) + } + } + private fun buildVoiceMessageItem(params: TimelineItemFactoryParams, messageContent: MessageAudioContent, - @Suppress("UNUSED_PARAMETER") informationData: MessageInformationData, highlight: Boolean, - attributes: AbsMessageItem.Attributes): MessageVoiceItem? { - val fileUrl = messageContent.getFileUrl()?.let { - if (informationData.sentByMe && !informationData.sendState.isSent()) { - it - } else { - it.takeIf { it.isMxcUrl() } - } - } ?: "" + attributes: AbsMessageItem.Attributes): MessageAudioItem { + val fileUrl = getAudioFileUrl(messageContent, informationData) + val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params) - val playbackControlButtonClickListener: ClickListener = object : ClickListener { - override fun invoke(view: View) { - params.callback?.onVoiceControlButtonClicked(informationData.eventId, messageContent) - } - } - - return MessageVoiceItem_() + return MessageAudioItem_() .attributes(attributes) .duration(messageContent.audioWaveformInfo?.duration ?: 0) .waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty()) .playbackControlButtonClickListener(playbackControlButtonClickListener) .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) - .isDownloaded(session.fileService().isFileInCache( - fileUrl, - messageContent.getFileName(), - messageContent.mimeType, - messageContent.encryptedFileInfo?.toElementToDecrypt()) - ) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) @@ -432,10 +416,8 @@ class MessageItemFactory @Inject constructor( } private fun buildFileMessageItem(messageContent: MessageFileContent, -// informationData: MessageInformationData, highlight: Boolean, -// callback: TimelineEventController.Callback?, - attributes: AbsMessageItem.Attributes): MessageFileItem? { + attributes: AbsMessageItem.Attributes): MessageFileItem { val mxcUrl = messageContent.getFileUrl() ?: "" return MessageFileItem_() .attributes(attributes) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt similarity index 64% rename from vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt rename to vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 6e176a8fa2..e2fac439f7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.item +import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.text.format.DateUtils @@ -36,7 +37,7 @@ import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLay import im.vector.app.features.themes.ThemeUtils @EpoxyModelClass(layout = R.layout.item_timeline_event_base) -abstract class MessageVoiceItem : AbsMessageItem() { +abstract class MessageAudioItem : AbsMessageItem() { @EpoxyAttribute var mxcUrl: String = "" @@ -53,7 +54,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { @EpoxyAttribute @JvmField - var isDownloaded = false + var isVoiceMessage = false @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder @@ -69,21 +70,21 @@ abstract class MessageVoiceItem : AbsMessageItem() { override fun bind(holder: Holder) { super.bind(holder) - renderSendState(holder.voiceLayout, null) + renderSendState(holder.audioLayout, null) if (!attributes.informationData.sendState.hasFailed()) { contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { - holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_cross) - holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_voice_message_unable_to_play) + holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_cross) + holder.audioPlaybackControlButton.contentDescription = getUnableToPlayContentDescription(holder.view.context) holder.progressLayout.isVisible = false } - holder.voicePlaybackWaveform.setOnLongClickListener(attributes.itemLongClickListener) + holder.audioPlaybackWaveform.setOnLongClickListener(attributes.itemLongClickListener) - holder.voicePlaybackWaveform.post { - holder.voicePlaybackWaveform.recreate() + holder.audioPlaybackWaveform.post { + holder.audioPlaybackWaveform.recreate() waveform.forEach { amplitude -> - holder.voicePlaybackWaveform.update(amplitude) + holder.audioPlaybackWaveform.update(amplitude) } } @@ -92,8 +93,8 @@ abstract class MessageVoiceItem : AbsMessageItem() { else ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) - holder.voicePlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) - holder.voicePlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } + holder.audioPlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) + holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } voiceMessagePlaybackTracker.track(attributes.informationData.eventId, object : VoiceMessagePlaybackTracker.Listener { override fun onUpdate(state: VoiceMessagePlaybackTracker.Listener.State) { @@ -107,22 +108,34 @@ abstract class MessageVoiceItem : AbsMessageItem() { }) } + private fun getUnableToPlayContentDescription(context: Context) = context.getString( + if (isVoiceMessage) R.string.error_voice_message_unable_to_play else R.string.error_audio_message_unable_to_play + ) + private fun renderIdleState(holder: Holder) { - holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message) - holder.voicePlaybackTime.text = formatPlaybackTime(duration) + holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) + holder.audioPlaybackControlButton.contentDescription = getPlayMessageContentDescription(holder.view.context) + holder.audioPlaybackTime.text = formatPlaybackTime(duration) } + private fun getPlayMessageContentDescription(context: Context) = context.getString( + if (isVoiceMessage) R.string.a11y_play_voice_message else R.string.a11y_play_audio_message + ) + private fun renderPlayingState(holder: Holder, state: VoiceMessagePlaybackTracker.Listener.State.Playing) { - holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) - holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_pause_voice_message) - holder.voicePlaybackTime.text = formatPlaybackTime(state.playbackTime) + holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) + holder.audioPlaybackControlButton.contentDescription = getPauseMessageContentDescription(holder.view.context) + holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } + private fun getPauseMessageContentDescription(context: Context) = context.getString( + if (isVoiceMessage) R.string.a11y_pause_voice_message else R.string.a11y_pause_audio_message + ) + private fun renderPausedState(holder: Holder, state: VoiceMessagePlaybackTracker.Listener.State.Paused) { - holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message) - holder.voicePlaybackTime.text = formatPlaybackTime(state.playbackTime) + holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) + holder.audioPlaybackControlButton.contentDescription = getPlayMessageContentDescription(holder.view.context) + holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } private fun formatPlaybackTime(time: Int) = DateUtils.formatElapsedTime((time / 1000).toLong()) @@ -137,15 +150,15 @@ abstract class MessageVoiceItem : AbsMessageItem() { override fun getViewStubId() = STUB_ID class Holder : AbsMessageItem.Holder(STUB_ID) { - val voicePlaybackLayout by bind(R.id.voicePlaybackLayout) - val voiceLayout by bind(R.id.voiceLayout) - val voicePlaybackControlButton by bind(R.id.voicePlaybackControlButton) - val voicePlaybackTime by bind(R.id.voicePlaybackTime) - val voicePlaybackWaveform by bind(R.id.voicePlaybackWaveform) - val progressLayout by bind(R.id.messageFileUploadProgressLayout) + val audioPlaybackLayout by bind(R.id.audioPlaybackLayout) + val audioLayout by bind(R.id.audioLayout) + val audioPlaybackControlButton by bind(R.id.audioPlaybackControlButton) + val audioPlaybackTime by bind(R.id.audioPlaybackTime) + val audioPlaybackWaveform by bind(R.id.audioPlaybackWaveform) + val progressLayout by bind(R.id.audioFileUploadProgressLayout) } companion object { - private const val STUB_ID = R.id.messageContentVoiceStub + private const val STUB_ID = R.id.messageContentAudioStub } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt index dc39fbfdd7..dbd232bfaf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt @@ -109,7 +109,7 @@ abstract class MessageFileItem : AbsMessageItem() { class Holder : AbsMessageItem.Holder(STUB_ID) { val mainLayout by bind(R.id.messageFileMainLayout) - val progressLayout by bind(R.id.messageFileUploadProgressLayout) + val progressLayout by bind(R.id.audioFileUploadProgressLayout) val fileLayout by bind(R.id.messageFileLayout) val fileImageView by bind(R.id.messageFileIconView) val fileImageWrapper by bind(R.id.messageFileImageView) diff --git a/vector/src/main/res/layout/item_timeline_event_voice_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml similarity index 82% rename from vector/src/main/res/layout/item_timeline_event_voice_stub.xml rename to vector/src/main/res/layout/item_timeline_event_audio_stub.xml index a180afbf8e..e8b3b80073 100644 --- a/vector/src/main/res/layout/item_timeline_event_voice_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -2,7 +2,7 @@ Cannot reply or edit while voice message is active Voice Message (%1$s) + Play Audio Message + Pause Audio Message + Pause Audio Message + Cannot reply or edit while audio message is active + Audio Message (%1$s) + Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. From 2e716cb8a043ccba3e706f88db98c66cfd9c2407 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Sat, 19 Mar 2022 18:26:37 +0100 Subject: [PATCH 014/368] Adapts special body text for audio messages --- .../app/features/home/room/detail/TimelineFragment.kt | 11 +++++++++++ vector/src/main/res/values/strings.xml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index a558a1cc87..8fad376a0b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1177,6 +1177,9 @@ class TimelineFragment @Inject constructor( val messageContent: MessageContent? = event.getLastMessageContent() val nonFormattedBody = when { + messageContent is MessageAudioContent -> { + getAudioContentBodyText(messageContent) + } messageContent is MessageAudioContent && messageContent.voiceMessageIndicator != null -> { val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) getString(R.string.voice_message_reply_content, formattedDuration) @@ -1225,6 +1228,14 @@ class TimelineFragment @Inject constructor( focusComposerAndShowKeyboard() } + private fun getAudioContentBodyText(messageContent: MessageAudioContent): String { + val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) + return if (messageContent.voiceMessageIndicator != null) + getString(R.string.voice_message_reply_content, formattedDuration) + else + getString(R.string.audio_message_reply_content, messageContent.body, formattedDuration) + } + override fun onResume() { super.onResume() notificationDrawerManager.setCurrentRoom(timelineArgs.roomId) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 83b2bba5fb..0cc9795589 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2862,7 +2862,7 @@ Pause Audio Message Pause Audio Message Cannot reply or edit while audio message is active - Audio Message (%1$s) + %1$s (%2$s) Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. From 7a7d36d010ccfdf2a039b7ad28768bb1c8200e2d Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 21 Mar 2022 16:23:19 +0100 Subject: [PATCH 015/368] Renames functions and variables --- .../home/room/detail/RoomDetailActivity.kt | 8 +++---- .../home/room/detail/TimelineFragment.kt | 8 +++---- .../detail/composer/VoiceMessageHelper.kt | 14 ++++++------ .../voice/VoiceMessageRecorderView.kt | 14 ++++++------ .../composer/voice/VoiceMessageViews.kt | 4 ++-- .../timeline/factory/MessageItemFactory.kt | 8 +++---- ...cker.kt => AudioMessagePlaybackTracker.kt} | 4 ++-- .../detail/timeline/item/MessageAudioItem.kt | 22 +++++++++---------- 8 files changed, 41 insertions(+), 41 deletions(-) rename vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/{VoiceMessagePlaybackTracker.kt => AudioMessagePlaybackTracker.kt} (97%) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index aa4ee825dc..5784e6e264 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -38,7 +38,7 @@ import im.vector.app.databinding.ActivityRoomDetailBinding import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment import im.vector.app.features.home.room.detail.arguments.TimelineArgs -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.matrixto.MatrixToBottomSheet import im.vector.app.features.navigation.Navigator import im.vector.app.features.room.RequireActiveMembershipAction @@ -75,7 +75,7 @@ class RoomDetailActivity : } private var lastKnownPlayingOrRecordingState: Boolean? = null - private val playbackActivityListener = VoiceMessagePlaybackTracker.ActivityListener { isPlayingOrRecording -> + private val playbackActivityListener = AudioMessagePlaybackTracker.ActivityListener { isPlayingOrRecording -> if (lastKnownPlayingOrRecordingState == isPlayingOrRecording) return@ActivityListener when (isPlayingOrRecording) { true -> keepScreenOn() @@ -86,7 +86,7 @@ class RoomDetailActivity : override fun getCoordinatorLayout() = views.coordinatorLayout - @Inject lateinit var playbackTracker: VoiceMessagePlaybackTracker + @Inject lateinit var playbackTracker: AudioMessagePlaybackTracker private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel private val requireActiveMembershipViewModel: RequireActiveMembershipViewModel by viewModel() @@ -152,7 +152,7 @@ class RoomDetailActivity : override fun onDestroy() { supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) views.drawerLayout.removeDrawerListener(drawerListener) - playbackTracker.unTrackActivity(playbackActivityListener) + playbackTracker.untrackActivity(playbackActivityListener) super.onDestroy() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 8fad376a0b..aa08ba2b93 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -156,7 +156,7 @@ import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBot import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem @@ -259,7 +259,7 @@ class TimelineFragment @Inject constructor( private val roomDetailPendingActionStore: RoomDetailPendingActionStore, private val pillsPostProcessorFactory: PillsPostProcessor.Factory, private val callManager: WebRtcCallManager, - private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker, + private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker, private val clock: Clock ) : VectorBaseFragment(), @@ -726,7 +726,7 @@ class TimelineFragment @Inject constructor( } private fun setupVoiceMessageView() { - voiceMessagePlaybackTracker.track(VoiceMessagePlaybackTracker.RECORDING_ID, views.voiceMessageRecorderView) + audioMessagePlaybackTracker.track(AudioMessagePlaybackTracker.RECORDING_ID, views.voiceMessageRecorderView) views.voiceMessageRecorderView.callback = object : VoiceMessageRecorderView.Callback { override fun onVoiceRecordingStarted() { @@ -1260,7 +1260,7 @@ class TimelineFragment @Inject constructor( override fun onPause() { super.onPause() notificationDrawerManager.setCurrentRoom(null) - voiceMessagePlaybackTracker.untrack(VoiceMessagePlaybackTracker.RECORDING_ID) + audioMessagePlaybackTracker.untrack(AudioMessagePlaybackTracker.RECORDING_ID) if (withState(messageComposerViewModel) { it.isVoiceRecording } && requireActivity().isChangingConfigurations) { // we're rotating, maintain any active recordings diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt index 735d356476..bede7a3ce7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt @@ -21,7 +21,7 @@ import android.media.AudioAttributes import android.media.MediaPlayer import androidx.core.content.FileProvider import im.vector.app.BuildConfig -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.voice.VoiceFailure import im.vector.app.features.voice.VoiceRecorder import im.vector.app.features.voice.VoiceRecorderProvider @@ -42,7 +42,7 @@ import javax.inject.Inject */ class VoiceMessageHelper @Inject constructor( private val context: Context, - private val playbackTracker: VoiceMessagePlaybackTracker, + private val playbackTracker: AudioMessagePlaybackTracker, voiceRecorderProvider: VoiceRecorderProvider ) { private var mediaPlayer: MediaPlayer? = null @@ -58,7 +58,7 @@ class VoiceMessageHelper @Inject constructor( amplitudeList.clear() attachmentData.waveform?.let { amplitudeList.addAll(it) - playbackTracker.updateCurrentRecording(VoiceMessagePlaybackTracker.RECORDING_ID, amplitudeList) + playbackTracker.updateCurrentRecording(AudioMessagePlaybackTracker.RECORDING_ID, amplitudeList) } } @@ -127,14 +127,14 @@ class VoiceMessageHelper @Inject constructor( fun startOrPauseRecordingPlayback() { voiceRecorder.getCurrentRecord()?.let { - startOrPausePlayback(VoiceMessagePlaybackTracker.RECORDING_ID, it) + startOrPausePlayback(AudioMessagePlaybackTracker.RECORDING_ID, it) } } fun startOrPausePlayback(id: String, file: File) { stopPlayback() stopRecordingAmplitudes() - if (playbackTracker.getPlaybackState(id) is VoiceMessagePlaybackTracker.Listener.State.Playing) { + if (playbackTracker.getPlaybackState(id) is AudioMessagePlaybackTracker.Listener.State.Playing) { playbackTracker.pausePlayback(id) } else { startPlayback(id, file) @@ -169,7 +169,7 @@ class VoiceMessageHelper @Inject constructor( } fun stopPlayback() { - playbackTracker.stopPlayback(VoiceMessagePlaybackTracker.RECORDING_ID) + playbackTracker.stopPlayback(AudioMessagePlaybackTracker.RECORDING_ID) mediaPlayer?.stop() stopPlaybackTicker() } @@ -190,7 +190,7 @@ class VoiceMessageHelper @Inject constructor( try { val maxAmplitude = voiceRecorder.getMaxAmplitude() amplitudeList.add(maxAmplitude) - playbackTracker.updateCurrentRecording(VoiceMessagePlaybackTracker.RECORDING_ID, amplitudeList) + playbackTracker.updateCurrentRecording(AudioMessagePlaybackTracker.RECORDING_ID, amplitudeList) } catch (e: IllegalStateException) { Timber.e(e, "Cannot get max amplitude. Amplitude recording timer will be stopped.") stopRecordingAmplitudes() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt index 9a643796a9..a9f6e33d50 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt @@ -28,7 +28,7 @@ import im.vector.app.core.hardware.vibrate import im.vector.app.core.time.Clock import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.ViewVoiceMessageRecorderBinding -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.lib.core.utils.timer.CountUpTimer import javax.inject.Inject import kotlin.math.floor @@ -41,7 +41,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 -) : ConstraintLayout(context, attrs, defStyleAttr), VoiceMessagePlaybackTracker.Listener { +) : ConstraintLayout(context, attrs, defStyleAttr), AudioMessagePlaybackTracker.Listener { interface Callback { fun onVoiceRecordingStarted() @@ -207,16 +207,16 @@ class VoiceMessageRecorderView @JvmOverloads constructor( recordingTicker = null } - override fun onUpdate(state: VoiceMessagePlaybackTracker.Listener.State) { + override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { when (state) { - is VoiceMessagePlaybackTracker.Listener.State.Recording -> { + is AudioMessagePlaybackTracker.Listener.State.Recording -> { voiceMessageViews.renderRecordingWaveform(state.amplitudeList.toTypedArray()) } - is VoiceMessagePlaybackTracker.Listener.State.Playing -> { + is AudioMessagePlaybackTracker.Listener.State.Playing -> { voiceMessageViews.renderPlaying(state) } - is VoiceMessagePlaybackTracker.Listener.State.Paused, - is VoiceMessagePlaybackTracker.Listener.State.Idle -> { + is AudioMessagePlaybackTracker.Listener.State.Paused, + is AudioMessagePlaybackTracker.Listener.State.Idle -> { voiceMessageViews.renderIdle() } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt index 09284ea5fc..5070785569 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt @@ -36,7 +36,7 @@ import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.ViewVoiceMessageRecorderBinding import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.DraggingState import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.RecordingUiState -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker class VoiceMessageViews( private val resources: Resources, @@ -287,7 +287,7 @@ class VoiceMessageViews( views.voicePlaybackWaveform.post { views.voicePlaybackWaveform.recreate() } } - fun renderPlaying(state: VoiceMessagePlaybackTracker.Listener.State.Playing) { + fun renderPlaying(state: AudioMessagePlaybackTracker.Listener.State.Playing) { views.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) views.voicePlaybackControlButton.contentDescription = resources.getString(R.string.a11y_pause_voice_message) val formattedTimerText = DateUtils.formatElapsedTime((state.playbackTime / 1000).toLong()) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 71e546e5f0..30e36d33d3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -41,7 +41,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem_ @@ -126,7 +126,7 @@ class MessageItemFactory @Inject constructor( private val lightweightSettingsStorage: LightweightSettingsStorage, private val spanUtils: SpanUtils, private val session: Session, - private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker, + private val audioMessagePlaybackTracker: AudioMessagePlaybackTracker, private val locationPinProvider: LocationPinProvider, private val vectorPreferences: VectorPreferences, private val urlMapProvider: UrlMapProvider, @@ -323,7 +323,7 @@ class MessageItemFactory @Inject constructor( .attributes(attributes) .duration(messageContent.audioInfo?.duration ?: 0) .playbackControlButtonClickListener(playbackControlButtonClickListener) - .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) + .audioMessagePlaybackTracker(audioMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) @@ -366,7 +366,7 @@ class MessageItemFactory @Inject constructor( .duration(messageContent.audioWaveformInfo?.duration ?: 0) .waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty()) .playbackControlButtonClickListener(playbackControlButtonClickListener) - .voiceMessagePlaybackTracker(voiceMessagePlaybackTracker) + .audioMessagePlaybackTracker(audioMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt similarity index 97% rename from vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt rename to vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt index c033af1ef5..774b0d797e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/VoiceMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt @@ -22,7 +22,7 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class VoiceMessagePlaybackTracker @Inject constructor() { +class AudioMessagePlaybackTracker @Inject constructor() { private val mainHandler = Handler(Looper.getMainLooper()) private val listeners = mutableMapOf() @@ -33,7 +33,7 @@ class VoiceMessagePlaybackTracker @Inject constructor() { activityListeners.add(listener) } - fun unTrackActivity(listener: ActivityListener) { + fun untrackActivity(listener: ActivityListener) { activityListeners.remove(listener) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index e2fac439f7..29f6ab7d62 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -32,7 +32,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder -import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.themes.ThemeUtils @@ -66,7 +66,7 @@ abstract class MessageAudioItem : AbsMessageItem() { var playbackControlButtonClickListener: ClickListener? = null @EpoxyAttribute - lateinit var voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker + lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker override fun bind(holder: Holder) { super.bind(holder) @@ -96,13 +96,13 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.audioPlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } - voiceMessagePlaybackTracker.track(attributes.informationData.eventId, object : VoiceMessagePlaybackTracker.Listener { - override fun onUpdate(state: VoiceMessagePlaybackTracker.Listener.State) { + audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { + override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { when (state) { - is VoiceMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder) - is VoiceMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state) - is VoiceMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state) - is VoiceMessagePlaybackTracker.Listener.State.Recording -> Unit + is AudioMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder) + is AudioMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state) + is AudioMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state) + is AudioMessagePlaybackTracker.Listener.State.Recording -> Unit } } }) @@ -122,7 +122,7 @@ abstract class MessageAudioItem : AbsMessageItem() { if (isVoiceMessage) R.string.a11y_play_voice_message else R.string.a11y_play_audio_message ) - private fun renderPlayingState(holder: Holder, state: VoiceMessagePlaybackTracker.Listener.State.Playing) { + private fun renderPlayingState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Playing) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) holder.audioPlaybackControlButton.contentDescription = getPauseMessageContentDescription(holder.view.context) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) @@ -132,7 +132,7 @@ abstract class MessageAudioItem : AbsMessageItem() { if (isVoiceMessage) R.string.a11y_pause_voice_message else R.string.a11y_pause_audio_message ) - private fun renderPausedState(holder: Holder, state: VoiceMessagePlaybackTracker.Listener.State.Paused) { + private fun renderPausedState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Paused) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) holder.audioPlaybackControlButton.contentDescription = getPlayMessageContentDescription(holder.view.context) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) @@ -144,7 +144,7 @@ abstract class MessageAudioItem : AbsMessageItem() { super.unbind(holder) contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) contentDownloadStateTrackerBinder.unbind(mxcUrl) - voiceMessagePlaybackTracker.untrack(attributes.informationData.eventId) + audioMessagePlaybackTracker.untrack(attributes.informationData.eventId) } override fun getViewStubId() = STUB_ID From ff26829d652c4db967986027a3f81316eb46c608 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 21 Mar 2022 18:42:07 +0100 Subject: [PATCH 016/368] Adds new audio timeline stub --- .../timeline/factory/MessageItemFactory.kt | 9 +- .../detail/timeline/item/MessageAudioItem.kt | 67 +++----- .../detail/timeline/item/MessageFileItem.kt | 10 +- .../detail/timeline/item/MessageVoiceItem.kt | 150 ++++++++++++++++++ .../layout/item_timeline_event_audio_stub.xml | 57 +++---- .../layout/item_timeline_event_file_stub.xml | 2 +- ...em_timeline_event_view_stubs_container.xml | 7 + .../layout/item_timeline_event_voice_stub.xml | 69 ++++++++ 8 files changed, 292 insertions(+), 79 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt create mode 100644 vector/src/main/res/layout/item_timeline_event_voice_stub.xml diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 30e36d33d3..b8d7af2d80 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -54,6 +54,8 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem import im.vector.app.features.home.room.detail.timeline.item.MessageLocationItem_ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem_ +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem_ import im.vector.app.features.home.room.detail.timeline.item.PollItem import im.vector.app.features.home.room.detail.timeline.item.PollItem_ import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState @@ -321,6 +323,7 @@ class MessageItemFactory @Inject constructor( return MessageAudioItem_() .attributes(attributes) + .filename(messageContent.body) .duration(messageContent.audioInfo?.duration ?: 0) .playbackControlButtonClickListener(playbackControlButtonClickListener) .audioMessagePlaybackTracker(audioMessagePlaybackTracker) @@ -357,16 +360,16 @@ class MessageItemFactory @Inject constructor( messageContent: MessageAudioContent, informationData: MessageInformationData, highlight: Boolean, - attributes: AbsMessageItem.Attributes): MessageAudioItem { + attributes: AbsMessageItem.Attributes): MessageVoiceItem { val fileUrl = getAudioFileUrl(messageContent, informationData) val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params) - return MessageAudioItem_() + return MessageVoiceItem_() .attributes(attributes) .duration(messageContent.audioWaveformInfo?.duration ?: 0) .waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty()) .playbackControlButtonClickListener(playbackControlButtonClickListener) - .audioMessagePlaybackTracker(audioMessagePlaybackTracker) + .voiceMessagePlaybackTracker(audioMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 29f6ab7d62..101a837b3f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -16,18 +16,15 @@ package im.vector.app.features.home.room.detail.timeline.item -import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.text.format.DateUtils -import android.view.View import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass -import com.visualizer.amplitude.AudioRecordView import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder @@ -39,23 +36,19 @@ import im.vector.app.features.themes.ThemeUtils @EpoxyModelClass(layout = R.layout.item_timeline_event_base) abstract class MessageAudioItem : AbsMessageItem() { + @EpoxyAttribute + var filename: String = "" + @EpoxyAttribute var mxcUrl: String = "" @EpoxyAttribute var duration: Int = 0 - @EpoxyAttribute - var waveform: List = emptyList() - @EpoxyAttribute @JvmField var isLocalFile = false - @EpoxyAttribute - @JvmField - var isVoiceMessage = false - @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder @@ -70,32 +63,34 @@ abstract class MessageAudioItem : AbsMessageItem() { override fun bind(holder: Holder) { super.bind(holder) - renderSendState(holder.audioLayout, null) + renderSendState(holder.rootLayout, null) + bindUploadState(holder) + holder.filenameView.text = filename + applyLayoutTint(holder) + holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } + renderStateBasedOnAudioPlayback(holder) + } + + private fun bindUploadState(holder: Holder) { if (!attributes.informationData.sendState.hasFailed()) { contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_cross) - holder.audioPlaybackControlButton.contentDescription = getUnableToPlayContentDescription(holder.view.context) + holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_audio_message_unable_to_play) holder.progressLayout.isVisible = false } + } - holder.audioPlaybackWaveform.setOnLongClickListener(attributes.itemLongClickListener) - - holder.audioPlaybackWaveform.post { - holder.audioPlaybackWaveform.recreate() - waveform.forEach { amplitude -> - holder.audioPlaybackWaveform.update(amplitude) - } - } - + private fun applyLayoutTint(holder: Holder) { val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) Color.TRANSPARENT else ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) - holder.audioPlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) - holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } + holder.mainLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) + } + private fun renderStateBasedOnAudioPlayback(holder: Holder) { audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { when (state) { @@ -108,33 +103,21 @@ abstract class MessageAudioItem : AbsMessageItem() { }) } - private fun getUnableToPlayContentDescription(context: Context) = context.getString( - if (isVoiceMessage) R.string.error_voice_message_unable_to_play else R.string.error_audio_message_unable_to_play - ) - private fun renderIdleState(holder: Holder) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.audioPlaybackControlButton.contentDescription = getPlayMessageContentDescription(holder.view.context) + holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message) holder.audioPlaybackTime.text = formatPlaybackTime(duration) } - private fun getPlayMessageContentDescription(context: Context) = context.getString( - if (isVoiceMessage) R.string.a11y_play_voice_message else R.string.a11y_play_audio_message - ) - private fun renderPlayingState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Playing) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) - holder.audioPlaybackControlButton.contentDescription = getPauseMessageContentDescription(holder.view.context) + holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_pause_audio_message) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } - private fun getPauseMessageContentDescription(context: Context) = context.getString( - if (isVoiceMessage) R.string.a11y_pause_voice_message else R.string.a11y_pause_audio_message - ) - private fun renderPausedState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Paused) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.audioPlaybackControlButton.contentDescription = getPlayMessageContentDescription(holder.view.context) + holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } @@ -150,12 +133,12 @@ abstract class MessageAudioItem : AbsMessageItem() { override fun getViewStubId() = STUB_ID class Holder : AbsMessageItem.Holder(STUB_ID) { - val audioPlaybackLayout by bind(R.id.audioPlaybackLayout) - val audioLayout by bind(R.id.audioLayout) + val rootLayout by bind(R.id.messageRootLayout) + val mainLayout by bind(R.id.messageMainInnerLayout) + val filenameView by bind(R.id.messageFilenameView) val audioPlaybackControlButton by bind(R.id.audioPlaybackControlButton) val audioPlaybackTime by bind(R.id.audioPlaybackTime) - val audioPlaybackWaveform by bind(R.id.audioPlaybackWaveform) - val progressLayout by bind(R.id.audioFileUploadProgressLayout) + val progressLayout by bind(R.id.messageFileUploadProgressLayout) } companion object { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt index dbd232bfaf..8a94f927f9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt @@ -47,9 +47,6 @@ abstract class MessageFileItem : AbsMessageItem() { @DrawableRes var iconRes: Int = 0 -// @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) -// var clickListener: ClickListener? = null - @EpoxyAttribute @JvmField var isLocalFile = false @@ -67,13 +64,16 @@ abstract class MessageFileItem : AbsMessageItem() { override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.fileLayout, holder.filenameView) + if (!attributes.informationData.sendState.hasFailed()) { contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { holder.fileImageView.setImageResource(R.drawable.ic_cross) holder.progressLayout.isVisible = false } + holder.filenameView.text = filename + if (attributes.informationData.sendState.isSending()) { holder.fileImageView.setImageResource(iconRes) } else { @@ -85,7 +85,7 @@ abstract class MessageFileItem : AbsMessageItem() { holder.fileImageView.setImageResource(R.drawable.ic_download) } } -// holder.view.setOnClickListener(clickListener) + val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) { Color.TRANSPARENT } else { @@ -109,7 +109,7 @@ abstract class MessageFileItem : AbsMessageItem() { class Holder : AbsMessageItem.Holder(STUB_ID) { val mainLayout by bind(R.id.messageFileMainLayout) - val progressLayout by bind(R.id.audioFileUploadProgressLayout) + val progressLayout by bind(R.id.messageFileUploadProgressLayout) val fileLayout by bind(R.id.messageFileLayout) val fileImageView by bind(R.id.messageFileIconView) val fileImageWrapper by bind(R.id.messageFileImageView) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt new file mode 100644 index 0000000000..06b622d1d6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -0,0 +1,150 @@ +/* + * 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.home.room.detail.timeline.item + +import android.content.res.ColorStateList +import android.graphics.Color +import android.text.format.DateUtils +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import com.visualizer.amplitude.AudioRecordView +import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder +import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout +import im.vector.app.features.themes.ThemeUtils + +@EpoxyModelClass(layout = R.layout.item_timeline_event_base) +abstract class MessageVoiceItem : AbsMessageItem() { + + @EpoxyAttribute + var mxcUrl: String = "" + + @EpoxyAttribute + var duration: Int = 0 + + @EpoxyAttribute + var waveform: List = emptyList() + + @EpoxyAttribute + @JvmField + var isLocalFile = false + + @EpoxyAttribute + @JvmField + var isDownloaded = false + + @EpoxyAttribute + lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder + + @EpoxyAttribute + lateinit var contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var playbackControlButtonClickListener: ClickListener? = null + + @EpoxyAttribute + lateinit var voiceMessagePlaybackTracker: AudioMessagePlaybackTracker + + override fun bind(holder: Holder) { + super.bind(holder) + renderSendState(holder.voiceLayout, null) + if (!attributes.informationData.sendState.hasFailed()) { + contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) + } else { + holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_cross) + holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_voice_message_unable_to_play) + holder.progressLayout.isVisible = false + } + + holder.voicePlaybackWaveform.setOnLongClickListener(attributes.itemLongClickListener) + + holder.voicePlaybackWaveform.post { + holder.voicePlaybackWaveform.recreate() + waveform.forEach { amplitude -> + holder.voicePlaybackWaveform.update(amplitude) + } + } + + val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) { + Color.TRANSPARENT + } else { + ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) + } + holder.voicePlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) + holder.voicePlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } + + voiceMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { + override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { + when (state) { + is AudioMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder) + is AudioMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state) + is AudioMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state) + } + } + }) + } + + private fun renderIdleState(holder: Holder) { + holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) + holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message) + holder.voicePlaybackTime.text = formatPlaybackTime(duration) + } + + private fun renderPlayingState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Playing) { + holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) + holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_pause_voice_message) + holder.voicePlaybackTime.text = formatPlaybackTime(state.playbackTime) + } + + private fun renderPausedState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Paused) { + holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) + holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message) + holder.voicePlaybackTime.text = formatPlaybackTime(state.playbackTime) + } + + private fun formatPlaybackTime(time: Int) = DateUtils.formatElapsedTime((time / 1000).toLong()) + + override fun unbind(holder: Holder) { + super.unbind(holder) + contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) + contentDownloadStateTrackerBinder.unbind(mxcUrl) + voiceMessagePlaybackTracker.untrack(attributes.informationData.eventId) + } + + override fun getViewStubId() = STUB_ID + + class Holder : AbsMessageItem.Holder(STUB_ID) { + val voicePlaybackLayout by bind(R.id.voicePlaybackLayout) + val voiceLayout by bind(R.id.voiceLayout) + val voicePlaybackControlButton by bind(R.id.voicePlaybackControlButton) + val voicePlaybackTime by bind(R.id.voicePlaybackTime) + val voicePlaybackWaveform by bind(R.id.voicePlaybackWaveform) + val progressLayout by bind(R.id.messageFileUploadProgressLayout) + } + + companion object { + private const val STUB_ID = R.id.messageContentVoiceStub + } +} diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index e8b3b80073..d0f4d4c85f 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -2,18 +2,18 @@ + tools:viewBindingIgnore="true"> + + - - + tools:text="0:23" /> - + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_file_stub.xml b/vector/src/main/res/layout/item_timeline_event_file_stub.xml index 82f2c8e886..41e4a118a3 100644 --- a/vector/src/main/res/layout/item_timeline_event_file_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_file_stub.xml @@ -49,7 +49,7 @@ + + + + + + + + + + + + + + + + + From 47dddd706c660c071c8b55e29f2aeb613ef752f3 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Mon, 21 Mar 2022 11:23:03 +0100 Subject: [PATCH 017/368] Only show HasUnread -> HasUnread updates for same readMarker --- .../app/features/home/room/detail/RoomDetailViewState.kt | 2 +- .../app/features/home/room/detail/TimelineViewModel.kt | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) 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 e2b97b0900..5d545ef4b5 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 @@ -36,7 +36,7 @@ sealed class UnreadState { object Unknown : UnreadState() object HasNoUnread : UnreadState() data class ReadMarkerNotLoaded(val readMarkerId: String) : UnreadState() - data class HasUnread(val firstUnreadEventId: String) : UnreadState() + data class HasUnread(val firstUnreadEventId: String, val readMarkerId: String) : UnreadState() } data class JitsiState( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt index 78e3469a58..4caad1bb5e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt @@ -1099,11 +1099,13 @@ class TimelineViewModel @AssistedInject constructor( computeUnreadState(timelineEvents, roomSummary) } // We don't want live update of unread so we skip when we already had a HasUnread or HasNoUnread - // However, we want to update an existing HasUnread, as we might get additional information during loading of events. + // However, we want to update an existing HasUnread, if the readMarkerId hasn't changed, + // as we might be loading new events to fill gaps in the timeline. .distinctUntilChanged { previous, current -> when { previous is UnreadState.Unknown || previous is UnreadState.ReadMarkerNotLoaded -> false - previous is UnreadState.HasUnread && current is UnreadState.HasUnread -> false + previous is UnreadState.HasUnread && current is UnreadState.HasUnread && + previous.readMarkerId == current.readMarkerId -> false current is UnreadState.HasUnread || current is UnreadState.HasNoUnread -> true else -> false } @@ -1132,7 +1134,7 @@ class TimelineViewModel @AssistedInject constructor( val eventId = timelineEvent.root.eventId ?: return UnreadState.Unknown val isFromMe = timelineEvent.root.senderId == session.myUserId if (!isFromMe) { - return UnreadState.HasUnread(eventId) + return UnreadState.HasUnread(eventId, readMarkerIdSnapshot) } } return UnreadState.HasNoUnread From c9946b6dd3d0e9998263b1122e0336df2fc88b4c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 21 Mar 2022 19:57:18 +0100 Subject: [PATCH 018/368] Code cleanup --- .../home/room/detail/TimelineFragment.kt | 27 +++++++------------ .../timeline/factory/MessageItemFactory.kt | 21 +++++++++------ .../detail/timeline/item/MessageAudioItem.kt | 8 +++--- .../detail/timeline/item/MessageVoiceItem.kt | 2 +- vector/src/main/res/values/strings.xml | 1 - 5 files changed, 28 insertions(+), 31 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 7f0c7105cb..cc34a35145 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -155,8 +155,8 @@ import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet -import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker +import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem @@ -164,6 +164,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoIt import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem +import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever @@ -1178,20 +1179,10 @@ class TimelineFragment @Inject constructor( } val messageContent: MessageContent? = event.getLastMessageContent() - val nonFormattedBody = when { - messageContent is MessageAudioContent -> { - getAudioContentBodyText(messageContent) - } - messageContent is MessageAudioContent && messageContent.voiceMessageIndicator != null -> { - val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) - getString(R.string.voice_message_reply_content, formattedDuration) - } - messageContent is MessagePollContent -> { - messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() - } - else -> { - messageContent?.body ?: "" - } + val nonFormattedBody = when (messageContent) { + is MessageAudioContent -> getAudioContentBodyText(messageContent) + is MessagePollContent -> messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() + else -> messageContent?.body ?: "" } var formattedBody: CharSequence? = null if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { @@ -1232,10 +1223,11 @@ class TimelineFragment @Inject constructor( private fun getAudioContentBodyText(messageContent: MessageAudioContent): String { val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong()) - return if (messageContent.voiceMessageIndicator != null) + return if (messageContent.voiceMessageIndicator != null) { getString(R.string.voice_message_reply_content, formattedDuration) - else + } else { getString(R.string.audio_message_reply_content, messageContent.body, formattedDuration) + } } override fun onResume() { @@ -1391,6 +1383,7 @@ class TimelineFragment @Inject constructor( return when (model) { is MessageFileItem, is MessageAudioItem, + is MessageVoiceItem, is MessageImageVideoItem, is MessageTextItem -> { return (model as AbsMessageItem).attributes.informationData.sendState == SendState.SYNCED diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 89fd807240..1b352b93e1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -34,6 +34,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.containsOnlyEmojis import im.vector.app.features.home.room.detail.timeline.TimelineEventController +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder @@ -41,7 +42,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid import im.vector.app.features.home.room.detail.timeline.helper.MessageInformationDataFactory import im.vector.app.features.home.room.detail.timeline.helper.MessageItemAttributesFactory import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem_ @@ -189,13 +189,7 @@ class MessageItemFactory @Inject constructor( is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, highlight, callback, attributes) is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, highlight, callback, attributes) is MessageFileContent -> buildFileMessageItem(messageContent, highlight, attributes) - is MessageAudioContent -> { - if (messageContent.voiceMessageIndicator != null) { - buildVoiceMessageItem(params, messageContent, informationData, highlight, attributes) - } else { - buildAudioMessageItem(params, messageContent, informationData, highlight, attributes) - } - } + is MessageAudioContent -> buildAudioContent(params, messageContent, informationData, highlight, attributes) is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes) is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes) is MessageLocationContent -> { @@ -435,6 +429,17 @@ class MessageItemFactory @Inject constructor( .iconRes(R.drawable.ic_paperclip) } + private fun buildAudioContent(params: TimelineItemFactoryParams, + messageContent: MessageAudioContent, + informationData: MessageInformationData, + highlight: Boolean, + attributes: AbsMessageItem.Attributes) = + if (messageContent.voiceMessageIndicator != null) { + buildVoiceMessageItem(params, messageContent, informationData, highlight, attributes) + } else { + buildAudioMessageItem(params, messageContent, informationData, highlight, attributes) + } + private fun buildNotHandledMessageItem(messageContent: MessageContent, informationData: MessageInformationData, highlight: Boolean, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 101a837b3f..ac65262ba2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 2022 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. @@ -82,11 +82,11 @@ abstract class MessageAudioItem : AbsMessageItem() { } private fun applyLayoutTint(holder: Holder) { - val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) + val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) { Color.TRANSPARENT - else + } else { ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) - + } holder.mainLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index 06b622d1d6..70d93e4263 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -29,9 +29,9 @@ import com.airbnb.epoxy.EpoxyModelClass import com.visualizer.amplitude.AudioRecordView import im.vector.app.R import im.vector.app.core.epoxy.ClickListener +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.themes.ThemeUtils diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index b76aa565b5..46565bfda5 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2861,7 +2861,6 @@ Play Audio Message Pause Audio Message Pause Audio Message - Cannot reply or edit while audio message is active %1$s (%2$s) Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. From 06468a43657109c79238756d074c12b4a8d04a6e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 21 Mar 2022 20:04:08 +0100 Subject: [PATCH 019/368] Adds changelog file --- changelog.d/5586.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5586.feature diff --git a/changelog.d/5586.feature b/changelog.d/5586.feature new file mode 100644 index 0000000000..17d7bfce86 --- /dev/null +++ b/changelog.d/5586.feature @@ -0,0 +1 @@ +Adds the ability for audio attachments to be played in the timeline From b035911d8f1936be15e1dc34419d512090c904a0 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 21 Mar 2022 20:05:47 +0100 Subject: [PATCH 020/368] Fixes import lint error --- .../im/vector/app/features/home/room/detail/TimelineFragment.kt | 2 +- .../features/home/room/detail/timeline/item/MessageAudioItem.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index cc34a35145..ebb07a3a90 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -159,11 +159,11 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem +import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem -import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index ac65262ba2..c3ec93ced3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -27,9 +27,9 @@ import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.ClickListener +import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder -import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.themes.ThemeUtils From 249db1820f22067e394a98e8ce3a929795799454 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 22 Mar 2022 16:22:40 +0100 Subject: [PATCH 021/368] Removes ic_headphones --- vector/src/main/res/drawable/ic_headphones.xml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 vector/src/main/res/drawable/ic_headphones.xml diff --git a/vector/src/main/res/drawable/ic_headphones.xml b/vector/src/main/res/drawable/ic_headphones.xml deleted file mode 100644 index 86f3d8ab7f..0000000000 --- a/vector/src/main/res/drawable/ic_headphones.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - From 806af4798a2e34f1a5deffe6ce7a98d1f2ec0ce4 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Thu, 24 Mar 2022 21:02:29 +0100 Subject: [PATCH 022/368] Fixes post merge errors --- .../features/home/room/detail/TimelineFragment.kt | 4 ++-- .../detail/timeline/factory/MessageItemFactory.kt | 2 +- .../room/detail/timeline/item/MessageVoiceItem.kt | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 587d3d3cc8..87327bbdc5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -786,13 +786,13 @@ class TimelineFragment @Inject constructor( override fun onVoiceWaveformTouchedUp(percentage: Float, duration: Int) { messageComposerViewModel.handle( - MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage) + MessageComposerAction.VoiceWaveformTouchedUp(AudioMessagePlaybackTracker.RECORDING_ID, duration, percentage) ) } override fun onVoiceWaveformMoved(percentage: Float, duration: Int) { messageComposerViewModel.handle( - MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage) + MessageComposerAction.VoiceWaveformTouchedUp(AudioMessagePlaybackTracker.RECORDING_ID, duration, percentage) ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 745f04b159..21050bf0cf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -377,7 +377,7 @@ class MessageItemFactory @Inject constructor( .waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty()) .playbackControlButtonClickListener(playbackControlButtonClickListener) .waveformTouchListener(waveformTouchListener) - .voiceMessagePlaybackTracker(audioMessagePlaybackTracker) + .audioMessagePlaybackTracker(audioMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index f82504595d..d66b9445f5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -75,7 +75,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { var waveformTouchListener: WaveformTouchListener? = null @EpoxyAttribute - lateinit var voiceMessagePlaybackTracker: AudioMessagePlaybackTracker + lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker override fun bind(holder: Holder) { super.bind(holder) @@ -127,12 +127,12 @@ abstract class MessageVoiceItem : AbsMessageItem() { true } - voiceMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { + audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { when (state) { - is AudioMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder) - is AudioMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state) - is AudioMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state) + is AudioMessagePlaybackTracker.Listener.State.Idle -> renderIdleState(holder, waveformColorIdle, waveformColorPlayed) + is AudioMessagePlaybackTracker.Listener.State.Playing -> renderPlayingState(holder, state, waveformColorIdle, waveformColorPlayed) + is AudioMessagePlaybackTracker.Listener.State.Paused -> renderPausedState(holder, state, waveformColorIdle, waveformColorPlayed) is AudioMessagePlaybackTracker.Listener.State.Recording -> Unit } } @@ -168,7 +168,7 @@ abstract class MessageVoiceItem : AbsMessageItem() { super.unbind(holder) contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) contentDownloadStateTrackerBinder.unbind(mxcUrl) - voiceMessagePlaybackTracker.untrack(attributes.informationData.eventId) + audioMessagePlaybackTracker.untrack(attributes.informationData.eventId) } override fun getViewStubId() = STUB_ID From 313595e496d1b2673c534f6c9840048a975a4c10 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 25 Mar 2022 14:30:00 +0100 Subject: [PATCH 023/368] Fixes textview layout bounds in item_timeline_event_audio_stub --- .../main/res/layout/item_timeline_event_audio_stub.xml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index d0f4d4c85f..a69afe4d9f 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -11,7 +11,7 @@ @@ -31,18 +31,17 @@ + tools:text="A filename here that is really long that I dont even wanna even like ugh can I keep talkin like this eventually its gotta end right get ready get your psyche up mob" /> Date: Fri, 25 Mar 2022 22:51:34 +0000 Subject: [PATCH 024/368] Align with web implementation --- .../sdk/api/session/LiveEventListener.kt | 8 +- .../algorithms/megolm/MXMegolmDecryption.kt | 2 +- .../internal/session/StreamEventsManager.kt | 30 +---- .../room/timeline/TokenChunkEventPersistor.kt | 2 +- .../sync/handler/room/RoomSyncHandler.kt | 1 - .../main/java/im/vector/app/AutoRageShaker.kt | 40 ++++--- .../main/java/im/vector/app/UISIDetector.kt | 105 ++++++------------ 7 files changed, 66 insertions(+), 122 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt index 6fda65953a..65e3e94d2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt @@ -21,13 +21,9 @@ import org.matrix.android.sdk.api.util.JsonDict interface LiveEventListener { - fun onLiveEvent(roomId: String, event: Event) + fun onEventDecrypted(event: Event) - fun onPaginatedEvent(roomId: String, event: Event) - - fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) - - fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) + fun onEventDecryptionError(event: Event, throwable: Throwable) fun onLiveToDeviceEvent(event: Event) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index e94daa0e76..22180dce86 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -113,7 +113,7 @@ internal class MXMegolmDecryption(private val userId: String, forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain .orEmpty() ).also { - liveEventManager.get().dispatchLiveEventDecrypted(event, it) + liveEventManager.get().dispatchLiveEventDecrypted(event) } } else { throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt index bb0ca11445..9876e265bf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt @@ -42,36 +42,12 @@ internal class StreamEventsManager @Inject constructor() { listeners.remove(listener) } - fun dispatchLiveEventReceived(event: Event, roomId: String, initialSync: Boolean) { - Timber.v("## dispatchLiveEventReceived ${event.eventId}") - coroutineScope.launch { - if (!initialSync) { - listeners.forEach { - tryOrNull { - it.onLiveEvent(roomId, event) - } - } - } - } - } - - fun dispatchPaginatedEventReceived(event: Event, roomId: String) { - Timber.v("## dispatchPaginatedEventReceived ${event.eventId}") - coroutineScope.launch { - listeners.forEach { - tryOrNull { - it.onPaginatedEvent(roomId, event) - } - } - } - } - - fun dispatchLiveEventDecrypted(event: Event, result: MXEventDecryptionResult) { + fun dispatchLiveEventDecrypted(event: Event) { Timber.v("## dispatchLiveEventDecrypted ${event.eventId}") coroutineScope.launch { listeners.forEach { tryOrNull { - it.onEventDecrypted(event.eventId ?: "", event.roomId ?: "", result.clearEvent) + it.onEventDecrypted(event) } } } @@ -82,7 +58,7 @@ internal class StreamEventsManager @Inject constructor() { coroutineScope.launch { listeners.forEach { tryOrNull { - it.onEventDecryptionError(event.eventId ?: "", event.roomId ?: "", error) + it.onEventDecryptionError(event, error) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 63383a99b3..4e940bb445 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -186,7 +186,7 @@ internal class TokenChunkEventPersistor @Inject constructor( } roomMemberContentsByUser[event.stateKey] = contentToUse.toModel() } - liveEventManager.get().dispatchPaginatedEventReceived(event, roomId) + currentChunk.addTimelineEvent( roomId = roomId, eventEntity = eventEntity, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 8fe85f0d31..1cb476d03a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -382,7 +382,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } eventIds.add(event.eventId) - liveEventService.get().dispatchLiveEventReceived(event, roomId, insertType == EventInsertType.INITIAL_SYNC) val isInitialSync = insertType == EventInsertType.INITIAL_SYNC diff --git a/vector/src/main/java/im/vector/app/AutoRageShaker.kt b/vector/src/main/java/im/vector/app/AutoRageShaker.kt index 43283254b1..836e44f57b 100644 --- a/vector/src/main/java/im/vector/app/AutoRageShaker.kt +++ b/vector/src/main/java/im/vector/app/AutoRageShaker.kt @@ -17,23 +17,31 @@ package im.vector.app import android.content.SharedPreferences +import androidx.lifecycle.asFlow import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.rageshake.ReportType +import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.subscribe import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.initsync.SyncStatusService +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -62,10 +70,11 @@ class AutoRageShaker @Inject constructor( private val e2eDetectedFlow = MutableSharedFlow(replay = 0) private val matchingRSRequestFlow = MutableSharedFlow(replay = 0) - + var hasSynced = false + var preferenceEnabled = false fun initialize() { observeActiveSession() - enable(vectorPreferences.labsAutoReportUISI()) + preferenceEnabled = vectorPreferences.labsAutoReportUISI() // It's a singleton... vectorPreferences.subscribeToChanges(this) @@ -74,7 +83,7 @@ class AutoRageShaker @Inject constructor( e2eDetectedFlow .onEach { sendRageShake(it) - delay(2_000) + delay(60_000) } .catch { cause -> Timber.w(cause, "Failed to RS") @@ -84,7 +93,7 @@ class AutoRageShaker @Inject constructor( matchingRSRequestFlow .onEach { sendMatchingRageShake(it) - delay(2_000) + delay(60_000) } .catch { cause -> Timber.w(cause, "Failed to send matching rageshake") @@ -93,14 +102,7 @@ class AutoRageShaker @Inject constructor( } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - enable(vectorPreferences.labsAutoReportUISI()) - } - - var _enabled = false - fun enable(enabled: Boolean) { - if (enabled == _enabled) return - _enabled = enabled - detector.enabled = enabled + preferenceEnabled = vectorPreferences.labsAutoReportUISI() } private fun observeActiveSession() { @@ -115,7 +117,6 @@ class AutoRageShaker @Inject constructor( } fun decryptionErrorDetected(target: E2EMessageDetected) { - if (target.source == UISIEventSource.INITIAL_SYNC) return if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return val shouldSendRS = synchronized(alreadyReportedUisi) { val reportInfo = ReportInfo(target.roomId, target.sessionId) @@ -148,7 +149,6 @@ class AutoRageShaker @Inject constructor( append("\"room_id\": \"${target.roomId}\",") append("\"sender_key\": \"${target.senderKey}\",") append("\"device_id\": \"${target.senderDeviceId}\",") - append("\"source\": \"${target.source}\",") append("\"user_id\": \"${target.senderUserId}\",") append("\"session_id\": \"${target.sessionId}\"") append("}") @@ -245,6 +245,9 @@ class AutoRageShaker @Inject constructor( override val reciprocateToDeviceEventType: String get() = AUTO_RS_REQUEST + override val enabled: Boolean + get() = this@AutoRageShaker.preferenceEnabled && this@AutoRageShaker.hasSynced + override fun uisiDetected(source: E2EMessageDetected) { decryptionErrorDetected(source) } @@ -261,7 +264,14 @@ class AutoRageShaker @Inject constructor( return } this.currentActiveSessionId = sessionId - this.detector.enabled = _enabled + + hasSynced = session.hasAlreadySynced() + session.getSyncStatusLive() + .asFlow() + .onEach { + hasSynced = it !is SyncStatusService.Status.Progressing + } + .launchIn(session.coroutineScope) activeSessionIds.add(sessionId) session.addListener(this) session.addEventStreamListener(detector) diff --git a/vector/src/main/java/im/vector/app/UISIDetector.kt b/vector/src/main/java/im/vector/app/UISIDetector.kt index d6a4805e78..f7c6725bd8 100644 --- a/vector/src/main/java/im/vector/app/UISIDetector.kt +++ b/vector/src/main/java/im/vector/app/UISIDetector.kt @@ -16,6 +16,7 @@ package im.vector.app +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel @@ -26,23 +27,17 @@ import java.util.Timer import java.util.TimerTask import java.util.concurrent.Executors -enum class UISIEventSource { - INITIAL_SYNC, - INCREMENTAL_SYNC, - PAGINATION -} - data class E2EMessageDetected( val eventId: String, val roomId: String, val senderUserId: String, val senderDeviceId: String, val senderKey: String, - val sessionId: String, - val source: UISIEventSource) { + val sessionId: String + ) { companion object { - fun fromEvent(event: Event, roomId: String, source: UISIEventSource): E2EMessageDetected { + fun fromEvent(event: Event, roomId: String): E2EMessageDetected { val encryptedContent = event.content.toModel() return E2EMessageDetected( @@ -51,8 +46,7 @@ data class E2EMessageDetected( senderUserId = event.senderId ?: "", senderDeviceId = encryptedContent?.deviceId ?: "", senderKey = encryptedContent?.senderKey ?: "", - sessionId = encryptedContent?.sessionId ?: "", - source = source + sessionId = encryptedContent?.sessionId ?: "" ) } } @@ -61,6 +55,7 @@ data class E2EMessageDetected( class UISIDetector : LiveEventListener { interface UISIDetectorCallback { + val enabled: Boolean val reciprocateToDeviceEventType: String fun uisiDetected(source: E2EMessageDetected) fun uisiReciprocateRequest(source: Event) @@ -68,30 +63,16 @@ class UISIDetector : LiveEventListener { var callback: UISIDetectorCallback? = null - private val trackedEvents = mutableListOf>() + private val trackedEvents = mutableMapOf() private val executor = Executors.newSingleThreadExecutor() private val timer = Timer() private val timeoutMillis = 30_000L - var enabled = false + val enabled: Boolean get() = callback?.enabled.orFalse() - override fun onLiveEvent(roomId: String, event: Event) { - if (!enabled) return - if (!event.isEncrypted()) return - executor.execute { - handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.INCREMENTAL_SYNC)) - } - } - - override fun onPaginatedEvent(roomId: String, event: Event) { - if (!enabled) return - if (!event.isEncrypted()) return - executor.execute { - handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.PAGINATION)) - } - } - - override fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) { - if (!enabled) return + override fun onEventDecrypted(event: Event) { + val eventId = event.eventId + val roomId = event.roomId + if (!enabled || eventId == null || roomId == null) return executor.execute { unTrack(eventId, roomId) } @@ -104,57 +85,39 @@ class UISIDetector : LiveEventListener { } } - override fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) { - if (!enabled) return - executor.execute { - unTrack(eventId, roomId)?.let { - triggerUISI(it) - } -// if (throwable is MXCryptoError.OlmError) { -// if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") { -// unTrack(eventId, roomId)?.let { -// triggerUISI(it) -// } -// } -// } - } - } + override fun onEventDecryptionError(event: Event, throwable: Throwable) { + val eventId = event.eventId + val roomId = event.roomId + if (!enabled || eventId == null || roomId == null) return - private fun handleEventReceived(detectorEvent: E2EMessageDetected) { - if (!enabled) return - if (trackedEvents.any { it.first == detectorEvent }) { - Timber.w("## UISIDetector: Event ${detectorEvent.eventId} is already tracked") - } else { - // track it and start timer - val timeoutTask = object : TimerTask() { - override fun run() { - executor.execute { - unTrack(detectorEvent.eventId, detectorEvent.roomId) - Timber.v("## UISIDetector: Timeout on ${detectorEvent.eventId} ") - triggerUISI(detectorEvent) - } + val trackerId: String = trackerId(eventId, roomId) + if (trackedEvents.containsKey(trackerId)) { + Timber.w("## UISIDetector: Event $eventId is already tracked") + return + } + // track it and start timer + val timeoutTask = object : TimerTask() { + override fun run() { + executor.execute { + unTrack(eventId, roomId) + Timber.v("## UISIDetector: Timeout on $eventId") + triggerUISI(E2EMessageDetected.fromEvent(event, roomId)) } } - trackedEvents.add(detectorEvent to timeoutTask) - timer.schedule(timeoutTask, timeoutMillis) } + trackedEvents[trackerId] = timeoutTask + timer.schedule(timeoutTask, timeoutMillis) } + private fun trackerId(eventId: String, roomId: String): String = "$roomId-$eventId" + private fun triggerUISI(source: E2EMessageDetected) { if (!enabled) return Timber.i("## UISIDetector: Unable To Decrypt $source") callback?.uisiDetected(source) } - private fun unTrack(eventId: String, roomId: String): E2EMessageDetected? { - val index = trackedEvents.indexOfFirst { it.first.eventId == eventId && it.first.roomId == roomId } - return if (index != -1) { - trackedEvents.removeAt(index).let { - it.second.cancel() - it.first - } - } else { - null - } + private fun unTrack(eventId: String, roomId: String) { + trackedEvents.remove(trackerId(eventId, roomId))?.cancel() } } From 531b62f634ae2b4b728e7cefce74c97b0adacdda Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 25 Mar 2022 23:00:47 +0000 Subject: [PATCH 025/368] Make properties private --- vector/src/main/java/im/vector/app/AutoRageShaker.kt | 4 ++-- vector/src/main/java/im/vector/app/UISIDetector.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/AutoRageShaker.kt b/vector/src/main/java/im/vector/app/AutoRageShaker.kt index 836e44f57b..b7578f8b89 100644 --- a/vector/src/main/java/im/vector/app/AutoRageShaker.kt +++ b/vector/src/main/java/im/vector/app/AutoRageShaker.kt @@ -70,8 +70,8 @@ class AutoRageShaker @Inject constructor( private val e2eDetectedFlow = MutableSharedFlow(replay = 0) private val matchingRSRequestFlow = MutableSharedFlow(replay = 0) - var hasSynced = false - var preferenceEnabled = false + private var hasSynced = false + private var preferenceEnabled = false fun initialize() { observeActiveSession() preferenceEnabled = vectorPreferences.labsAutoReportUISI() diff --git a/vector/src/main/java/im/vector/app/UISIDetector.kt b/vector/src/main/java/im/vector/app/UISIDetector.kt index f7c6725bd8..0dcc94516f 100644 --- a/vector/src/main/java/im/vector/app/UISIDetector.kt +++ b/vector/src/main/java/im/vector/app/UISIDetector.kt @@ -67,7 +67,7 @@ class UISIDetector : LiveEventListener { private val executor = Executors.newSingleThreadExecutor() private val timer = Timer() private val timeoutMillis = 30_000L - val enabled: Boolean get() = callback?.enabled.orFalse() + private val enabled: Boolean get() = callback?.enabled.orFalse() override fun onEventDecrypted(event: Event) { val eventId = event.eventId From f38bf2548f86efc679cb9bfd5e0b219e91701f0b Mon Sep 17 00:00:00 2001 From: David Langley Date: Fri, 25 Mar 2022 23:18:45 +0000 Subject: [PATCH 026/368] lint --- .../org/matrix/android/sdk/api/session/LiveEventListener.kt | 1 - .../android/sdk/internal/session/StreamEventsManager.kt | 1 - vector/src/main/java/im/vector/app/AutoRageShaker.kt | 5 ----- vector/src/main/java/im/vector/app/UISIDetector.kt | 1 - 4 files changed, 8 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt index 65e3e94d2d..def3ecbfca 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.api.session import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.util.JsonDict interface LiveEventListener { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt index 9876e265bf..7c1ca1f630 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/AutoRageShaker.kt b/vector/src/main/java/im/vector/app/AutoRageShaker.kt index b7578f8b89..46390f30b8 100644 --- a/vector/src/main/java/im/vector/app/AutoRageShaker.kt +++ b/vector/src/main/java/im/vector/app/AutoRageShaker.kt @@ -25,23 +25,18 @@ import im.vector.app.features.session.coroutineScope import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.subscribe import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.initsync.SyncStatusService -import org.matrix.android.sdk.api.session.sync.SyncState -import org.matrix.android.sdk.flow.flow import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton diff --git a/vector/src/main/java/im/vector/app/UISIDetector.kt b/vector/src/main/java/im/vector/app/UISIDetector.kt index 0dcc94516f..2b6974eefb 100644 --- a/vector/src/main/java/im/vector/app/UISIDetector.kt +++ b/vector/src/main/java/im/vector/app/UISIDetector.kt @@ -20,7 +20,6 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import timber.log.Timber import java.util.Timer From 8aaaf80262a26ca7b02852386a11cb88ca685d37 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 28 Mar 2022 10:47:59 +0200 Subject: [PATCH 027/368] Fixes lint error --- .../home/room/detail/timeline/factory/MessageItemFactory.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 5770a457b8..adb5cfdda6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -492,8 +492,7 @@ class MessageItemFactory @Inject constructor( informationData: MessageInformationData, highlight: Boolean, callback: TimelineEventController.Callback?, - attributes: AbsMessageItem.Attributes) - : MessageTextItem? { + attributes: AbsMessageItem.Attributes): MessageTextItem? { // For compatibility reason we should display the body return buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes) } From 931c0e9826244100b76cb536988c9743551e794c Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 28 Mar 2022 11:01:31 +0200 Subject: [PATCH 028/368] Improves accessibility talkback on MessageAudioItem --- .../room/detail/timeline/item/MessageAudioItem.kt | 12 ++++++++---- vector/src/main/res/values/strings.xml | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index c3ec93ced3..bf248a27e8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -76,7 +76,8 @@ abstract class MessageAudioItem : AbsMessageItem() { contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } else { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_cross) - holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_audio_message_unable_to_play) + holder.audioPlaybackControlButton.contentDescription = + holder.view.context.getString(R.string.error_audio_message_unable_to_play, filename) holder.progressLayout.isVisible = false } } @@ -105,19 +106,22 @@ abstract class MessageAudioItem : AbsMessageItem() { private fun renderIdleState(holder: Holder) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message) + holder.audioPlaybackControlButton.contentDescription = + holder.view.context.getString(R.string.a11y_play_audio_message, filename) holder.audioPlaybackTime.text = formatPlaybackTime(duration) } private fun renderPlayingState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Playing) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) - holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_pause_audio_message) + holder.audioPlaybackControlButton.contentDescription = + holder.view.context.getString(R.string.a11y_pause_audio_message, filename) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } private fun renderPausedState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Paused) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play) - holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message) + holder.audioPlaybackControlButton.contentDescription = + holder.view.context.getString(R.string.a11y_play_audio_message, filename) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index d646b73aa0..90f6c5f7b1 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2860,9 +2860,9 @@ Cannot reply or edit while voice message is active Voice Message (%1$s) - Play Audio Message - Pause Audio Message - Pause Audio Message + Play %1$s + Pause %1$s + Unable to play %1$s %1$s (%2$s) Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. From 4fe86503879379237231dd4d676b328d9971380a Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 28 Mar 2022 10:07:29 +0100 Subject: [PATCH 029/368] Add changelog --- changelog.d/5596.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5596.bugfix diff --git a/changelog.d/5596.bugfix b/changelog.d/5596.bugfix new file mode 100644 index 0000000000..f51794c352 --- /dev/null +++ b/changelog.d/5596.bugfix @@ -0,0 +1 @@ +Align auto-reporting of decryption errors implementation with web client. \ No newline at end of file From fa56a5efa976bdeb532bcee61f3a44dfc614b556 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 28 Mar 2022 13:52:47 +0200 Subject: [PATCH 030/368] Fixes playback button state not tracking after onPause --- .../app/features/home/room/detail/TimelineFragment.kt | 2 +- .../room/detail/composer/MessageComposerViewModel.kt | 1 - .../timeline/helper/AudioMessagePlaybackTracker.kt | 6 ++++++ .../home/room/detail/timeline/item/MessageAudioItem.kt | 10 +++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 703696173e..a5c1b8a544 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1265,7 +1265,7 @@ class TimelineFragment @Inject constructor( override fun onPause() { super.onPause() notificationDrawerManager.setCurrentRoom(null) - audioMessagePlaybackTracker.untrack(AudioMessagePlaybackTracker.RECORDING_ID) + audioMessagePlaybackTracker.pauseAllPlaybacks() if (withState(messageComposerViewModel) { it.isVoiceRecording } && requireActivity().isChangingConfigurations) { // we're rotating, maintain any active recordings diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index 976489eec3..eb7d87c371 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -881,7 +881,6 @@ class MessageComposerViewModel @AssistedInject constructor( private fun handleEntersBackground(composerText: String) { // Always stop all voice actions. It may be playing in timeline or active recording val playingAudioContent = voiceMessageHelper.stopAllVoiceActions(deleteRecord = false) - voiceMessageHelper.clearTracker() val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording } if (isVoiceRecording) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt index 7404735e28..546b76bebd 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt @@ -50,6 +50,12 @@ class AudioMessagePlaybackTracker @Inject constructor() { listeners.remove(id) } + fun pauseAllPlaybacks() { + listeners.keys.forEach { key -> + pausePlayback(key) + } + } + fun makeAllPlaybacksIdle() { listeners.keys.forEach { key -> setState(key, Listener.State.Idle) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index bf248a27e8..243aa8c6a4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.item import android.content.res.ColorStateList import android.graphics.Color +import android.graphics.Paint import android.text.format.DateUtils import android.view.ViewGroup import android.widget.ImageButton @@ -27,6 +28,7 @@ import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.onClick import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder @@ -64,8 +66,8 @@ abstract class MessageAudioItem : AbsMessageItem() { override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.rootLayout, null) + bindFilenameViewAttributes(holder) bindUploadState(holder) - holder.filenameView.text = filename applyLayoutTint(holder) holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } renderStateBasedOnAudioPlayback(holder) @@ -91,6 +93,12 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.mainLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) } + private fun bindFilenameViewAttributes(holder: Holder) { + holder.filenameView.text = filename + holder.filenameView.onClick(attributes.itemClickListener) + holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG) + } + private fun renderStateBasedOnAudioPlayback(holder: Holder) { audioMessagePlaybackTracker.track(attributes.informationData.eventId, object : AudioMessagePlaybackTracker.Listener { override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) { From b4885629af493cd5fbe726d0e81dc01cd7d7cb82 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 28 Mar 2022 17:23:05 +0100 Subject: [PATCH 031/368] Keep live event/pagination listeners. --- changelog.d/5639.sdk | 1 + .../sdk/api/session/LiveEventListener.kt | 7 ++++- .../algorithms/megolm/MXMegolmDecryption.kt | 2 +- .../internal/session/StreamEventsManager.kt | 29 +++++++++++++++++-- .../room/timeline/TokenChunkEventPersistor.kt | 2 +- .../sync/handler/room/RoomSyncHandler.kt | 1 + .../main/java/im/vector/app/UISIDetector.kt | 7 ++++- 7 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 changelog.d/5639.sdk diff --git a/changelog.d/5639.sdk b/changelog.d/5639.sdk new file mode 100644 index 0000000000..96b495f809 --- /dev/null +++ b/changelog.d/5639.sdk @@ -0,0 +1 @@ +Include original event in decryption live decryption listeners. \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt index def3ecbfca..b4b283c86a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/LiveEventListener.kt @@ -17,10 +17,15 @@ package org.matrix.android.sdk.api.session import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.util.JsonDict interface LiveEventListener { - fun onEventDecrypted(event: Event) + fun onLiveEvent(roomId: String, event: Event) + + fun onPaginatedEvent(roomId: String, event: Event) + + fun onEventDecrypted(event: Event, clearEvent: JsonDict) fun onEventDecryptionError(event: Event, throwable: Throwable) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 22180dce86..e94daa0e76 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -113,7 +113,7 @@ internal class MXMegolmDecryption(private val userId: String, forwardingCurve25519KeyChain = olmDecryptionResult.forwardingCurve25519KeyChain .orEmpty() ).also { - liveEventManager.get().dispatchLiveEventDecrypted(event) + liveEventManager.get().dispatchLiveEventDecrypted(event, it) } } else { throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt index 7c1ca1f630..8ae9772714 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/StreamEventsManager.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import timber.log.Timber import javax.inject.Inject @@ -41,12 +42,36 @@ internal class StreamEventsManager @Inject constructor() { listeners.remove(listener) } - fun dispatchLiveEventDecrypted(event: Event) { + fun dispatchLiveEventReceived(event: Event, roomId: String, initialSync: Boolean) { + Timber.v("## dispatchLiveEventReceived ${event.eventId}") + coroutineScope.launch { + if (!initialSync) { + listeners.forEach { + tryOrNull { + it.onLiveEvent(roomId, event) + } + } + } + } + } + + fun dispatchPaginatedEventReceived(event: Event, roomId: String) { + Timber.v("## dispatchPaginatedEventReceived ${event.eventId}") + coroutineScope.launch { + listeners.forEach { + tryOrNull { + it.onPaginatedEvent(roomId, event) + } + } + } + } + + fun dispatchLiveEventDecrypted(event: Event, result: MXEventDecryptionResult) { Timber.v("## dispatchLiveEventDecrypted ${event.eventId}") coroutineScope.launch { listeners.forEach { tryOrNull { - it.onEventDecrypted(event) + it.onEventDecrypted(event, result.clearEvent) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index 4e940bb445..63383a99b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -186,7 +186,7 @@ internal class TokenChunkEventPersistor @Inject constructor( } roomMemberContentsByUser[event.stateKey] = contentToUse.toModel() } - + liveEventManager.get().dispatchPaginatedEventReceived(event, roomId) currentChunk.addTimelineEvent( roomId = roomId, eventEntity = eventEntity, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 1cb476d03a..8fe85f0d31 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -382,6 +382,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle } eventIds.add(event.eventId) + liveEventService.get().dispatchLiveEventReceived(event, roomId, insertType == EventInsertType.INITIAL_SYNC) val isInitialSync = insertType == EventInsertType.INITIAL_SYNC diff --git a/vector/src/main/java/im/vector/app/UISIDetector.kt b/vector/src/main/java/im/vector/app/UISIDetector.kt index 2b6974eefb..bb76a96b92 100644 --- a/vector/src/main/java/im/vector/app/UISIDetector.kt +++ b/vector/src/main/java/im/vector/app/UISIDetector.kt @@ -20,6 +20,7 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.LiveEventListener import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import timber.log.Timber import java.util.Timer @@ -68,7 +69,7 @@ class UISIDetector : LiveEventListener { private val timeoutMillis = 30_000L private val enabled: Boolean get() = callback?.enabled.orFalse() - override fun onEventDecrypted(event: Event) { + override fun onEventDecrypted(event: Event, clearEvent: JsonDict) { val eventId = event.eventId val roomId = event.roomId if (!enabled || eventId == null || roomId == null) return @@ -108,6 +109,10 @@ class UISIDetector : LiveEventListener { timer.schedule(timeoutTask, timeoutMillis) } + override fun onLiveEvent(roomId: String, event: Event) { } + + override fun onPaginatedEvent(roomId: String, event: Event) { } + private fun trackerId(eventId: String, roomId: String): String = "$roomId-$eventId" private fun triggerUISI(source: E2EMessageDetected) { From 3ae4303ecd4d28b8194bfe16025866957ee5ecb6 Mon Sep 17 00:00:00 2001 From: David Langley Date: Mon, 28 Mar 2022 17:28:33 +0100 Subject: [PATCH 032/368] Fix changelog wording --- changelog.d/5639.sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/5639.sdk b/changelog.d/5639.sdk index 96b495f809..1ff8b4c053 100644 --- a/changelog.d/5639.sdk +++ b/changelog.d/5639.sdk @@ -1 +1 @@ -Include original event in decryption live decryption listeners. \ No newline at end of file +Include original event in live decryption listeners. \ No newline at end of file From 365c03e76365a2669ed9698cd67ce3e144eea98f Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Tue, 29 Mar 2022 12:45:23 +0200 Subject: [PATCH 033/368] Load timeline without initial eventId if not found --- changelog.d/5659.bugfix | 1 + .../session/room/timeline/DefaultTimeline.kt | 18 +++++++++++++++++- .../room/timeline/LoadTimelineStrategy.kt | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 changelog.d/5659.bugfix diff --git a/changelog.d/5659.bugfix b/changelog.d/5659.bugfix new file mode 100644 index 0000000000..eec39a7738 --- /dev/null +++ b/changelog.d/5659.bugfix @@ -0,0 +1 @@ +Fix endless loading if the event from a permalink is not found diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 8c2b4d2bbe..99b9683c31 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -223,7 +223,15 @@ internal class DefaultTimeline(private val roomId: String, updateState(direction) { it.copy(loading = true) } - val loadMoreResult = strategy.loadMore(count, direction, fetchOnServerIfNeeded) + val loadMoreResult = try { + strategy.loadMore(count, direction, fetchOnServerIfNeeded) + } catch (throwable: Throwable) { + // Timeline could not be loaded with a (likely) permanent issue, such as the + // server now knowing the initialEventId, so we want to show an error message + // and possibly restart without initialEventId. + onTimelineFailure(throwable) + return false + } Timber.v("$baseLogMessage: result $loadMoreResult") val hasMoreToLoad = loadMoreResult != LoadMoreResult.REACHED_END updateState(direction) { @@ -342,6 +350,14 @@ internal class DefaultTimeline(private val roomId: String, } } + private fun onTimelineFailure(throwable: Throwable) { + timelineScope.launch(coroutineDispatchers.main) { + listeners.forEach { + tryOrNull { it.onTimelineFailure(throwable) } + } + } + } + private fun buildStrategy(mode: LoadTimelineStrategy.Mode): LoadTimelineStrategy { return LoadTimelineStrategy( roomId = roomId, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index a9e7b3bcdc..bb565a8d6b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -24,6 +24,8 @@ import io.realm.RealmResults import io.realm.kotlin.createObject import kotlinx.coroutines.CompletableDeferred import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent @@ -194,6 +196,10 @@ internal class LoadTimelineStrategy( getContextLatch?.await() getContextLatch = null } catch (failure: Throwable) { + if (failure is Failure.ServerError && failure.error.code == MatrixError.M_NOT_FOUND) { + // This failure is likely permanent, so handle in DefaultTimeline to restart without eventId + throw failure + } return LoadMoreResult.FAILURE } } From cc5e8f35a73eedf1d55c5635310697e91dafc038 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 29 Mar 2022 16:25:01 +0200 Subject: [PATCH 034/368] Improves code formatting --- .../app/features/home/room/detail/TimelineFragment.kt | 2 +- .../home/room/detail/timeline/item/MessageAudioItem.kt | 6 +++--- .../home/room/detail/timeline/item/MessageVoiceItem.kt | 4 ---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index a5c1b8a544..db86b5a55d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1193,7 +1193,7 @@ class TimelineFragment @Inject constructor( val nonFormattedBody = when (messageContent) { is MessageAudioContent -> getAudioContentBodyText(messageContent) is MessagePollContent -> messageContent.getBestPollCreationInfo()?.question?.getBestQuestion() - else -> messageContent?.body ?: "" + else -> messageContent?.body.orEmpty() } var formattedBody: CharSequence? = null if (messageContent is MessageTextContent && messageContent.format == MessageFormat.FORMAT_MATRIX_HTML) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 243aa8c6a4..7748ccb03f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -74,13 +74,13 @@ abstract class MessageAudioItem : AbsMessageItem() { } private fun bindUploadState(holder: Holder) { - if (!attributes.informationData.sendState.hasFailed()) { - contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) - } else { + if (attributes.informationData.sendState.hasFailed()) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_cross) holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.error_audio_message_unable_to_play, filename) holder.progressLayout.isVisible = false + } else { + contentUploadStateTrackerBinder.bind(attributes.informationData.eventId, isLocalFile, holder.progressLayout) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index d66b9445f5..8da4a3dcd8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -58,10 +58,6 @@ abstract class MessageVoiceItem : AbsMessageItem() { @JvmField var isLocalFile = false - @EpoxyAttribute - @JvmField - var isDownloaded = false - @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder From 6adf4878f7cda2bc3560bbea9ecc8f135fe74f33 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 29 Mar 2022 17:40:00 +0200 Subject: [PATCH 035/368] Fixes bugs related to audio message duration being set incorrectly between activity states --- .../app/features/home/room/detail/TimelineFragment.kt | 1 + .../detail/timeline/helper/AudioMessagePlaybackTracker.kt | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index db86b5a55d..4907f99d25 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -900,6 +900,7 @@ class TimelineFragment @Inject constructor( } override fun onDestroyView() { + audioMessagePlaybackTracker.makeAllPlaybacksIdle() lazyLoadedViews.unBind() timelineEventController.callback = null timelineEventController.removeModelBuildListener(modelBuildListener) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt index 546b76bebd..fb000d7b70 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt @@ -93,9 +93,11 @@ class AudioMessagePlaybackTracker @Inject constructor() { } fun pausePlayback(id: String) { - val currentPlaybackTime = getPlaybackTime(id) - val currentPercentage = getPercentage(id) - setState(id, Listener.State.Paused(currentPlaybackTime, currentPercentage)) + if (getPlaybackState(id) is Listener.State.Playing) { + val currentPlaybackTime = getPlaybackTime(id) + val currentPercentage = getPercentage(id) + setState(id, Listener.State.Paused(currentPlaybackTime, currentPercentage)) + } } fun stopPlayback(id: String) { From 8ad4f20d990c079bdee9e0f41c3d93a043a511fe Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 29 Mar 2022 19:54:23 +0200 Subject: [PATCH 036/368] Fixes bug where audio can be played before waveform is ready --- .../features/home/room/detail/timeline/item/MessageVoiceItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt index 8da4a3dcd8..02937574f2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt @@ -94,11 +94,11 @@ abstract class MessageVoiceItem : AbsMessageItem() { ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quinary) } holder.voicePlaybackLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) - holder.voicePlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } } private fun onWaveformViewReady(holder: Holder) { holder.voicePlaybackWaveform.setOnLongClickListener(attributes.itemLongClickListener) + holder.voicePlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } val waveformColorIdle = ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_quaternary) val waveformColorPlayed = ThemeUtils.getColor(holder.view.context, R.attr.vctr_content_secondary) From c44c637fbaa5be5329d480baa291a0a144272025 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Wed, 30 Mar 2022 17:35:51 +0200 Subject: [PATCH 037/368] Also give up loading event for M_FORBIDDEN --- .../sdk/internal/session/room/timeline/LoadTimelineStrategy.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index bb565a8d6b..42973797f3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -196,7 +196,7 @@ internal class LoadTimelineStrategy( getContextLatch?.await() getContextLatch = null } catch (failure: Throwable) { - if (failure is Failure.ServerError && failure.error.code == MatrixError.M_NOT_FOUND) { + if (failure is Failure.ServerError && failure.error.code in listOf(MatrixError.M_NOT_FOUND, MatrixError.M_FORBIDDEN)) { // This failure is likely permanent, so handle in DefaultTimeline to restart without eventId throw failure } From 34dcd70a64f9a1b2ec7000516e863c449e972ec6 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Fri, 1 Apr 2022 15:15:12 +0200 Subject: [PATCH 038/368] Adds layout with seek bar to audio stub --- vector/src/main/res/drawable/bg_seek_bar.xml | 21 +++++++++ .../layout/item_timeline_event_audio_stub.xml | 45 +++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 vector/src/main/res/drawable/bg_seek_bar.xml diff --git a/vector/src/main/res/drawable/bg_seek_bar.xml b/vector/src/main/res/drawable/bg_seek_bar.xml new file mode 100644 index 0000000000..0a33522dfd --- /dev/null +++ b/vector/src/main/res/drawable/bg_seek_bar.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index a69afe4d9f..ee57046bf4 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -23,9 +23,10 @@ android:backgroundTint="?android:colorBackground" android:contentDescription="@string/a11y_play_voice_message" android:src="@drawable/ic_play_pause_play" + android:layout_marginTop="10dp" + android:layout_marginStart="4dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" app:tint="?vctr_content_secondary" /> + tools:text="Filename.mp3" /> + + + + + + From 4c059e781920b24d7a9920cc93400b83b433b744 Mon Sep 17 00:00:00 2001 From: NIkita Fedrunov Date: Mon, 4 Apr 2022 09:49:47 +0200 Subject: [PATCH 039/368] add text to spinner when click on space member (dm) --- .../vector/app/features/spaces/people/SpacePeopleActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt index f5832a8547..23a76b4b68 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt @@ -24,6 +24,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.Mavericks import dagger.hilt.android.AndroidEntryPoint +import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.platform.GenericIdArgs @@ -79,7 +80,7 @@ class SpacePeopleActivity : VectorBaseActivity() { is SpacePeopleSharedAction.NavigateToRoom -> navigateToRooms(sharedAction) SpacePeopleSharedAction.HideModalLoading -> hideWaitingView() SpacePeopleSharedAction.ShowModalLoading -> { - showWaitingView() + showWaitingView(getString(R.string.please_wait)) } is SpacePeopleSharedAction.NavigateToInvite -> { ShareSpaceBottomSheet.show(supportFragmentManager, sharedAction.spaceId) From 9050c488e4040becffd3cbc690076004b631ffd7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 27 Jan 2022 09:56:41 +0100 Subject: [PATCH 040/368] konfetti 2.0.1 Does not compile --- vector/build.gradle | 3 ++- .../im/vector/app/core/animations/Konfetti.kt | 24 ++++++++++++++++--- .../layout/fragment_ftue_account_created.xml | 2 +- ...fragment_ftue_personalization_complete.xml | 2 +- .../src/main/res/layout/fragment_timeline.xml | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index a7e347949b..d26553d689 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -446,7 +446,8 @@ dependencies { implementation 'me.leolin:ShortcutBadger:1.1.22@aar' // Chat effects - implementation 'nl.dionsegijn:konfetti:1.3.2' + implementation 'nl.dionsegijn:konfetti-xml:2.0.1' + implementation 'com.github.jetradarmobile:android-snowfall:1.2.1' // DI implementation libs.dagger.hilt diff --git a/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt b/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt index 22764ac5bd..3192be9251 100644 --- a/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt +++ b/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt @@ -20,9 +20,9 @@ import android.content.Context import androidx.annotation.ColorInt import androidx.core.content.ContextCompat import im.vector.app.R -import nl.dionsegijn.konfetti.KonfettiView -import nl.dionsegijn.konfetti.models.Shape -import nl.dionsegijn.konfetti.models.Size +import nl.dionsegijn.konfetti.core.Party +import nl.dionsegijn.konfetti.core.emitter.Emitter +import nl.dionsegijn.konfetti.xml.KonfettiView fun KonfettiView.play() { val confettiColors = listOf( @@ -35,6 +35,7 @@ fun KonfettiView.play() { R.color.palette_prune, R.color.palette_kiwi ) + /* build() .addColors(confettiColors.toColorInt(context)) .setDirection(0.0, 359.0) @@ -45,6 +46,23 @@ fun KonfettiView.play() { .addSizes(Size(12)) .setPosition(-50f, width + 50f, -50f, -50f) .streamFor(150, 3000L) + */ + + val party = Party( + colors = confettiColors.toColorInt(context), + /* + // Keep other default setting for now. + timeToLive = 2000L, + fadeOutEnabled = true, + speed = 2f, + maxSpeed = 5f, + damping = 0.9f, + spread = 360, + position = Position.Relative(0.5, 0.3), + */ + emitter = Emitter(duration = 100).max(100) + ) + start(party) } @ColorInt diff --git a/vector/src/main/res/layout/fragment_ftue_account_created.xml b/vector/src/main/res/layout/fragment_ftue_account_created.xml index 5b8dddba22..83c5cb76b9 100644 --- a/vector/src/main/res/layout/fragment_ftue_account_created.xml +++ b/vector/src/main/res/layout/fragment_ftue_account_created.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:background="?colorSecondary"> - - - Date: Tue, 22 Feb 2022 22:31:06 +0100 Subject: [PATCH 041/368] Try with 2.0.2 --- vector/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/build.gradle b/vector/build.gradle index d26553d689..f5e0173601 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -446,7 +446,7 @@ dependencies { implementation 'me.leolin:ShortcutBadger:1.1.22@aar' // Chat effects - implementation 'nl.dionsegijn:konfetti-xml:2.0.1' + implementation 'nl.dionsegijn:konfetti-xml:2.0.2' implementation 'com.github.jetradarmobile:android-snowfall:1.2.1' // DI From 4f27ab263f25a31d8f7f425615b65b92d5e16f0d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 4 Apr 2022 16:08:56 +0200 Subject: [PATCH 042/368] Update parameters --- .../im/vector/app/core/animations/Konfetti.kt | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt b/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt index 3192be9251..882891810b 100644 --- a/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt +++ b/vector/src/main/java/im/vector/app/core/animations/Konfetti.kt @@ -20,8 +20,13 @@ import android.content.Context import androidx.annotation.ColorInt import androidx.core.content.ContextCompat import im.vector.app.R +import nl.dionsegijn.konfetti.core.Angle import nl.dionsegijn.konfetti.core.Party +import nl.dionsegijn.konfetti.core.Position +import nl.dionsegijn.konfetti.core.Spread import nl.dionsegijn.konfetti.core.emitter.Emitter +import nl.dionsegijn.konfetti.core.models.Shape +import nl.dionsegijn.konfetti.core.models.Size import nl.dionsegijn.konfetti.xml.KonfettiView fun KonfettiView.play() { @@ -35,33 +40,21 @@ fun KonfettiView.play() { R.color.palette_prune, R.color.palette_kiwi ) - /* - build() - .addColors(confettiColors.toColorInt(context)) - .setDirection(0.0, 359.0) - .setSpeed(2f, 5f) - .setFadeOutEnabled(true) - .setTimeToLive(2000L) - .addShapes(Shape.Square, Shape.Circle) - .addSizes(Size(12)) - .setPosition(-50f, width + 50f, -50f, -50f) - .streamFor(150, 3000L) - */ - + val emitterConfig = Emitter(2000).perSecond(100) val party = Party( + emitter = emitterConfig, colors = confettiColors.toColorInt(context), - /* - // Keep other default setting for now. - timeToLive = 2000L, - fadeOutEnabled = true, + angle = Angle.Companion.BOTTOM, + spread = Spread.ROUND, + shapes = listOf(Shape.Square, Shape.Circle), + size = listOf(Size(12)), speed = 2f, maxSpeed = 5f, - damping = 0.9f, - spread = 360, - position = Position.Relative(0.5, 0.3), - */ - emitter = Emitter(duration = 100).max(100) + fadeOutEnabled = true, + timeToLive = 2000L, + position = Position.Relative(0.0, 0.0).between(Position.Relative(1.0, 0.0)), ) + reset() start(party) } From d0155c9890aa5cebad9b196247f5739229b332d2 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Mon, 4 Apr 2022 16:17:41 +0100 Subject: [PATCH 043/368] Adds proper handling of audio seek bar --- .../home/room/detail/TimelineFragment.kt | 5 +- ...MessageHelper.kt => AudioMessageHelper.kt} | 20 ++++++-- .../detail/composer/MessageComposerAction.kt | 1 + .../composer/MessageComposerViewModel.kt | 36 ++++++++------- .../timeline/TimelineEventController.kt | 2 + .../timeline/factory/MessageItemFactory.kt | 3 ++ .../helper/AudioMessagePlaybackTracker.kt | 6 ++- .../detail/timeline/item/MessageAudioItem.kt | 46 +++++++++++++++++-- .../layout/item_timeline_event_audio_stub.xml | 11 +++-- 9 files changed, 99 insertions(+), 31 deletions(-) rename vector/src/main/java/im/vector/app/features/home/room/detail/composer/{VoiceMessageHelper.kt => AudioMessageHelper.kt} (92%) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 4907f99d25..57dbd07daf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -105,7 +105,6 @@ import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.onPermissionDeniedDialog -import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.core.utils.openLocation import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.core.utils.registerForPermissionsResult @@ -2080,6 +2079,10 @@ class TimelineFragment @Inject constructor( messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformMovedTo(eventId, duration, percentage)) } + override fun onAudioSeekBarMovedTo(eventId: String, duration: Int, percentage: Float) { + messageComposerViewModel.handle(MessageComposerAction.AudioSeekBarMovedTo(eventId, duration, percentage)) + } + private fun onShareActionClicked(action: EventSharedAction.Share) { when (action.messageContent) { is MessageTextContent -> shareText(requireContext(), action.messageContent.body) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt similarity index 92% rename from vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt rename to vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt index 3d263a9a97..f4cab3305d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/AudioMessageHelper.kt @@ -40,12 +40,13 @@ import javax.inject.Inject /** * Helper class to record audio for voice messages. */ -class VoiceMessageHelper @Inject constructor( +class AudioMessageHelper @Inject constructor( private val context: Context, private val playbackTracker: AudioMessagePlaybackTracker, voiceRecorderProvider: VoiceRecorderProvider ) { private var mediaPlayer: MediaPlayer? = null + private var currentPlayingId: String? = null private var voiceRecorder: VoiceRecorder = voiceRecorderProvider.provideVoiceRecorder() private val amplitudeList = mutableListOf() @@ -136,6 +137,7 @@ class VoiceMessageHelper @Inject constructor( mediaPlayer?.stop() stopPlaybackTicker() stopRecordingAmplitudes() + currentPlayingId = null if (playbackState is AudioMessagePlaybackTracker.Listener.State.Playing) { playbackTracker.pausePlayback(id) } else { @@ -163,6 +165,7 @@ class VoiceMessageHelper @Inject constructor( seekTo(currentPlaybackTime) } } + currentPlayingId = id } catch (failure: Throwable) { Timber.e(failure, "Unable to start playback") throw VoiceFailure.UnableToPlay(failure) @@ -174,14 +177,21 @@ class VoiceMessageHelper @Inject constructor( playbackTracker.pausePlayback(AudioMessagePlaybackTracker.RECORDING_ID) mediaPlayer?.stop() stopPlaybackTicker() + currentPlayingId = null } fun movePlaybackTo(id: String, percentage: Float, totalDuration: Int) { val toMillisecond = (totalDuration * percentage).toInt() - playbackTracker.updateCurrentPlaybackTime(id, toMillisecond, percentage) + playbackTracker.pauseAllPlaybacks() - stopPlayback() - playbackTracker.pausePlayback(id) + if (currentPlayingId == id) { + mediaPlayer?.seekTo(toMillisecond) + playbackTracker.updatePlayingAtPlaybackTime(id, toMillisecond, percentage) + } else { + mediaPlayer?.pause() + playbackTracker.updatePausedAtPlaybackTime(id, toMillisecond, percentage) + stopPlaybackTicker() + } } private fun startRecordingAmplitudes() { @@ -233,7 +243,7 @@ class VoiceMessageHelper @Inject constructor( val currentPosition = mediaPlayer?.currentPosition ?: 0 val totalDuration = mediaPlayer?.duration ?: 0 val percentage = currentPosition.toFloat() / totalDuration - playbackTracker.updateCurrentPlaybackTime(id, currentPosition, percentage) + playbackTracker.updatePlayingAtPlaybackTime(id, currentPosition, percentage) } else { playbackTracker.stopPlayback(id) stopPlaybackTicker() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerAction.kt index 091e9f7869..dca698ee52 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerAction.kt @@ -42,4 +42,5 @@ sealed class MessageComposerAction : VectorViewModelAction { data class EndAllVoiceActions(val deleteRecord: Boolean = true) : MessageComposerAction() data class VoiceWaveformTouchedUp(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction() data class VoiceWaveformMovedTo(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction() + data class AudioSeekBarMovedTo(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt index eb7d87c371..aabc319ee2 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/MessageComposerViewModel.kt @@ -73,7 +73,7 @@ class MessageComposerViewModel @AssistedInject constructor( private val vectorPreferences: VectorPreferences, private val commandParser: CommandParser, private val rainbowGenerator: RainbowGenerator, - private val voiceMessageHelper: VoiceMessageHelper, + private val audioMessageHelper: AudioMessageHelper, private val analyticsTracker: AnalyticsTracker, private val voicePlayerHelper: VoicePlayerHelper ) : VectorViewModel(initialState) { @@ -90,7 +90,6 @@ class MessageComposerViewModel @AssistedInject constructor( } override fun handle(action: MessageComposerAction) { - Timber.v("Handle action: $action") when (action) { is MessageComposerAction.EnterEditMode -> handleEnterEditMode(action) is MessageComposerAction.EnterQuoteMode -> handleEnterQuoteMode(action) @@ -110,6 +109,7 @@ class MessageComposerViewModel @AssistedInject constructor( is MessageComposerAction.OnEntersBackground -> handleEntersBackground(action.composerText) is MessageComposerAction.VoiceWaveformTouchedUp -> handleVoiceWaveformTouchedUp(action) is MessageComposerAction.VoiceWaveformMovedTo -> handleVoiceWaveformMovedTo(action) + is MessageComposerAction.AudioSeekBarMovedTo -> handleAudioSeekBarMovedTo(action) } } @@ -811,18 +811,18 @@ class MessageComposerViewModel @AssistedInject constructor( private fun handleStartRecordingVoiceMessage() { try { - voiceMessageHelper.startRecording(room.roomId) + audioMessageHelper.startRecording(room.roomId) } catch (failure: Throwable) { _viewEvents.post(MessageComposerViewEvents.VoicePlaybackOrRecordingFailure(failure)) } } private fun handleEndRecordingVoiceMessage(isCancelled: Boolean, rootThreadEventId: String? = null) { - voiceMessageHelper.stopPlayback() + audioMessageHelper.stopPlayback() if (isCancelled) { - voiceMessageHelper.deleteRecording() + audioMessageHelper.deleteRecording() } else { - voiceMessageHelper.stopRecording(convertForSending = true)?.let { audioType -> + audioMessageHelper.stopRecording(convertForSending = true)?.let { audioType -> if (audioType.duration > 1000) { room.sendMedia( attachment = audioType.toContentAttachmentData(isVoiceMessage = true), @@ -830,7 +830,7 @@ class MessageComposerViewModel @AssistedInject constructor( roomIds = emptySet(), rootThreadEventId = rootThreadEventId) } else { - voiceMessageHelper.deleteRecording() + audioMessageHelper.deleteRecording() } } } @@ -845,7 +845,7 @@ class MessageComposerViewModel @AssistedInject constructor( // Conversion can fail, fallback to the original file in this case and let the player fail for us val convertedFile = voicePlayerHelper.convertFile(audioFile) ?: audioFile // Play can fail - voiceMessageHelper.startOrPausePlayback(action.eventId, convertedFile) + audioMessageHelper.startOrPausePlayback(action.eventId, convertedFile) } catch (failure: Throwable) { _viewEvents.post(MessageComposerViewEvents.VoicePlaybackOrRecordingFailure(failure)) } @@ -853,34 +853,38 @@ class MessageComposerViewModel @AssistedInject constructor( } private fun handlePlayOrPauseRecordingPlayback() { - voiceMessageHelper.startOrPauseRecordingPlayback() + audioMessageHelper.startOrPauseRecordingPlayback() } private fun handleEndAllVoiceActions(deleteRecord: Boolean) { - voiceMessageHelper.clearTracker() - voiceMessageHelper.stopAllVoiceActions(deleteRecord) + audioMessageHelper.clearTracker() + audioMessageHelper.stopAllVoiceActions(deleteRecord) } private fun handleInitializeVoiceRecorder(attachmentData: ContentAttachmentData) { - voiceMessageHelper.initializeRecorder(attachmentData) + audioMessageHelper.initializeRecorder(attachmentData) setState { copy(voiceRecordingUiState = VoiceMessageRecorderView.RecordingUiState.Draft) } } private fun handlePauseRecordingVoiceMessage() { - voiceMessageHelper.pauseRecording() + audioMessageHelper.pauseRecording() } private fun handleVoiceWaveformTouchedUp(action: MessageComposerAction.VoiceWaveformTouchedUp) { - voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration) + audioMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration) } private fun handleVoiceWaveformMovedTo(action: MessageComposerAction.VoiceWaveformMovedTo) { - voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration) + audioMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration) + } + + private fun handleAudioSeekBarMovedTo(action: MessageComposerAction.AudioSeekBarMovedTo) { + audioMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration) } private fun handleEntersBackground(composerText: String) { // Always stop all voice actions. It may be playing in timeline or active recording - val playingAudioContent = voiceMessageHelper.stopAllVoiceActions(deleteRecord = false) + val playingAudioContent = audioMessageHelper.stopAllVoiceActions(deleteRecord = false) val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording } if (isVoiceRecording) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 023c28cdc7..981e5740d7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -148,6 +148,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec fun onVoiceWaveformTouchedUp(eventId: String, duration: Int, percentage: Float) fun onVoiceWaveformMovedTo(eventId: String, duration: Int, percentage: Float) + fun onAudioSeekBarMovedTo(eventId: String, duration: Int, percentage: Float) + fun onAddMoreReaction(event: TimelineEvent) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index adb5cfdda6..dc2266b154 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -341,6 +341,7 @@ class MessageItemFactory @Inject constructor( ): MessageAudioItem { val fileUrl = getAudioFileUrl(messageContent, informationData) val playbackControlButtonClickListener = createOnPlaybackButtonClickListener(messageContent, informationData, params) + val duration = messageContent.audioInfo?.duration ?: 0 return MessageAudioItem_() .attributes(attributes) @@ -349,6 +350,8 @@ class MessageItemFactory @Inject constructor( .playbackControlButtonClickListener(playbackControlButtonClickListener) .audioMessagePlaybackTracker(audioMessagePlaybackTracker) .isLocalFile(localFilesHelper.isLocalFile(fileUrl)) + .fileSize(messageContent.audioInfo?.size ?: 0L) + .onSeek { params.callback?.onAudioSeekBarMovedTo(informationData.eventId, duration, it) } .mxcUrl(fileUrl) .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder) .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt index fb000d7b70..0312ac9e6f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/AudioMessagePlaybackTracker.kt @@ -104,10 +104,14 @@ class AudioMessagePlaybackTracker @Inject constructor() { setState(id, Listener.State.Idle) } - fun updateCurrentPlaybackTime(id: String, time: Int, percentage: Float) { + fun updatePlayingAtPlaybackTime(id: String, time: Int, percentage: Float) { setState(id, Listener.State.Playing(time, percentage)) } + fun updatePausedAtPlaybackTime(id: String, time: Int, percentage: Float) { + setState(id, Listener.State.Paused(time, percentage)) + } + fun updateCurrentRecording(id: String, amplitudeList: List) { setState(id, Listener.State.Recording(amplitudeList)) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 7748ccb03f..8d5810a60a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -22,6 +22,7 @@ import android.graphics.Paint import android.text.format.DateUtils import android.view.ViewGroup import android.widget.ImageButton +import android.widget.SeekBar import android.widget.TextView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute @@ -29,6 +30,7 @@ import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.onClick +import im.vector.app.core.utils.TextUtils import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder @@ -47,10 +49,16 @@ abstract class MessageAudioItem : AbsMessageItem() { @EpoxyAttribute var duration: Int = 0 + @EpoxyAttribute + var fileSize: Long = 0 + @EpoxyAttribute @JvmField var isLocalFile = false + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var onSeek: ((percentage: Float) -> Unit)? = null + @EpoxyAttribute lateinit var contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder @@ -63,12 +71,15 @@ abstract class MessageAudioItem : AbsMessageItem() { @EpoxyAttribute lateinit var audioMessagePlaybackTracker: AudioMessagePlaybackTracker + private var isUserSeeking = false + override fun bind(holder: Holder) { super.bind(holder) renderSendState(holder.rootLayout, null) - bindFilenameViewAttributes(holder) + bindViewAttributes(holder) bindUploadState(holder) applyLayoutTint(holder) + bindSeekBar(holder) holder.audioPlaybackControlButton.setOnClickListener { playbackControlButtonClickListener?.invoke(it) } renderStateBasedOnAudioPlayback(holder) } @@ -93,10 +104,30 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.mainLayout.backgroundTintList = ColorStateList.valueOf(backgroundTint) } - private fun bindFilenameViewAttributes(holder: Holder) { + private fun bindViewAttributes(holder: Holder) { holder.filenameView.text = filename holder.filenameView.onClick(attributes.itemClickListener) holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG) + holder.audioPlaybackDuration.text = formatPlaybackTime(duration) + holder.fileSize.text = TextUtils.formatFileSize(holder.rootLayout.context, fileSize, true) + } + + private fun bindSeekBar(holder: Holder) { + holder.audioSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + holder.audioPlaybackTime.text = formatPlaybackTime( + (duration * (progress.toFloat() / 100)).toInt() + ) + } + override fun onStartTrackingTouch(seekBar: SeekBar) { + isUserSeeking = true + } + override fun onStopTrackingTouch(seekBar: SeekBar) { + isUserSeeking = false + val percentage = seekBar.progress.toFloat() / 100 + onSeek?.invoke(percentage) + } + }) } private fun renderStateBasedOnAudioPlayback(holder: Holder) { @@ -117,13 +148,18 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message, filename) holder.audioPlaybackTime.text = formatPlaybackTime(duration) + holder.audioSeekBar.progress = 0 } private fun renderPlayingState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Playing) { holder.audioPlaybackControlButton.setImageResource(R.drawable.ic_play_pause_pause) holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_pause_audio_message, filename) - holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) + + if (!isUserSeeking) { + holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) + holder.audioSeekBar.progress = (state.percentage * 100).toInt() + } } private fun renderPausedState(holder: Holder, state: AudioMessagePlaybackTracker.Listener.State.Paused) { @@ -131,6 +167,7 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.audioPlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_audio_message, filename) holder.audioPlaybackTime.text = formatPlaybackTime(state.playbackTime) + holder.audioSeekBar.progress = (state.percentage * 100).toInt() } private fun formatPlaybackTime(time: Int) = DateUtils.formatElapsedTime((time / 1000).toLong()) @@ -151,6 +188,9 @@ abstract class MessageAudioItem : AbsMessageItem() { val audioPlaybackControlButton by bind(R.id.audioPlaybackControlButton) val audioPlaybackTime by bind(R.id.audioPlaybackTime) val progressLayout by bind(R.id.messageFileUploadProgressLayout) + val fileSize by bind(R.id.fileSize) + val audioPlaybackDuration by bind(R.id.audioPlaybackDuration) + val audioSeekBar by bind(R.id.audioSeekBar) } companion object { diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index ee57046bf4..092810e446 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -45,10 +45,11 @@ tools:text="Filename.mp3" /> Date: Tue, 5 Apr 2022 12:49:12 +0300 Subject: [PATCH 044/368] Send live location data. --- .../sdk/api/session/events/model/EventType.kt | 11 +--- .../livelocation/LiveLocationBeaconContent.kt | 2 +- .../message/MessageLiveLocationContent.kt | 52 +++++++++++++++++++ .../session/room/model/message/MessageType.kt | 5 +- .../sdk/api/session/room/send/SendService.kt | 9 ++++ .../session/room/send/DefaultSendService.kt | 6 +++ .../room/send/LocalEchoEventFactory.kt | 27 ++++++++++ .../location/LocationSharingService.kt | 17 ++++-- 8 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.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 855801e79e..fa3a9f6acd 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 @@ -49,7 +49,8 @@ object EventType { const val STATE_ROOM_JOIN_RULES = "m.room.join_rules" const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access" const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels" - private const val STATE_ROOM_BEACON_INFO_PREFIX = "org.matrix.msc3489.beacon_info." + val STATE_ROOM_BEACON_INFO = listOf("org.matrix.msc3672.beacon_info", "m.beacon_info") + val BEACON_LOCATION_DATA = listOf("org.matrix.msc3672.beacon", "m.beacon") const val STATE_SPACE_CHILD = "m.space.child" @@ -121,12 +122,4 @@ object EventType { type == CALL_REJECT || type == CALL_REPLACES } - - /** - * Returns an event type like org.matrix.msc3489.beacon_info.@userid:matrix.org.1648814272273 - */ - fun generateBeaconInfoStateEventType(userId: String): String { - val uniqueId = System.currentTimeMillis() - return "$STATE_ROOM_BEACON_INFO_PREFIX$userId.$uniqueId" - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt index e08d5b629b..a4551d462e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt @@ -26,7 +26,7 @@ data class LiveLocationBeaconContent( /** * Indicates user's intent to share ephemeral location. */ - @Json(name = "org.matrix.msc3489.beacon_info") val unstableBeaconInfo: BeaconInfo? = null, + @Json(name = "org.matrix.msc3672.beacon_info") val unstableBeaconInfo: BeaconInfo? = null, @Json(name = "m.beacon_info") val beaconInfo: BeaconInfo? = null, /** * Beacon creation timestamp. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt new file mode 100644 index 0000000000..e5de46b3ef --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2022 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.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Content +import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent + +@JsonClass(generateAdapter = true) +data class MessageLiveLocationContent( + /** + * Local message type, not from server + */ + @Transient + override val msgType: String = MessageType.MSGTYPE_LIVE_LOCATION, + + @Json(name = "body") override val body: String = "", + @Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null, + @Json(name = "m.new_content") override val newContent: Content? = null, + + /** + * See [MSC3488](https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md) + */ + @Json(name = "org.matrix.msc3488.location") val unstableLocationInfo: LocationInfo? = null, + @Json(name = "m.location") val locationInfo: LocationInfo? = null, + + /** + * Exact time that the data in the event refers to (milliseconds since the UNIX epoch) + */ + @Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null, + @Json(name = "m.ts") val ts: Long? = null +) : MessageContent { + + fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo + + fun getBestTs() = ts ?: unstableTs +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt index 2a6138ae60..280699cde8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageType.kt @@ -33,10 +33,13 @@ object MessageType { const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker" // Fake message types for poll events to be able to inherit them from MessageContent - // Because poll events are not message events and they don't hanve msgtype field + // Because poll events are not message events and they don't have msgtype field const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start" const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response" const val MSGTYPE_CONFETTI = "nic.custom.confetti" const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall" + + // Fake message types for live location events to be able to inherit them from MessageContent + const val MSGTYPE_LIVE_LOCATION = "org.matrix.android.sdk.livelocation" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt index 9f8b1d93d7..af7ab11df1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/send/SendService.kt @@ -146,6 +146,15 @@ interface SendService { */ fun sendLocation(latitude: Double, longitude: Double, uncertainty: Double?, isUserLocation: Boolean): Cancelable + /** + * Send a live location event to the room. beacon_info state event has to be sent before sending live location updates. + * @param beaconInfoEventId event id of the initial beacon info state event + * @param latitude required latitude of the location + * @param longitude required longitude of the location + * @param uncertainty Accuracy of the location in meters + */ + fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable + /** * Remove this failed message from the timeline * @param localEcho the unsent local echo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index 31c7254ed5..c315bf9f7a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -134,6 +134,12 @@ internal class DefaultSendService @AssistedInject constructor( .let { sendEvent(it) } } + override fun sendLiveLocation(beaconInfoEventId: String, latitude: Double, longitude: Double, uncertainty: Double?): Cancelable { + return localEchoEventFactory.createLiveLocationEvent(beaconInfoEventId, roomId, latitude, longitude, uncertainty) + .also { createLocalEcho(it) } + .let { sendEvent(it) } + } + override fun redactEvent(event: Event, reason: String?): Cancelable { // TODO manage media/attachements? val redactionEcho = localEchoEventFactory.createRedactEvent(roomId, event.eventId!!, reason) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index 0ba95cc1fb..d7d74ca601 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -43,6 +43,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollConte import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent import org.matrix.android.sdk.api.session.room.model.message.MessageFormat import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent @@ -242,6 +243,32 @@ internal class LocalEchoEventFactory @Inject constructor( return createMessageEvent(roomId, content) } + fun createLiveLocationEvent(beaconInfoEventId: String, + roomId: String, + latitude: Double, + longitude: Double, + uncertainty: Double?): Event { + val geoUri = buildGeoUri(latitude, longitude, uncertainty) + val content = MessageLiveLocationContent( + body = geoUri, + relatesTo = RelationDefaultContent( + type = RelationType.REFERENCE, + eventId = beaconInfoEventId + ), + unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri), + unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + ) + val localId = LocalEcho.createLocalEchoId() + return Event( + roomId = roomId, + originServerTs = dummyOriginServerTs(), + senderId = userId, + eventId = localId, + type = EventType.BEACON_LOCATION_DATA.first(), + content = content.toContent(), + unsignedData = UnsignedData(age = null, transactionId = localId)) + } + fun createReplaceTextOfReply(roomId: String, eventReplaced: TimelineEvent, originalEvent: TimelineEvent, diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 063cc3caa6..f9a32ad549 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -29,8 +29,9 @@ import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.events.model.EventType.generateBeaconInfoStateEventType +import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent +import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.livelocation.BeaconInfo import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import timber.log.Timber @@ -104,12 +105,11 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { unstableTimestampAsMilliseconds = clock.epochMillis() ).toContent() - val eventType = generateBeaconInfoStateEventType(session.myUserId) val stateKey = session.myUserId session .getRoom(roomArgs.roomId) ?.sendStateEvent( - eventType = eventType, + eventType = EventType.STATE_ROOM_BEACON_INFO.first(), stateKey = stateKey, body = beaconContent ) @@ -143,6 +143,17 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onLocationUpdate(locationData: LocationData) { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") + roomArgsList.forEach { roomArg -> + val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomArg.roomId) + room?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first())?.let { beaconInfoEvent -> + room.sendLiveLocation( + beaconInfoEventId = beaconInfoEvent.eventId!!, + latitude = locationData.latitude, + longitude = locationData.longitude, + uncertainty = locationData.uncertainty + ) + } + } } override fun onLocationProviderIsNotAvailable() { From f00fc1e5ab04a09b604e27c55f94f7128c2c68b5 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 12:55:50 +0300 Subject: [PATCH 045/368] Changelog added. --- changelog.d/5697.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5697.feature diff --git a/changelog.d/5697.feature b/changelog.d/5697.feature new file mode 100644 index 0000000000..47504084f4 --- /dev/null +++ b/changelog.d/5697.feature @@ -0,0 +1 @@ +Live Location Sharing - Send location data \ No newline at end of file From db45ebd012ea9038ddfd2c2d5267483711e4644e Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 13:05:09 +0300 Subject: [PATCH 046/368] Lint fixes. --- .../im/vector/app/features/location/LocationSharingService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index f9a32ad549..38e2b3e6a6 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -31,7 +31,6 @@ import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent -import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.livelocation.BeaconInfo import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import timber.log.Timber From dbb43fe046f7105660a0ca8e4a5cfcba7a3fe187 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Tue, 5 Apr 2022 15:42:01 +0300 Subject: [PATCH 047/368] Refactor code to avoid force unwrapping. --- .../location/LocationSharingService.kt | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 38e2b3e6a6..81aeeee602 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -142,19 +142,28 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { override fun onLocationUpdate(locationData: LocationData) { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") + + // Emit location update to all rooms in which live location sharing is active roomArgsList.forEach { roomArg -> - val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomArg.roomId) - room?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first())?.let { beaconInfoEvent -> - room.sendLiveLocation( - beaconInfoEventId = beaconInfoEvent.eventId!!, - latitude = locationData.latitude, - longitude = locationData.longitude, - uncertainty = locationData.uncertainty - ) - } + sendLiveLocation(roomArg.roomId, locationData) } } + private fun sendLiveLocation(roomId: String, locationData: LocationData) { + val room = activeSessionHolder.getSafeActiveSession()?.getRoom(roomId) + room + ?.getStateEvent(EventType.STATE_ROOM_BEACON_INFO.first()) + ?.eventId + ?.let { + room.sendLiveLocation( + beaconInfoEventId = it, + latitude = locationData.latitude, + longitude = locationData.longitude, + uncertainty = locationData.uncertainty + ) + } + } + override fun onLocationProviderIsNotAvailable() { stopForeground(true) stopSelf() From 3d64f13c7e5170ea2e752acb763d2bdf39b0e6a8 Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:04:45 +0000 Subject: [PATCH 048/368] Translated using Weblate (Polish) Currently translated at 92.6% (2030 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index fe98d6b6a1..8974f1798c 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2334,4 +2334,5 @@ Odkrywanie (%s) Dokończ konfigurację odkrywania. Usuń wszystkie nieudane wiadomości + Nie znasz swojego hasła zapasowego klucza\? Możesz %s \ No newline at end of file From 232c400c082c8c141cf668e84ab3cc907afaaa8e Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:01:35 +0000 Subject: [PATCH 049/368] Translated using Weblate (Polish) Currently translated at 92.6% (2030 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 95 ++++++++++++----------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 8974f1798c..df3253a462 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -287,22 +287,21 @@ ${app_name} wymaga dostępu do kamery i mikrofonu, aby przeprowadzać rozmowy wideo. \n \nPrzyznaj dostęp w następnym oknie. - Lista uczestników + Członkowie - 1 członek - kilku członków + %d członek + %d członków %d członków - + %d członków Nie będziesz w stanie cofnąć tej zmiany, ponieważ przyznajesz użytkownikowi uprawnienia równe swoim. -\n \nJesteś pewien\? - Zbanowanie użytkownika usunie go z tego pokoju i uniemożliwi ponowne dołączenie. + Zablokowanie użytkownika usunie go z tego pokoju i uniemożliwi ponowne dołączenie. - Nowa wiadomość - Kilka nowych wiadomości + %d nowa wiadomość + %d nowe wiadomości %d nowych wiadomości - + %d nowych wiadomości Może to oznaczać że ktoś zakłóca twoje połączenie, lub Twój telefon nie ufa certyfikatowi dostarczonemu przez zdalny serwer. Jeśli administrator serwera oświadczył, że jest to oczekiwane, upewnij się, że poniższy odcisk palca odpowiada odciskowi palca dostarczonemu przez niego. @@ -539,7 +538,8 @@ Przywrócono kopię zapasową z %d kluczem. Przywrócono kopię zapasową z %d kluczami. - + Przywrócono kopię zapasową z %d kluczami. + Przywrócono kopię zapasową z %d kluczami. Użyj kopii zapasowej klucza Uruchamianie… (%1$d z %2$d) @@ -736,7 +736,7 @@ \nMenedżerzy integracji odbierają dane konfiguracji, modyfikują widżety, wysyłają zaproszenia do pokojów i ustawiają poziomy uprawnień w Twoim imieniu. Pokaż podgląd linków wewnątrz czatu jeśli twój serwer wspiera tę funkcję. Formatuj wiadomości używając składni Markdown zanim zostaną wysłane. Pozwala to na zaawansowane formatowanie takie jak używanie asterysków do wyświetlania tekstu w kursywie. - Nie wpływa to na zaproszenia, wyrzucenia oraz bany. + Zaproszenia, usunięcia i bany pozostają nienaruszone. Wysyłaj wiadomości za pomocą klawisza enter Przycisk enter na klawiaturze programowej wyśle wiadomość zamiast wprowadzania łamanania linii Znajdź @@ -747,13 +747,14 @@ %1$s: %2$d wiadomość %1$s: %2$d wiadomości - - + %1$s: %2$d wiadomości + %1$s: %2$d wiadomości %d powiadomienie - %d powiadomień - + %d powiadomienia + %d powiadomień + %d powiadomień Nowe wydarzenie Nowe wiadomości @@ -825,9 +826,10 @@ Zarządzaj w Kopii Zapasowej Kluczy Tworzenei kopii zapasowej kluczy… - Kopiowanie %d klucza… - Kopiowanie %d kluczy… - + Tworzenie kopii zapasowej %d klucza… + Tworzenie kopii zapasowej %d kluczy… + Tworzenie kopii zapasowej %d kluczy… + Tworzenie kopii zapasowej %d kluczy… Niektóre powiadomienia są wyłączone w osobistej konfiguracji. Usługi Google Play są aktualne. @@ -888,8 +890,8 @@ %d użytkownik odczytał %d użytkowników odczytało - - + %d użytkowników odczytało + %d użytkowników odczytało Wystąpił błąd poczas otrzymywania załącznika. Nie można obsłużyć otrzymanych danych @@ -999,9 +1001,10 @@ Wprowadzony kod jest nieprawidłowy. Sprawdź. Nieaktualny serwer domowy - Zostało wysłane zbyt wiele żądań. Możesz spróbować ponownie za %1$d sekundę… - Zostało wysłane zbyt wiele żądań. Możesz spróbować ponownie za %1$d sekundy… - + Wysłano zbyt wiele próśb. Możesz spróbować ponownie za %1$d sekundę… + Wysłano zbyt wiele próśb. Możesz spróbować ponownie za %1$d sekundy… + Wysłano zbyt wiele próśb. Możesz spróbować ponownie za %1$d sekund… + Wysłano zbyt wiele próśb. Możesz spróbować ponownie za %1$d sekund… Wylogowałeś(-łaś) się Mogło to się stać z wielu powodów: @@ -1081,8 +1084,9 @@ Dowiedz się więcej Jedna osoba - %1$d osób - + %1$d osoby + %1$d osób + %1$d osób Opuszczanie pokoju… Administratorzy @@ -1104,8 +1108,7 @@ Oś czasu Edycja wiadomości Aktywować szyfrowanie\? - Raz włączone szyfrowanie w pokoju nie może zostać wyłączone. Wiadomości wysłane w zaszyfrowanym pokoju nie są widziane przez serwer, a jedynie przez uczestników w pokoju. -\nAktywowanie szyfrowania może uniemożliwić wielu botom i mostom prawidłowe działanie. + Raz włączone szyfrowanie w pokoju nie może zostać wyłączone. Wiadomości wysłane w zaszyfrowanym pokoju nie są widziane przez serwer, a jedynie przez uczestników w pokoju. Aktywowanie szyfrowania może uniemożliwić wielu botom i mostkom prawidłowe działanie. Aktywuj szyfrowanie Aby być bezpiecznym, zweryfikuj %s poprzez sprawdzenie jednorazowego kodu. Aby być bezpiecznym, zrób to osobiście lub użyj innej metody komunikacji. @@ -1131,8 +1134,9 @@ Zweryfikuj tą sesję aby oznaczyć ją jako zaufaną i przyznać jej dostęp do zaszyfrowanych wiadomości. Jeżeli nie logowałeś(-łaś) się do tej sesji, twoje konto mogło zostać naruszone: %d aktywna sesja - %d aktywnych sesji - + %d aktywne sesje + %d aktywnych sesji + %d aktywnych sesji Zweryfikuj tą sesję Otwórz obecną sesję i użyj jej do zweryfikowania obecnej, przyznając jej dostęp do zaszyfrowanych wiadomości. @@ -1161,7 +1165,8 @@ Wyślij obraz w oryginalnym rozmiarze Wyślij obrazy w oryginalnym rozmiarze - + Wyślij obrazów w oryginalnym rozmiarze + Wyślij obrazów w oryginalnym rozmiarze Potwierdź Usunięcie Jesteś pewny(-na), że chcesz usunąć to wydarzenie\? Jeżeli usuniesz nazwę pokoju lub zmienisz temat, wciąż będzie możliwe cofnięcie zmiany. @@ -1354,9 +1359,9 @@ WIDOK %d sekunda - %d sek. - %d sek. - %d sek. + %d sekundy + %d sekund + %d sekund Aplikacja odebrała PUSH Przetestuj powiadomienia push @@ -1581,10 +1586,10 @@ Naklejka %1$s wymaga otrzymania zaproszenia do dołączenia. - %1$s, %2$s i %3$d czyta - %1$s, %2$s i %3$d czytają - - + %1$s, %2$s i %3$d inny czyta + %1$s, %2$s i %3$d innych czyta + %1$s, %2$s i %3$d innych czyta + %1$s, %2$s i %3$d innych czyta Nie udało się uzyskać widoczności katalogu bieżącego pokoju (%1$s). Skonfiguruj na tym urządzeniu @@ -1825,7 +1830,7 @@ Uprawnienia Przestrzeni Uprawnienia pokoju Odblokowanie użytkownika pozwoli mu na ponowne dołączenie do tej przestrzeni. - Blokowanie użytkowników wyrzuci ich z tej przestrzeni i uniemożliwi im dołączenie ponownie. + Zablokowanie użytkownika usunie go z tego miejsca i uniemożliwi ponowne dołączenie. Ten pokój jest prywatny. Nie będziesz w stanie dołączyć bez zaproszenia. Zakańczanie połączenia… Brak odpowiedzi @@ -1975,7 +1980,7 @@ Nie będziesz w stanie ponownie dołączyć, do momentu kiedy nie zostaniesz ponownie zaproszony. Jesteś jedyną osoba tutaj. Jeśli wyjdziesz, nikt nie będzie w stanie dołączyć w przyszłości, włączając Ciebie. Czy jesteś pewny, że chcesz opuścić %s\? - Opuść przestrzeń + Opuść Dodawaj pokoje Przeglądaj pokoje Ukończ konfigurację @@ -1985,7 +1990,7 @@ \nSpróbuj ponownie później lub zapytaj administratora tego pokoju czy masz dostęp. Dołącz pomimo to Dołącz do przestrzeni - Stwórz przestrzeń + Utwórz przestrzeń Na razie pomiń Dołącz do mojej przestrzeni %1$s %2$s Nie będą częścią %s @@ -2016,7 +2021,7 @@ Ja i moi znajomi Prywatna przestrzeń do organizacji Twoich pokojów Tylko ja - Upewnij się, że odpowiednie osoby mają dostęp do %s. Możesz zmienić to później. + Upewnij się, że odpowiednie osoby mają dostęp do %s. Z kim pracujesz\? Aby dołączyć do istniejącej przestrzeni, potrzebujesz zaproszenia. Możesz zmienić to później @@ -2028,10 +2033,10 @@ \nCzekanie na odpowiedź serwera… Pusty pokój (było %s) - %1$s, %2$s, %3$s i %4$d innych + %1$s, %2$s, %3$s i %4$d inny %1$s, %2$s, %3$s i %4$d innych - - + %1$s, %2$s, %3$s i %4$d innych + %1$s, %2$s, %3$s i %4$d innych %1$s, %2$s, %3$s i %4$s Niestandardowe @@ -2208,7 +2213,7 @@ Dodano %1$s jako adresy dla tego pokoju. pojedyncze logowanie - Pokój opuszczony! + Pokój został opuszczony! Błąd podczas wyszukiwania numeru telefonu Przekaż %1$s dotknij by powrócić From 8f751af7e49164335d7339ffa44b51635325d2c5 Mon Sep 17 00:00:00 2001 From: m1chj <8hm0nutuhsa9@opayq.com> Date: Tue, 5 Apr 2022 12:52:35 +0000 Subject: [PATCH 050/368] Translated using Weblate (Polish) Currently translated at 92.6% (2030 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index df3253a462..c8578f359e 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2110,7 +2110,7 @@ Zdecyduj kto może odnaleźć i dołączyć do tego pokoju. Dotknij, aby edytować przestrzenie Wybierz przestrzenie - Zdecyduj które przestrzenie mogą mieć dostęp do tego pokoju. Członkowie wybranej przestrzeni będą mogli ją odnaleźć i dołączyć do Pokoju przez nazwę. + Zdecyduj, które przestrzenie mogą mieć dostęp do tego pokoju. Członkowie zaznaczonej przestrzeni będą mogli odnaleźć i dołączyć do nazwy pokoju. Przestrzenie mogące uzyskać dostęp Zezwól użytkownikom przestrzeni na znalezienie i dostęp. Ulepszenia pokoju From b19b10015057131c2760c9007981a8f9e6d1bc0d Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:05:31 +0000 Subject: [PATCH 051/368] Translated using Weblate (Polish) Currently translated at 92.7% (2031 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index c8578f359e..8c6a559c42 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2340,4 +2340,10 @@ Dokończ konfigurację odkrywania. Usuń wszystkie nieudane wiadomości Nie znasz swojego hasła zapasowego klucza\? Możesz %s + + wpis + wpisów + wpisy + wpisy + \ No newline at end of file From 2b9dd885546d056907a1ee49d531761b61e920a8 Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:05:13 +0000 Subject: [PATCH 052/368] Translated using Weblate (Polish) Currently translated at 92.7% (2031 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 8c6a559c42..236ea514c0 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2339,7 +2339,7 @@ Odkrywanie (%s) Dokończ konfigurację odkrywania. Usuń wszystkie nieudane wiadomości - Nie znasz swojego hasła zapasowego klucza\? Możesz %s + Nie znasz hasła swojego zapasowego klucza\? Możesz %s. wpis wpisów From da32eb8a99c98aeadfdbfb846a74a2a289b5183e Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:10:11 +0000 Subject: [PATCH 053/368] Translated using Weblate (Polish) Currently translated at 93.1% (2039 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 236ea514c0..95e7115b44 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2346,4 +2346,12 @@ wpisy wpisy + + %d osoba którą znasz już dołączyła + %d osoby które znasz już dołączyły + + + + Brakujący typ wiadomości + Brak zawartości \ No newline at end of file From be3b75e1368cf6bdd87bb64509397866df6e86e1 Mon Sep 17 00:00:00 2001 From: m1chj <8hm0nutuhsa9@opayq.com> Date: Tue, 5 Apr 2022 13:07:46 +0000 Subject: [PATCH 054/368] Translated using Weblate (Polish) Currently translated at 93.1% (2039 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 95e7115b44..ffec372c06 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2333,8 +2333,8 @@ Uzyskaj pomoc w korzystaniu z ${app_name} Nie masz uprawnień by dołączyć do tego pokoju Typ - Niedostępny - Dostępny + Offline + Online Pokaż znaczniki odczytania Odkrywanie (%s) Dokończ konfigurację odkrywania. @@ -2354,4 +2354,7 @@ Brakujący typ wiadomości Brak zawartości + Wyślij wydarzenie niestandardowe + Ostrzegawczy poziom zaufania + Upewnij się najpierw \ No newline at end of file From 3d9974ed82aaafa03de75002bbc3ab95296c489d Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:07:23 +0000 Subject: [PATCH 055/368] Translated using Weblate (Polish) Currently translated at 93.1% (2039 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index ffec372c06..c4aa40884a 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2335,16 +2335,16 @@ Typ Offline Online - Pokaż znaczniki odczytania + Wyświetl znaczniki odczytania Odkrywanie (%s) Dokończ konfigurację odkrywania. Usuń wszystkie nieudane wiadomości Nie znasz hasła swojego zapasowego klucza\? Możesz %s. - wpis - wpisów - wpisy - wpisy + %d wpis + %d wpisy + %d wpisów + %d wpisów %d osoba którą znasz już dołączyła @@ -2357,4 +2357,6 @@ Wyślij wydarzenie niestandardowe Ostrzegawczy poziom zaufania Upewnij się najpierw + Nie sprawdzono + Sprawdzono \ No newline at end of file From 2e81f2ab2af08d46c5a058c771783dd0fa732f51 Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:11:25 +0000 Subject: [PATCH 056/368] Translated using Weblate (Polish) Currently translated at 93.1% (2041 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index c4aa40884a..9965c6e629 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2349,8 +2349,8 @@ %d osoba którą znasz już dołączyła %d osoby które znasz już dołączyły - - + + Brakujący typ wiadomości Brak zawartości @@ -2359,4 +2359,5 @@ Upewnij się najpierw Nie sprawdzono Sprawdzono + Aby zmiany weszły w życie, należy uruchomić ponownie aplikację. \ No newline at end of file From 5e035267178c79c500d2137bd3d5da0fb676b19d Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:10:57 +0000 Subject: [PATCH 057/368] Translated using Weblate (Polish) Currently translated at 93.1% (2041 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 9965c6e629..eef2755808 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2347,10 +2347,10 @@ %d wpisów - %d osoba którą znasz już dołączyła - %d osoby które znasz już dołączyły - - + %d osoba, którą znasz, już dołączyła + %d osoby, które znasz, już dołączyły + %d osób, które znasz, już dołączyły + %d osób, które znasz, już dołączyły Brakujący typ wiadomości Brak zawartości @@ -2360,4 +2360,5 @@ Nie sprawdzono Sprawdzono Aby zmiany weszły w życie, należy uruchomić ponownie aplikację. + Włącz matematykę LaTeX \ No newline at end of file From aaf3f080d02faf3ae2a174230056f3da5db51fff Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:11:56 +0000 Subject: [PATCH 058/368] Translated using Weblate (Polish) Currently translated at 93.2% (2042 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index eef2755808..5766aafc73 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2361,4 +2361,5 @@ Sprawdzono Aby zmiany weszły w życie, należy uruchomić ponownie aplikację. Włącz matematykę LaTeX + Zakończ ankietę. \ No newline at end of file From 67159f7dc9e33fe8dc5b67a60de092d8f79d3a27 Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:11:46 +0000 Subject: [PATCH 059/368] Translated using Weblate (Polish) Currently translated at 93.2% (2042 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 5766aafc73..8609f13af4 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2359,7 +2359,7 @@ Upewnij się najpierw Nie sprawdzono Sprawdzono - Aby zmiany weszły w życie, należy uruchomić ponownie aplikację. + Aby zmiany zostały zastosowane, należy uruchomić ponownie aplikację. Włącz matematykę LaTeX Zakończ ankietę. \ No newline at end of file From 99ad49498d8e953e859566faee621df1ca4c2c1d Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:12:09 +0000 Subject: [PATCH 060/368] Translated using Weblate (Polish) Currently translated at 93.2% (2043 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 8609f13af4..6328703475 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2362,4 +2362,5 @@ Aby zmiany zostały zastosowane, należy uruchomić ponownie aplikację. Włącz matematykę LaTeX Zakończ ankietę. + Zakończyć ankietę\? \ No newline at end of file From 7dd56244a974c76300fcb9ced9d8339b48133a3f Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:12:03 +0000 Subject: [PATCH 061/368] Translated using Weblate (Polish) Currently translated at 93.2% (2043 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 6328703475..2d6a1d2abb 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2361,6 +2361,6 @@ Sprawdzono Aby zmiany zostały zastosowane, należy uruchomić ponownie aplikację. Włącz matematykę LaTeX - Zakończ ankietę. + Zakończ ankietę Zakończyć ankietę\? \ No newline at end of file From bad0c0a2e1e823346d6d4952e84a10e5b4ead3b3 Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:12:32 +0000 Subject: [PATCH 062/368] Translated using Weblate (Polish) Currently translated at 93.3% (2044 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index 2d6a1d2abb..f829f7f307 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2363,4 +2363,5 @@ Włącz matematykę LaTeX Zakończ ankietę Zakończyć ankietę\? + To powstrzyma użytkowników od głosowania i wyświetli finalny wynik ankiety. \ No newline at end of file From 157a14fdc24b748c57384e95b4b318f84fc2bb7e Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:12:20 +0000 Subject: [PATCH 063/368] Translated using Weblate (Polish) Currently translated at 93.3% (2044 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index f829f7f307..e4b7b28a01 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2362,6 +2362,6 @@ Aby zmiany zostały zastosowane, należy uruchomić ponownie aplikację. Włącz matematykę LaTeX Zakończ ankietę - Zakończyć ankietę\? + Czy zakończyć ankietę\? To powstrzyma użytkowników od głosowania i wyświetli finalny wynik ankiety. \ No newline at end of file From 76b2cfdf40a8c851821d46f839fb3f3261ad301e Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 5 Apr 2022 14:30:57 +0100 Subject: [PATCH 064/368] Readds onPermissionDeniedSnackbar import in TimelineFragment --- .../im/vector/app/features/home/room/detail/TimelineFragment.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 57dbd07daf..9c77df53ef 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -105,6 +105,7 @@ import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.onPermissionDeniedDialog +import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.core.utils.openLocation import im.vector.app.core.utils.openUrlInExternalBrowser import im.vector.app.core.utils.registerForPermissionsResult From dc36301070e754952f447a2480fa86ce68663f4a Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 5 Apr 2022 16:01:37 +0100 Subject: [PATCH 065/368] Improves accessibility on MessageAudioItem --- .../detail/timeline/item/MessageAudioItem.kt | 20 +++++++++++++++++-- vector/src/main/res/values/strings.xml | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 8d5810a60a..397f43546e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home.room.detail.timeline.item +import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.Paint @@ -105,11 +106,20 @@ abstract class MessageAudioItem : AbsMessageItem() { } private fun bindViewAttributes(holder: Holder) { + val formattedDuration = formatPlaybackTime(duration) + val formattedFileSize = TextUtils.formatFileSize(holder.rootLayout.context, fileSize, true) + val durationContentDescription = getPlaybackTimeContentDescription(holder.rootLayout.context, duration) + holder.filenameView.text = filename holder.filenameView.onClick(attributes.itemClickListener) holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG) - holder.audioPlaybackDuration.text = formatPlaybackTime(duration) - holder.fileSize.text = TextUtils.formatFileSize(holder.rootLayout.context, fileSize, true) + holder.audioPlaybackDuration.text = formattedDuration + holder.fileSize.text = holder.rootLayout.context.getString( + R.string.audio_message_file_size, formattedFileSize + ) + holder.mainLayout.contentDescription = holder.rootLayout.context.getString( + R.string.a11y_audio_message_item, filename, durationContentDescription, formattedFileSize + ) } private fun bindSeekBar(holder: Holder) { @@ -172,6 +182,12 @@ abstract class MessageAudioItem : AbsMessageItem() { private fun formatPlaybackTime(time: Int) = DateUtils.formatElapsedTime((time / 1000).toLong()) + private fun getPlaybackTimeContentDescription(context: Context, time: Int): String { + val formattedPlaybackTime = formatPlaybackTime(time) + val (minutes, seconds) = formattedPlaybackTime.split(":").map { it.toIntOrNull() ?: 0 } + return context.getString(R.string.a11y_audio_playback_duration, minutes, seconds) + } + override fun unbind(holder: Holder) { super.unbind(holder) contentUploadStateTrackerBinder.unbind(attributes.informationData.eventId) diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 90f6c5f7b1..1e50b32c6b 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2860,10 +2860,13 @@ Cannot reply or edit while voice message is active Voice Message (%1$s) + %1$s, %2$s, %3$s + %1$d minutes %2$d seconds Play %1$s Pause %1$s Unable to play %1$s %1$s (%2$s) + (%1$s) Anyone in %s will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. Anyone in a parent space will be able to find and join this room - no need to manually invite everyone. You’ll be able to change this in room settings anytime. From 355db98146c13649e67a359eaedf96a0efa34393 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Tue, 5 Apr 2022 16:09:48 +0100 Subject: [PATCH 066/368] Updates some text sizes and colours in audio timeline item --- .../res/layout/item_timeline_event_audio_stub.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml index 092810e446..2a6fbf5a9e 100644 --- a/vector/src/main/res/layout/item_timeline_event_audio_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_audio_stub.xml @@ -46,21 +46,21 @@ Date: Tue, 5 Apr 2022 19:08:11 +0300 Subject: [PATCH 067/368] Secure list while sending the location. --- .../im/vector/app/features/location/LocationSharingService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt index 81aeeee602..85679e34a7 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt @@ -144,7 +144,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback { Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}") // Emit location update to all rooms in which live location sharing is active - roomArgsList.forEach { roomArg -> + roomArgsList.toList().forEach { roomArg -> sendLiveLocation(roomArg.roomId, locationData) } } From 247f59899afdb2d645d8bcf7bc1e7b639790769a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Apr 2022 18:51:25 +0200 Subject: [PATCH 068/368] Small update of the remove string process. --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f3739be08d..053931cac5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,6 +144,8 @@ Instead, please comment the original string with: ```xml ``` +And add `tools:ignore="UnusedResources"` to the string, to let lint ignore that the string is not used. + The string will be removed during the next sync with Weblate. ### Accessibility From 02e3f0a3596fa961ccf777455392d22b2094995a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Apr 2022 18:53:40 +0200 Subject: [PATCH 069/368] Add step to handle the TODO in strings.xml --- .github/ISSUE_TEMPLATE/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/release.yml b/.github/ISSUE_TEMPLATE/release.yml index 582998d492..c22b00e2e9 100644 --- a/.github/ISSUE_TEMPLATE/release.yml +++ b/.github/ISSUE_TEMPLATE/release.yml @@ -18,6 +18,7 @@ body: - [ ] Weblate sync, fix lint issue if any (in a dedicated PR) - [ ] Check the update of the store descriptions (using Google Translate if necessary) to ensure that the changes are acceptable to be published to the stores. + - [ ] While Weblate is locked, and after the PR from Weblate has been merged, handle all the TODOs in the main `strings.xml` file - [ ] Run the script `./tools/release/pushPlayStoreMetaData.sh`. You can check in the GooglePlay console the Activity log to check the effect. ### Do the release From 02401f7f3d0159810c8254dbf4ce0ccbe83dd9ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 5 Apr 2022 22:37:16 +0200 Subject: [PATCH 070/368] Version++ --- 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 748bae8ff2..4a33e967cf 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -31,7 +31,7 @@ android { // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' - buildConfigField "String", "SDK_VERSION", "\"1.4.10\"" + buildConfigField "String", "SDK_VERSION", "\"1.4.12\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\"" diff --git a/vector/build.gradle b/vector/build.gradle index a7e347949b..4d93305fb9 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -18,7 +18,7 @@ ext.versionMinor = 4 // Note: even values are reserved for regular release, odd values for hotfix release. // When creating a hotfix, you should decrease the value, since the current value // is the value for the next regular release. -ext.versionPatch = 10 +ext.versionPatch = 12 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' From 415985094a384cdc1cff4b63c16448d339d5eb58 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 6 Apr 2022 07:25:30 +0100 Subject: [PATCH 071/368] Removes underline in audio filename --- .../features/home/room/detail/timeline/item/MessageAudioItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 397f43546e..57629fb17d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -112,7 +112,6 @@ abstract class MessageAudioItem : AbsMessageItem() { holder.filenameView.text = filename holder.filenameView.onClick(attributes.itemClickListener) - holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG) holder.audioPlaybackDuration.text = formattedDuration holder.fileSize.text = holder.rootLayout.context.getString( R.string.audio_message_file_size, formattedFileSize From d9f2033a3807de920c739e76cbe3c44100587117 Mon Sep 17 00:00:00 2001 From: ericdecanini Date: Wed, 6 Apr 2022 07:50:07 +0100 Subject: [PATCH 072/368] Removes unused import --- .../features/home/room/detail/timeline/item/MessageAudioItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt index 57629fb17d..3c071578cc 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt @@ -19,7 +19,6 @@ package im.vector.app.features.home.room.detail.timeline.item import android.content.Context import android.content.res.ColorStateList import android.graphics.Color -import android.graphics.Paint import android.text.format.DateUtils import android.view.ViewGroup import android.widget.ImageButton From 930ce0629bfc0ad3c5032b21e4fd3d8de0000a4a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 09:59:26 +0200 Subject: [PATCH 073/368] Changelog --- changelog.d/5079.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5079.misc diff --git a/changelog.d/5079.misc b/changelog.d/5079.misc new file mode 100644 index 0000000000..c38bf043ea --- /dev/null +++ b/changelog.d/5079.misc @@ -0,0 +1 @@ +Upgrade konfetti lib from 1.3.2 to 2.0.2 \ No newline at end of file From 0b76beb2415a1f17ce24bf5a77fb7677ec018c9c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 10:21:43 +0200 Subject: [PATCH 074/368] Add a handy markdown table --- .github/PULL_REQUEST_TEMPLATE.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7096e6bdb5..3940f2ca9e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -17,7 +17,17 @@ ## Screenshots / GIFs - + + + ## Tests From 24915591f21a6bea90bddac580a7eb7ed2aa5954 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 11:04:58 +0200 Subject: [PATCH 075/368] Avoid error log when the Event is not a LocationEvent --- .../BottomSheetMessagePreviewItem.kt | 23 +++++--------- .../detail/timeline/action/LocationUiData.kt | 28 +++++++++++++++++ .../action/MessageActionsEpoxyController.kt | 31 +++++++++++++------ 3 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/LocationUiData.kt diff --git a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt index 14ba34cc52..b90956ad9e 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/bottomsheet/BottomSheetMessagePreviewItem.kt @@ -33,7 +33,7 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.glide.GlideApp import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer -import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider +import im.vector.app.features.home.room.detail.timeline.action.LocationUiData import im.vector.app.features.home.room.detail.timeline.item.BindingOptions import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess import im.vector.app.features.media.ImageContentRenderer @@ -71,13 +71,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel GlideApp.with(holder.staticMapImageView) - .load(locationUrl) + .load(safeLocationUiData.locationUrl) .apply(RequestOptions.centerCropTransform()) .into(holder.staticMapImageView) - locationPinProvider?.create(locationOwnerId) { pinDrawable -> + safeLocationUiData.locationPinProvider.create(safeLocationUiData.locationOwnerId) { pinDrawable -> GlideApp.with(holder.staticMapPinImageView) .load(pinDrawable) .into(holder.staticMapPinImageView) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/LocationUiData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/LocationUiData.kt new file mode 100644 index 0000000000..c50c649221 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/LocationUiData.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 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.room.detail.timeline.action + +import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider + +/** + * Data used to display Location data in the message bottom sheet + */ +data class LocationUiData( + val locationUrl: String, + val locationOwnerId: String?, + val locationPinProvider: LocationPinProvider, +) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 307be220d3..05758bec31 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -49,7 +49,9 @@ import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent +import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState import javax.inject.Inject @@ -80,12 +82,7 @@ class MessageActionsEpoxyController @Inject constructor( val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL) val body = state.messageBody.linkify(host.listener) val bindingOptions = spanUtils.getBindingOptions(body) - - val locationContent = state.timelineEvent()?.root?.getClearContent() - ?.toModel(catchError = true) - val locationUrl = locationContent?.toLocationData() - ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } - val locationOwnerId = if (locationContent?.isSelfLocation().orTrue()) state.informationData.matrixItem.id else null + val locationUiData = buildLocationUiData(state) bottomSheetMessagePreviewItem { id("preview") @@ -99,9 +96,7 @@ class MessageActionsEpoxyController @Inject constructor( body(body.toEpoxyCharSequence()) bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)?.toEpoxyCharSequence()) time(formattedDate) - locationUrl(locationUrl) - locationPinProvider(host.locationPinProvider) - locationOwnerId(locationOwnerId) + locationUiData(locationUiData) } // Send state @@ -222,6 +217,24 @@ class MessageActionsEpoxyController @Inject constructor( } } + private fun buildLocationUiData(state: MessageActionState): LocationUiData? { + val clearContent = state.timelineEvent()?.root?.getClearContent() + val isLocationEvent = clearContent?.get(MessageContent.MSG_TYPE_JSON_KEY) == MessageType.MSGTYPE_LOCATION + if (!isLocationEvent) return null + + val locationContent = clearContent.toModel(catchError = true) + val locationUrl = locationContent?.toLocationData() + ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } + ?: return null + val locationOwnerId = if (locationContent.isSelfLocation().orTrue()) state.informationData.matrixItem.id else null + + return LocationUiData( + locationUrl = locationUrl, + locationOwnerId = locationOwnerId, + locationPinProvider = locationPinProvider, + ) + } + private fun EventSharedAction.shouldShowBetaLabel(): Boolean = this is EventSharedAction.ReplyInThread && !vectorPreferences.areThreadMessagesEnabled() From 45bd1061d2ad33b8752e88bfd887a2b86eec7f7f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 11:06:43 +0200 Subject: [PATCH 076/368] Small optimization --- .../detail/timeline/action/MessageActionsEpoxyController.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 05758bec31..788c89e3b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -223,10 +223,11 @@ class MessageActionsEpoxyController @Inject constructor( if (!isLocationEvent) return null val locationContent = clearContent.toModel(catchError = true) - val locationUrl = locationContent?.toLocationData() + ?: return null + val locationUrl = locationContent.toLocationData() ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } ?: return null - val locationOwnerId = if (locationContent.isSelfLocation().orTrue()) state.informationData.matrixItem.id else null + val locationOwnerId = if (locationContent.isSelfLocation()) state.informationData.matrixItem.id else null return LocationUiData( locationUrl = locationUrl, From a648a06ad16e5f73a80730fde85910ae8b13a0a4 Mon Sep 17 00:00:00 2001 From: Michael Kaye <1917473+michaelkaye@users.noreply.github.com> Date: Wed, 6 Apr 2022 10:07:58 +0100 Subject: [PATCH 077/368] Update to latest setup-matrix-synapse --- .github/workflows/nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b875a57abe..9dd0719428 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -43,7 +43,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: Start synapse server - uses: michaelkaye/setup-matrix-synapse@v0.4.0 + uses: michaelkaye/setup-matrix-synapse@v1.0.1 with: uploadLogs: true httpPort: 8080 @@ -230,7 +230,7 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: Start synapse server - uses: michaelkaye/setup-matrix-synapse@v0.4.0 + uses: michaelkaye/setup-matrix-synapse@v1.0.1 with: uploadLogs: true httpPort: 8080 From e27dcbc9b9580859c1947d229feb818f469e1d16 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 11:12:51 +0200 Subject: [PATCH 078/368] Create Event.getMsgType() extension --- .../sdk/api/session/events/model/Event.kt | 73 ++++++++++--------- .../action/MessageActionsEpoxyController.kt | 9 +-- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index f1304f6216..6d0bed4ee6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -300,57 +300,60 @@ data class Event( } } +/** + * Return the value of "content.msgtype", if the Event type is "m.room.message" + * and if the content has it, and if it is a String + */ +fun Event.getMsgType(): String? { + if (getClearType() != EventType.MESSAGE) return null + return getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY) as? String +} + fun Event.isTextMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_TEXT, - MessageType.MSGTYPE_EMOTE, - MessageType.MSGTYPE_NOTICE -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_TEXT, + MessageType.MSGTYPE_EMOTE, + MessageType.MSGTYPE_NOTICE -> true + else -> false + } } fun Event.isImageMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_IMAGE -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_IMAGE -> true + else -> false + } } fun Event.isVideoMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_VIDEO -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_VIDEO -> true + else -> false + } } fun Event.isAudioMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_AUDIO -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_AUDIO -> true + else -> false + } } fun Event.isFileMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_FILE -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_FILE -> true + else -> false + } } fun Event.isAttachmentMessage(): Boolean { - return getClearType() == EventType.MESSAGE && - when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { - MessageType.MSGTYPE_IMAGE, - MessageType.MSGTYPE_AUDIO, - MessageType.MSGTYPE_VIDEO, - MessageType.MSGTYPE_FILE -> true - else -> false - } + return when (getMsgType()) { + MessageType.MSGTYPE_IMAGE, + MessageType.MSGTYPE_AUDIO, + MessageType.MSGTYPE_VIDEO, + MessageType.MSGTYPE_FILE -> true + else -> false + } } fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClearType() in EventType.POLL_END diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index 788c89e3b0..adb83ecf8e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -46,10 +46,9 @@ import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.settings.VectorPreferences import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.session.events.model.getMsgType import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState @@ -218,11 +217,9 @@ class MessageActionsEpoxyController @Inject constructor( } private fun buildLocationUiData(state: MessageActionState): LocationUiData? { - val clearContent = state.timelineEvent()?.root?.getClearContent() - val isLocationEvent = clearContent?.get(MessageContent.MSG_TYPE_JSON_KEY) == MessageType.MSGTYPE_LOCATION - if (!isLocationEvent) return null + if (state.timelineEvent()?.root?.getMsgType() != MessageType.MSGTYPE_LOCATION) return null - val locationContent = clearContent.toModel(catchError = true) + val locationContent = state.timelineEvent()?.root?.getClearContent().toModel(catchError = true) ?: return null val locationUrl = locationContent.toLocationData() ?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) } From e8f421fe34a569568997ab980f132735d50564de Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 11:17:55 +0200 Subject: [PATCH 079/368] Create Event.isLocationMessage() --- .../matrix/android/sdk/api/session/events/model/Event.kt | 7 +++++++ .../timeline/action/MessageActionsEpoxyController.kt | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 6d0bed4ee6..9a40a06d3d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -356,6 +356,13 @@ fun Event.isAttachmentMessage(): Boolean { } } +fun Event.isLocationMessage(): Boolean { + return when (getMsgType()) { + MessageType.MSGTYPE_LOCATION -> true + else -> false + } +} + fun Event.isPoll(): Boolean = getClearType() in EventType.POLL_START || getClearType() in EventType.POLL_END fun Event.isSticker(): Boolean = getClearType() == EventType.STICKER diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt index adb83ecf8e..41916c609d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt @@ -47,10 +47,9 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.session.events.model.getMsgType +import org.matrix.android.sdk.api.session.events.model.isLocationMessage import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent -import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.send.SendState import javax.inject.Inject @@ -217,7 +216,7 @@ class MessageActionsEpoxyController @Inject constructor( } private fun buildLocationUiData(state: MessageActionState): LocationUiData? { - if (state.timelineEvent()?.root?.getMsgType() != MessageType.MSGTYPE_LOCATION) return null + if (state.timelineEvent()?.root?.isLocationMessage() != true) return null val locationContent = state.timelineEvent()?.root?.getClearContent().toModel(catchError = true) ?: return null From 785ccc8bc49a7bb892e3e1d0c5eb33ff66d48401 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 11:18:45 +0200 Subject: [PATCH 080/368] Format file --- .../org/matrix/android/sdk/api/session/events/model/Event.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 9a40a06d3d..7766168297 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -202,7 +202,9 @@ data class Event( fun getDecryptedTextSummary(): String? { if (isRedacted()) return "Message Deleted" val text = getDecryptedValue() ?: run { - if (isPoll()) { return getPollQuestion() ?: "created a poll." } + if (isPoll()) { + return getPollQuestion() ?: "created a poll." + } return null } From 26e80ba70b1de8dbf1bbaefded6e19182330a2ea Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 12:18:13 +0200 Subject: [PATCH 081/368] KeysBackup: Better handling of 404 to avoid error log --- changelog.d/5703.sdk | 1 + .../crypto/keysbackup/KeysBackupTest.kt | 19 +-- .../android/sdk/api/failure/Extensions.kt | 5 + .../crypto/keysbackup/KeysBackupService.kt | 5 +- .../keysbackup/DefaultKeysBackupService.kt | 117 +++++++++--------- .../model/KeysBackupLastVersionResult.kt | 30 +++++ .../tasks/GetKeysBackupLastVersionTask.kt | 21 +++- .../KeysBackupRestoreSharedViewModel.kt | 6 +- .../setup/KeysBackupSetupSharedViewModel.kt | 9 +- .../recover/BootstrapCrossSigningTask.kt | 12 +- .../recover/BootstrapSharedViewModel.kt | 6 +- .../VerificationBottomSheetViewModel.kt | 7 +- 12 files changed, 144 insertions(+), 94 deletions(-) create mode 100644 changelog.d/5703.sdk create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/KeysBackupLastVersionResult.kt diff --git a/changelog.d/5703.sdk b/changelog.d/5703.sdk new file mode 100644 index 0000000000..ece1fec8e8 --- /dev/null +++ b/changelog.d/5703.sdk @@ -0,0 +1 @@ +KeysBackupService.getCurrentVersion takes a new type `KeysBackupLastVersionResult` in the callback. \ No newline at end of file diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index 4c94566219..3326eab55d 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -39,10 +39,11 @@ import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.common.TestMatrixCallback import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM_BACKUP import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion -import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import java.util.Collections import java.util.concurrent.CountDownLatch @@ -403,9 +404,9 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = testHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) - } + }.toKeysVersionResult() // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) @@ -463,9 +464,9 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = testHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) - } + }.toKeysVersionResult() // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) @@ -565,9 +566,9 @@ class KeysBackupTest : InstrumentedTest { assertTrue(testData.aliceSession2.cryptoService().keysBackupService().isEnabled) // - Retrieve the last version from the server - val keysVersionResult = testHelper.doSync { + val keysVersionResult = testHelper.doSync { testData.aliceSession2.cryptoService().keysBackupService().getCurrentVersion(it) - } + }.toKeysVersionResult() // - It must be the same assertEquals(testData.prepareKeysBackupDataResult.version, keysVersionResult!!.version) @@ -835,9 +836,9 @@ class KeysBackupTest : InstrumentedTest { keysBackupTestHelper.prepareAndCreateKeysBackupData(keysBackup) // Get key backup version from the homeserver - val keysVersionResult = testHelper.doSync { + val keysVersionResult = testHelper.doSync { keysBackup.getCurrentVersion(it) - } + }.toKeysVersionResult() // - Check the returned KeyBackupVersion is trusted val keysBackupVersionTrust = testHelper.doSync { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt index 89b4a343dd..99fc0ba8b7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt @@ -29,6 +29,11 @@ fun Throwable.is401() = httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */ error.code == MatrixError.M_UNAUTHORIZED +fun Throwable.is404() = + this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_NOT_FOUND && /* 404 */ + error.code == MatrixError.M_NOT_FOUND + fun Throwable.isTokenError() = this is Failure.ServerError && (error.code == MatrixError.M_UNKNOWN_TOKEN || diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt index 4464427b90..1d9dbed3e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keysbackup/KeysBackupService.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.api.session.crypto.keysbackup import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.StepProgressListener +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion @@ -31,9 +32,9 @@ interface KeysBackupService { * Retrieve the current version of the backup from the homeserver * * It can be different than keysBackupVersion. - * @param callback onSuccess(null) will be called if there is no backup on the server + * @param callback Asynchronous callback */ - fun getCurrentVersion(callback: MatrixCallback) + fun getCurrentVersion(callback: MatrixCallback) /** * Create a new keys backup version and enable it, using the information return from [prepareKeysBackupVersion]. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index 954c2dbe43..db4265b7b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -41,6 +41,7 @@ import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.ObjectSigner import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrust import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData @@ -54,6 +55,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.RoomKeysBackupData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.UpdateKeysBackupVersionBody +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.CreateKeysBackupVersionTask import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.DeleteBackupTask import org.matrix.android.sdk.internal.crypto.keysbackup.tasks.DeleteRoomSessionDataTask @@ -586,21 +588,28 @@ internal class DefaultKeysBackupService @Inject constructor( cryptoCoroutineScope.launch(coroutineDispatchers.main) { try { - val keysBackupVersion = getKeysBackupLastVersionTask.execute(Unit) - val recoveryKey = computeRecoveryKey(secret.fromBase64()) - if (isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { - awaitCallback { - trustKeysBackupVersion(keysBackupVersion, true, it) + when (val keysBackupLastVersionResult = getKeysBackupLastVersionTask.execute(Unit)) { + KeysBackupLastVersionResult.NoKeysBackup -> { + Timber.d("No keys backup found") } - val importResult = awaitCallback { - restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, null, null, null, it) + is KeysBackupLastVersionResult.KeysBackup -> { + val keysBackupVersion = keysBackupLastVersionResult.keysVersionResult + val recoveryKey = computeRecoveryKey(secret.fromBase64()) + if (isValidRecoveryKeyForKeysBackupVersion(recoveryKey, keysBackupVersion)) { + awaitCallback { + trustKeysBackupVersion(keysBackupVersion, true, it) + } + val importResult = awaitCallback { + restoreKeysWithRecoveryKey(keysBackupVersion, recoveryKey, null, null, null, it) + } + withContext(coroutineDispatchers.crypto) { + cryptoStore.saveBackupRecoveryKey(recoveryKey, keysBackupVersion.version) + } + Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}") + } else { + Timber.e("onSecretKeyGossip: Recovery key is not valid ${keysBackupVersion.version}") + } } - withContext(coroutineDispatchers.crypto) { - cryptoStore.saveBackupRecoveryKey(recoveryKey, keysBackupVersion.version) - } - Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}") - } else { - Timber.e("onSecretKeyGossip: Recovery key is not valid ${keysBackupVersion.version}") } } catch (failure: Throwable) { Timber.e("onSecretKeyGossip: failed to trust key backup version ${keysBackupVersion?.version}") @@ -875,63 +884,49 @@ internal class DefaultKeysBackupService @Inject constructor( .executeBy(taskExecutor) } - override fun getCurrentVersion(callback: MatrixCallback) { + override fun getCurrentVersion(callback: MatrixCallback) { getKeysBackupLastVersionTask .configureWith { - this.callback = object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult) { - callback.onSuccess(data) - } - - override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError && - failure.error.code == MatrixError.M_NOT_FOUND) { - // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup - callback.onSuccess(null) - } else { - // Transmit the error - callback.onFailure(failure) - } - } - } + this.callback = callback } .executeBy(taskExecutor) } override fun forceUsingLastVersion(callback: MatrixCallback) { - getCurrentVersion(object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult?) { + getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysBackupLastVersionResult) { val localBackupVersion = keysBackupVersion?.version - val serverBackupVersion = data?.version - - if (serverBackupVersion == null) { - if (localBackupVersion == null) { - // No backup on the server, and backup is not active - callback.onSuccess(true) - } else { - // No backup on the server, and we are currently backing up, so stop backing up - callback.onSuccess(false) - resetKeysBackupData() - keysBackupVersion = null - keysBackupStateManager.state = KeysBackupState.Disabled - } - } else { - if (localBackupVersion == null) { - // backup on the server, and backup is not active - callback.onSuccess(false) - // Do a check - checkAndStartWithKeysBackupVersion(data) - } else { - // Backup on the server, and we are currently backing up, compare version - if (localBackupVersion == serverBackupVersion) { - // We are already using the last version of the backup + when (data) { + KeysBackupLastVersionResult.NoKeysBackup -> { + if (localBackupVersion == null) { + // No backup on the server, and backup is not active callback.onSuccess(true) } else { - // We are not using the last version, so delete the current version we are using on the server + // No backup on the server, and we are currently backing up, so stop backing up callback.onSuccess(false) + resetKeysBackupData() + keysBackupVersion = null + keysBackupStateManager.state = KeysBackupState.Disabled + } + } + is KeysBackupLastVersionResult.KeysBackup -> { + if (localBackupVersion == null) { + // backup on the server, and backup is not active + callback.onSuccess(false) + // Do a check + checkAndStartWithKeysBackupVersion(data.keysVersionResult) + } else { + // Backup on the server, and we are currently backing up, compare version + if (localBackupVersion == data.keysVersionResult.version) { + // We are already using the last version of the backup + callback.onSuccess(true) + } else { + // We are not using the last version, so delete the current version we are using on the server + callback.onSuccess(false) - // This will automatically check for the last version then - deleteBackup(localBackupVersion, null) + // This will automatically check for the last version then + deleteBackup(localBackupVersion, null) + } } } } @@ -954,9 +949,9 @@ internal class DefaultKeysBackupService @Inject constructor( keysBackupVersion = null keysBackupStateManager.state = KeysBackupState.CheckingBackUpOnHomeserver - getCurrentVersion(object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult?) { - checkAndStartWithKeysBackupVersion(data) + getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysBackupLastVersionResult) { + checkAndStartWithKeysBackupVersion(data.toKeysVersionResult()) } override fun onFailure(failure: Throwable) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/KeysBackupLastVersionResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/KeysBackupLastVersionResult.kt new file mode 100644 index 0000000000..72d6b45bbd --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/model/KeysBackupLastVersionResult.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 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.internal.crypto.keysbackup.model + +import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult + +sealed interface KeysBackupLastVersionResult { + // No Keys backup found (404 error) + object NoKeysBackup : KeysBackupLastVersionResult + data class KeysBackup(val keysVersionResult: KeysVersionResult) : KeysBackupLastVersionResult +} + +fun KeysBackupLastVersionResult.toKeysVersionResult(): KeysVersionResult? = when (this) { + is KeysBackupLastVersionResult.KeysBackup -> keysVersionResult + KeysBackupLastVersionResult.NoKeysBackup -> null +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt index 54dbf85e30..a17ced1181 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/tasks/GetKeysBackupLastVersionTask.kt @@ -16,23 +16,34 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.tasks +import org.matrix.android.sdk.api.failure.is404 import org.matrix.android.sdk.internal.crypto.keysbackup.api.RoomKeysApi -import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject -internal interface GetKeysBackupLastVersionTask : Task +internal interface GetKeysBackupLastVersionTask : Task internal class DefaultGetKeysBackupLastVersionTask @Inject constructor( private val roomKeysApi: RoomKeysApi, private val globalErrorReceiver: GlobalErrorReceiver ) : GetKeysBackupLastVersionTask { - override suspend fun execute(params: Unit): KeysVersionResult { - return executeRequest(globalErrorReceiver) { - roomKeysApi.getKeysBackupLastVersion() + override suspend fun execute(params: Unit): KeysBackupLastVersionResult { + return try { + val keysVersionResult = executeRequest(globalErrorReceiver) { + roomKeysApi.getKeysBackupLastVersion() + } + KeysBackupLastVersionResult.KeysBackup(keysVersionResult) + } catch (throwable: Throwable) { + if (throwable.is404()) { + KeysBackupLastVersionResult.NoKeysBackup + } else { + // Propagate other errors + throw throwable + } } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt index 8362a3566e..3b7b2e91d6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt @@ -32,7 +32,9 @@ import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_S import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService import org.matrix.android.sdk.api.session.securestorage.KeyInfoResult import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.util.awaitCallback @@ -117,9 +119,9 @@ class KeysBackupRestoreSharedViewModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { try { - val version = awaitCallback { + val version = awaitCallback { keysBackup.getCurrentVersion(it) - } + }.toKeysVersionResult() if (version?.version == null) { loadingEvent.postValue(null) _keyVersionResultError.postValue(LiveEvent(stringProvider.getString(R.string.keys_backup_get_version_error, ""))) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt index 1141886689..3c78b72ff7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupSharedViewModel.kt @@ -27,9 +27,10 @@ import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion -import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import timber.log.Timber import javax.inject.Inject @@ -150,9 +151,9 @@ class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() { creatingBackupError.value = null - keysBackup.getCurrentVersion(object : MatrixCallback { - override fun onSuccess(data: KeysVersionResult?) { - if (data?.version.isNullOrBlank() || forceOverride) { + keysBackup.getCurrentVersion(object : MatrixCallback { + override fun onSuccess(data: KeysBackupLastVersionResult) { + if (data.toKeysVersionResult()?.version.isNullOrBlank() || forceOverride) { processOnCreate() } else { loadingStatus.value = null 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 cc863346aa..7c1685d30c 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 @@ -33,9 +33,10 @@ import org.matrix.android.sdk.api.session.securestorage.SharedSecretStorageServi import org.matrix.android.sdk.api.session.securestorage.SsssKeyCreationInfo import org.matrix.android.sdk.api.session.securestorage.SsssKeySpec import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion -import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.util.awaitCallback import timber.log.Timber @@ -221,10 +222,9 @@ class BootstrapCrossSigningTask @Inject constructor( Timber.d("## BootstrapCrossSigningTask: Creating 4S - Checking megolm backup") // First ensure that in sync - var serverVersion = awaitCallback { + var serverVersion = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) - } - + }.toKeysVersionResult() val knownMegolmSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() val isMegolmBackupSecretKnown = knownMegolmSecret != null && knownMegolmSecret.version == serverVersion?.version val shouldCreateKeyBackup = serverVersion == null || @@ -236,9 +236,9 @@ class BootstrapCrossSigningTask @Inject constructor( awaitCallback { session.cryptoService().keysBackupService().deleteBackup(serverVersion!!.version, it) } - serverVersion = awaitCallback { + serverVersion = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) - } + }.toKeysVersionResult() } Timber.d("## BootstrapCrossSigningTask: Creating 4S - Create megolm backup") diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 8b8a8faaef..5fa77214fc 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -45,7 +45,9 @@ import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import org.matrix.android.sdk.internal.util.awaitCallback @@ -103,9 +105,9 @@ class BootstrapSharedViewModel @AssistedInject constructor( // We need to check if there is an existing backup viewModelScope.launch(Dispatchers.IO) { - val version = awaitCallback { + val version = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) - } + }.toKeysVersionResult() if (version == null) { // we just resume plain bootstrap doesKeyBackupExist = false 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 2495ae4ea5..e347e4d91b 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 @@ -52,7 +52,8 @@ import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified -import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupLastVersionResult +import org.matrix.android.sdk.internal.crypto.keysbackup.model.toKeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.computeRecoveryKey import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult import org.matrix.android.sdk.internal.util.awaitCallback @@ -426,9 +427,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( Timber.v("## Keybackup secret not restored from SSSS") } - val version = awaitCallback { + val version = awaitCallback { session.cryptoService().keysBackupService().getCurrentVersion(it) - } ?: return@launch + }.toKeysVersionResult() ?: return@launch awaitCallback { session.cryptoService().keysBackupService().restoreKeysWithRecoveryKey( From 85a00106c8cc016efb7cbcffe6c12b52303b6ba8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 12:46:26 +0200 Subject: [PATCH 082/368] Add changelog about the logs --- changelog.d/5703.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5703.misc diff --git a/changelog.d/5703.misc b/changelog.d/5703.misc new file mode 100644 index 0000000000..05e5080410 --- /dev/null +++ b/changelog.d/5703.misc @@ -0,0 +1 @@ +Reduce error logs \ No newline at end of file From 7e8c7f32fb3448bb9daf391065c793e65816362c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 14:33:06 +0200 Subject: [PATCH 083/368] Fix long line. It's OK to log the whole data class (same log than previous) --- .../sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index db4265b7b1..f1182340f9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -605,7 +605,7 @@ internal class DefaultKeysBackupService @Inject constructor( withContext(coroutineDispatchers.crypto) { cryptoStore.saveBackupRecoveryKey(recoveryKey, keysBackupVersion.version) } - Timber.i("onSecretKeyGossip: Recovered keys ${importResult.successfullyNumberOfImportedKeys} out of ${importResult.totalNumberOfKeys}") + Timber.i("onSecretKeyGossip: Recovered keys $importResult") } else { Timber.e("onSecretKeyGossip: Recovery key is not valid ${keysBackupVersion.version}") } From 4d19d0370da00df236079e33f717ac81d0d9a82e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 14:34:23 +0200 Subject: [PATCH 084/368] `matrix-sdk-android-rx` module has been replaced by `matrix-sdk-android-flow` --- tools/check/check_code_quality.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh index 9535ff9efb..af09dce9d4 100755 --- a/tools/check/check_code_quality.sh +++ b/tools/check/check_code_quality.sh @@ -66,7 +66,7 @@ echo "Search for forbidden patterns in code..." ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt \ ./matrix-sdk-android/src/main/java \ - ./matrix-sdk-android-rx/src/main/java \ + ./matrix-sdk-android-flow/src/main/java \ ./vector/src/main/java \ ./vector/src/debug/java \ ./vector/src/release/java \ @@ -80,7 +80,7 @@ echo "Search for forbidden patterns specific for SDK code..." ${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code_sdk.txt \ ./matrix-sdk-android/src \ - ./matrix-sdk-android-rx/src + ./matrix-sdk-android-flow/src resultForbiddenStringInCodeSdk=$? @@ -131,7 +131,7 @@ echo "Search for kotlin files with more than ${maxLines} lines..." ${checkLongFilesScript} ${maxLines} \ ./matrix-sdk-android/src/main/java \ - ./matrix-sdk-android-rx/src/main/java \ + ./matrix-sdk-android-flow/src/main/java \ ./vector/src/androidTest/java \ ./vector/src/debug/java \ ./vector/src/fdroid/java \ From 7cbe77eacbb61084e001fca2f3ab8a481f988e46 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 14:35:12 +0200 Subject: [PATCH 085/368] Use correct copyright for SDK module --- .../src/main/java/org/matrix/android/sdk/flow/FlowExt.kt | 2 +- .../src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt index 72493325c3..d21e898c1d 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/FlowExt.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 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. diff --git a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt index a9f062f379..42d84ed535 100644 --- a/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt +++ b/matrix-sdk-android-flow/src/main/java/org/matrix/android/sdk/flow/OptionalFlow.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright (c) 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. From 9d41508530b31690c59858597b03519ff1c56d69 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 6 Apr 2022 15:48:54 +0300 Subject: [PATCH 086/368] Prune the content of deleted events completely --- .../internal/session/room/prune/RedactionEventProcessor.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt index b19b8d4a6b..fe7dc28228 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/prune/RedactionEventProcessor.kt @@ -86,9 +86,9 @@ internal class RedactionEventProcessor @Inject constructor() : EventInsertLivePr // } val modified = unsignedData.copy(redactedEvent = redactionEvent) - // I Commented the line below, it should not be empty while we lose all the previous info about - // the redacted event -// eventToPrune.content = ContentMapper.map(emptyMap()) + // Deleting the content of a thread message will result to delete the thread relation, however threads are now dynamic + // so there is not much of a problem + eventToPrune.content = ContentMapper.map(emptyMap()) eventToPrune.unsignedData = MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(modified) eventToPrune.decryptionResultJson = null eventToPrune.decryptionErrorCode = null From 1b9fd3bdd4e680ae0552f75aa817e10bbd0b4d00 Mon Sep 17 00:00:00 2001 From: ariskotsomitopoulos Date: Wed, 6 Apr 2022 16:04:48 +0300 Subject: [PATCH 087/368] Add changelog --- changelog.d/5707.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5707.bugfix diff --git a/changelog.d/5707.bugfix b/changelog.d/5707.bugfix new file mode 100644 index 0000000000..66bd8f30b6 --- /dev/null +++ b/changelog.d/5707.bugfix @@ -0,0 +1 @@ +Redacted events are no longer visible. \ No newline at end of file From aae281a7eaec2c8ead6d9ade6f8cffcada4dcab2 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Apr 2022 16:34:30 +0300 Subject: [PATCH 088/368] Support aggregation of live location beacon events. --- .../room/model/livelocation/LiveLocationBeaconContent.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt index a4551d462e..022176b02d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt @@ -20,6 +20,7 @@ import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.room.model.message.LocationAsset import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent @JsonClass(generateAdapter = true) data class LiveLocationBeaconContent( @@ -37,7 +38,13 @@ data class LiveLocationBeaconContent( * Live location asset type. */ @Json(name = "org.matrix.msc3488.asset") val unstableLocationAsset: LocationAsset = LocationAsset(LocationAssetType.SELF), - @Json(name = "m.asset") val locationAsset: LocationAsset? = null + @Json(name = "m.asset") val locationAsset: LocationAsset? = null, + + /** + * Client side tracking of the last location + */ + @Transient + val lastLocationContent: MessageLiveLocationContent? = null ) { fun getBestBeaconInfo() = beaconInfo ?: unstableBeaconInfo From c17599604b9782363ea0635f4fc5387d70c4a223 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 6 Apr 2022 15:50:03 +0200 Subject: [PATCH 089/368] The string `ftue_auth_carousel_workplace_body` was declared not translatable by mistake --- changelog.d/5262.bugfix | 1 + vector/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/5262.bugfix diff --git a/changelog.d/5262.bugfix b/changelog.d/5262.bugfix new file mode 100644 index 0000000000..389c1ed8fa --- /dev/null +++ b/changelog.d/5262.bugfix @@ -0,0 +1 @@ +The string `ftue_auth_carousel_workplace_body` was declared not translatable by mistake \ No newline at end of file diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 0ab450421c..8bc25bb05b 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -1862,7 +1862,7 @@ Choose where your conversations are kept, giving you control and independence. Connected via Matrix. End-to-end encrypted and no phone number required. No ads or datamining. - ${app_name} is also great for the workplace. It’s trusted by the world’s most secure organisations. + ${app_name} is also great for the workplace. It’s trusted by the world’s most secure organisations. Who will you chat to the most? We\'ll help you get connected. From 197b5420306c94abe68aea534d8acbeb47466ee4 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Apr 2022 17:31:56 +0300 Subject: [PATCH 090/368] Check if live location data is valid. --- .../EventRelationsAggregationProcessor.kt | 81 ++++++++++++++++--- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 8bbe3a9ac6..ed7fc5b721 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -32,8 +32,10 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent import org.matrix.android.sdk.api.session.room.model.VoteInfo import org.matrix.android.sdk.api.session.room.model.VoteSummary +import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent import org.matrix.android.sdk.api.session.room.model.message.MessagePollResponseContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent @@ -47,6 +49,7 @@ import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.EventMapper +import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.EditionOfEvent import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity @@ -60,7 +63,9 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.getOrCreate +import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where +import org.matrix.android.sdk.internal.database.query.whereStateKey import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor @@ -88,7 +93,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( // EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_KEY, EventType.ENCRYPTED - ) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + ) + EventType.POLL_START + EventType.POLL_RESPONSE + EventType.POLL_END + EventType.BEACON_LOCATION_DATA override fun shouldProcess(eventId: String, eventType: String, insertType: EventInsertType): Boolean { return allowedTypes.contains(eventType) @@ -103,12 +108,12 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "") when (event.type) { - EventType.REACTION -> { + EventType.REACTION -> { // we got a reaction!! Timber.v("###REACTION in room $roomId , reaction eventID ${event.eventId}") handleReaction(realm, event, roomId, isLocalEcho) } - EventType.MESSAGE -> { + EventType.MESSAGE -> { if (event.unsignedData?.relations?.annotations != null) { Timber.v("###REACTION Aggregation in room $roomId for event ${event.eventId}") handleInitialAggregatedRelations(realm, event, roomId, event.unsignedData.relations.annotations) @@ -134,7 +139,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( EventType.KEY_VERIFICATION_START, EventType.KEY_VERIFICATION_MAC, EventType.KEY_VERIFICATION_READY, - EventType.KEY_VERIFICATION_KEY -> { + EventType.KEY_VERIFICATION_KEY -> { Timber.v("## SAS REF in room $roomId for event ${event.eventId}") event.content.toModel()?.relatesTo?.let { if (it.type == RelationType.REFERENCE && it.eventId != null) { @@ -143,7 +148,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } - EventType.ENCRYPTED -> { + EventType.ENCRYPTED -> { // Relation type is in clear val encryptedEventContent = event.content.toModel() if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE || @@ -169,22 +174,27 @@ internal class EventRelationsAggregationProcessor @Inject constructor( EventType.KEY_VERIFICATION_START, EventType.KEY_VERIFICATION_MAC, EventType.KEY_VERIFICATION_READY, - EventType.KEY_VERIFICATION_KEY -> { + EventType.KEY_VERIFICATION_KEY -> { Timber.v("## SAS REF in room $roomId for event ${event.eventId}") encryptedEventContent.relatesTo.eventId?.let { handleVerification(realm, event, roomId, isLocalEcho, it) } } - in EventType.POLL_RESPONSE -> { + in EventType.POLL_RESPONSE -> { event.getClearContent().toModel(catchError = true)?.let { handleResponse(realm, event, it, roomId, isLocalEcho, event.getRelationContent()?.eventId) } } - in EventType.POLL_END -> { + in EventType.POLL_END -> { event.content.toModel(catchError = true)?.let { handleEndPoll(realm, event, it, roomId, isLocalEcho) } } + in EventType.BEACON_LOCATION_DATA -> { + event.content.toModel(catchError = true)?.let { + handleLiveLocation(realm, event, it, roomId, isLocalEcho) + } + } } } else if (encryptedEventContent?.relatesTo?.type == RelationType.ANNOTATION) { // Reaction @@ -205,7 +215,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( // } // } } - EventType.REDACTION -> { + EventType.REDACTION -> { val eventToPrune = event.redacts?.let { EventEntity.where(realm, eventId = it).findFirst() } ?: return when (eventToPrune.type) { @@ -225,7 +235,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } } - in EventType.POLL_START -> { + in EventType.POLL_START -> { val content: MessagePollContent? = event.content.toModel() if (content?.relatesTo?.type == RelationType.REPLACE) { Timber.v("###REPLACE in room $roomId for event ${event.eventId}") @@ -233,17 +243,22 @@ internal class EventRelationsAggregationProcessor @Inject constructor( handleReplace(realm, event, content, roomId, isLocalEcho) } } - in EventType.POLL_RESPONSE -> { + in EventType.POLL_RESPONSE -> { event.content.toModel(catchError = true)?.let { handleResponse(realm, event, it, roomId, isLocalEcho) } } - in EventType.POLL_END -> { + in EventType.POLL_END -> { event.content.toModel(catchError = true)?.let { handleEndPoll(realm, event, it, roomId, isLocalEcho) } } - else -> Timber.v("UnHandled event ${event.eventId}") + in EventType.BEACON_LOCATION_DATA -> { + event.content.toModel(catchError = true)?.let { + handleLiveLocation(realm, event, it, roomId, isLocalEcho) + } + } + else -> Timber.v("UnHandled event ${event.eventId}") } } catch (t: Throwable) { Timber.e(t, "## Should not happen ") @@ -532,6 +547,46 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } + private fun handleLiveLocation(realm: Realm, + event: Event, + content: MessageLiveLocationContent, + roomId: String, + isLocalEcho: Boolean) { + val beaconInfoEventId = event.getRelationContent()?.eventId ?: return + val locationSenderId = event.senderId ?: return + + // We shouldn't process local echos + if (isLocalEcho) { + return + } + + // A beacon info state event has to be sent before sending location + val beaconInfoEntity = CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, EventType.STATE_ROOM_BEACON_INFO.first()) + if (beaconInfoEntity == null) { + Timber.v("## LIVE LOCATION. There is not any beacon info which should be emitted before sending location updates") + return + } + val beaconInfoContent = ContentMapper.map(beaconInfoEntity.root?.content)?.toModel(catchError = true) + if (beaconInfoContent == null) { + Timber.v("## LIVE LOCATION. Beacon info content is invalid") + return + } + + // Check if beacon info is outdated + if (isBeaconInfoOutdated(beaconInfoContent, content)) { + Timber.v("## LIVE LOCATION. Beacon info content is invalid") + return + } + } + + private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent, + liveLocationContent: MessageLiveLocationContent): Boolean { + val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0 + val liveLocationEventTime = liveLocationContent.getBestTs() ?: 0 + val timeout = beaconInfoContent.getBestBeaconInfo()?.timeout ?: 0 + return liveLocationEventTime - beaconInfoStartTime > timeout + } + private fun handleInitialAggregatedRelations(realm: Realm, event: Event, roomId: String, From faa07513eca566835d654840a908e655d4a53d0e Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Apr 2022 19:01:25 +0300 Subject: [PATCH 091/368] Update last location content of beacon info state event. --- .../room/model/livelocation/LiveLocationBeaconContent.kt | 3 +-- .../session/room/EventRelationsAggregationProcessor.kt | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt index 022176b02d..a3faee0568 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/livelocation/LiveLocationBeaconContent.kt @@ -43,8 +43,7 @@ data class LiveLocationBeaconContent( /** * Client side tracking of the last location */ - @Transient - val lastLocationContent: MessageLiveLocationContent? = null + var lastLocationContent: MessageLiveLocationContent? = null ) { fun getBestBeaconInfo() = beaconInfo ?: unstableBeaconInfo diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index ed7fc5b721..76e757d895 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -552,7 +552,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor( content: MessageLiveLocationContent, roomId: String, isLocalEcho: Boolean) { - val beaconInfoEventId = event.getRelationContent()?.eventId ?: return val locationSenderId = event.senderId ?: return // We shouldn't process local echos @@ -577,6 +576,10 @@ internal class EventRelationsAggregationProcessor @Inject constructor( Timber.v("## LIVE LOCATION. Beacon info content is invalid") return } + + // Update last location info of the beacon state event + beaconInfoContent.lastLocationContent = content + beaconInfoEntity.root?.content = ContentMapper.map(beaconInfoContent.toContent()) } private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent, From 6708ed8b345ec94448a2e370a87fc7f1906bed51 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Apr 2022 19:02:50 +0300 Subject: [PATCH 092/368] Lint fixes. --- .../internal/session/room/EventRelationsAggregationProcessor.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 76e757d895..74c4ab9d68 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -65,7 +65,6 @@ import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where -import org.matrix.android.sdk.internal.database.query.whereStateKey import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor From 44c6d88c444d2427c769f274182b9cf7e305a6a9 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 6 Apr 2022 19:24:20 +0300 Subject: [PATCH 093/368] Changelog added. --- changelog.d/5711.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/5711.feature diff --git a/changelog.d/5711.feature b/changelog.d/5711.feature new file mode 100644 index 0000000000..76c6b23b69 --- /dev/null +++ b/changelog.d/5711.feature @@ -0,0 +1 @@ +Live Location Sharing - Attach location data to beacon info state event \ No newline at end of file From 90616de89df71ba4ce0e0b486b8ba7f0d97a37dd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Apr 2022 09:30:18 +0200 Subject: [PATCH 094/368] Those log must be visible in the rageshake, even when verbose log is not enabled --- .../main/java/im/vector/app/VectorApplication.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index a3f4ffcfcd..e12eecfefc 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -230,13 +230,13 @@ class VectorApplication : val sdkVersion = Matrix.getSdkVersion() val date = SimpleDateFormat("MM-dd HH:mm:ss.SSSZ", Locale.US).format(Date()) - Timber.v("----------------------------------------------------------------") - Timber.v("----------------------------------------------------------------") - Timber.v(" Application version: $appVersion") - Timber.v(" SDK version: $sdkVersion") - Timber.v(" Local time: $date") - Timber.v("----------------------------------------------------------------") - Timber.v("----------------------------------------------------------------\n\n\n\n") + Timber.d("----------------------------------------------------------------") + Timber.d("----------------------------------------------------------------") + Timber.d(" Application version: $appVersion") + Timber.d(" SDK version: $sdkVersion") + Timber.d(" Local time: $date") + Timber.d("----------------------------------------------------------------") + Timber.d("----------------------------------------------------------------\n\n\n\n") } override fun attachBaseContext(base: Context) { From d4add052d95448e31a8a850e6e240fc1afac23ad Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Apr 2022 09:30:42 +0200 Subject: [PATCH 095/368] This log was quite useless --- .../sdk/internal/session/sync/handler/room/RoomSyncHandler.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt index 8fe85f0d31..e7fee7a42d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/handler/room/RoomSyncHandler.kt @@ -107,7 +107,6 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle isInitialSync: Boolean, aggregator: SyncResponsePostTreatmentAggregator, reporter: ProgressReporter? = null) { - Timber.v("Execute transaction from $this") handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, aggregator, reporter) handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, aggregator, reporter) handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter) From 1a53d2c8da33f126cea9b4c7478e84967b487d37 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Apr 2022 09:30:52 +0200 Subject: [PATCH 096/368] python3 --- tools/hs_diag.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/hs_diag.py b/tools/hs_diag.py index 7d7a947c4c..50f117bc8e 100755 --- a/tools/hs_diag.py +++ b/tools/hs_diag.py @@ -65,6 +65,6 @@ for item in items: print("# " + item[0] + " (" + item[1] + ")") print("====================================================================================================") if item[2]: - os.system("curl -s -X GET '" + item[1] + "' | python -m json.tool") + os.system("curl -s -X GET '" + item[1] + "' | python3 -m json.tool") else: - os.system("curl -s -X POST --data $'{}' '" + item[1] + "' | python -m json.tool") + os.system("curl -s -X POST --data $'{}' '" + item[1] + "' | python3 -m json.tool") From dbf10a222ff0ff84e54f21c15b41ff440c3b5c4e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 7 Apr 2022 10:15:21 +0200 Subject: [PATCH 097/368] Privacy: does not log reaction (it is a `v`log, so not critical) --- .../session/room/EventRelationsAggregationProcessor.kt | 10 +++++----- .../room/relation/threads/FetchThreadTimelineTask.kt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 8bbe3a9ac6..72fc9a4b04 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -584,11 +584,11 @@ internal class EventRelationsAggregationProcessor @Inject constructor( sum.key = reaction sum.firstTimestamp = event.originServerTs ?: 0 if (isLocalEcho) { - Timber.v("Adding local echo reaction $reaction") + Timber.v("Adding local echo reaction") sum.sourceLocalEcho.add(txId) sum.count = 1 } else { - Timber.v("Adding synced reaction $reaction") + Timber.v("Adding synced reaction") sum.count = 1 sum.sourceEvents.add(reactionEventId) } @@ -600,16 +600,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor( // check if it's not the sync of a local echo if (!isLocalEcho && sum.sourceLocalEcho.contains(txId)) { // ok it has already been counted, just sync the list, do not touch count - Timber.v("Ignoring synced of local echo for reaction $reaction") + Timber.v("Ignoring synced of local echo for reaction") sum.sourceLocalEcho.remove(txId) sum.sourceEvents.add(reactionEventId) } else { sum.count += 1 if (isLocalEcho) { - Timber.v("Adding local echo reaction $reaction") + Timber.v("Adding local echo reaction") sum.sourceLocalEcho.add(txId) } else { - Timber.v("Adding synced reaction $reaction") + Timber.v("Adding synced reaction") sum.sourceEvents.add(reactionEventId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt index a46bbe8d9f..15f70a8b4d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/threads/FetchThreadTimelineTask.kt @@ -251,7 +251,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor( sum = realm.createObject(ReactionAggregatedSummaryEntity::class.java) sum.key = reaction sum.firstTimestamp = event.originServerTs ?: 0 - Timber.v("Adding synced reaction $reaction") + Timber.v("Adding synced reaction") sum.count = 1 // reactionEventId not included in the /relations API // sum.sourceEvents.add(reactionEventId) From eab3e5aefa11fd07a91c60fd61219e2e0bec5da9 Mon Sep 17 00:00:00 2001 From: fedrunov <66663241+fedrunov@users.noreply.github.com> Date: Thu, 7 Apr 2022 15:16:12 +0200 Subject: [PATCH 098/368] don't leave DMs when leaving all rooms in space (#5720) --- changelog.d/5609.bugfix | 1 + .../java/im/vector/app/features/spaces/SpaceMenuViewModel.kt | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 changelog.d/5609.bugfix diff --git a/changelog.d/5609.bugfix b/changelog.d/5609.bugfix new file mode 100644 index 0000000000..001b4bf400 --- /dev/null +++ b/changelog.d/5609.bugfix @@ -0,0 +1 @@ +Choosing "leave all rooms and spaces" while leaving Space won't cause leaving DMs in this Space anymore \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt index 9b95b5328f..cf3d01681b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.Membership @@ -140,6 +141,7 @@ class SpaceMenuViewModel @AssistedInject constructor( excludeType = null activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(initialState.spaceId) memberships = listOf(Membership.JOIN) + roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS } ).forEach { try { From 504a2427757576efb32a92fbc3639985ae0ea72a Mon Sep 17 00:00:00 2001 From: fedrunov <66663241+fedrunov@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:43:41 +0200 Subject: [PATCH 099/368] tracking number of spaces user joined (#5300) --- .../java/im/vector/app/AppStateHandler.kt | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 1ff3d97576..cc3aed03fb 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -18,9 +18,12 @@ package im.vector.app import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.asFlow import arrow.core.Option import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.BehaviorDataSource +import im.vector.app.features.analytics.AnalyticsTracker +import im.vector.app.features.analytics.plan.UserProperties import im.vector.app.features.session.coroutineScope import im.vector.app.features.ui.UiStateRepository import kotlinx.coroutines.CoroutineScope @@ -28,12 +31,15 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.group.model.GroupSummary +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject import javax.inject.Singleton @@ -55,7 +61,8 @@ fun RoomGroupingMethod.group() = (this as? RoomGroupingMethod.ByLegacyGroup)?.gr class AppStateHandler @Inject constructor( private val sessionDataSource: ActiveSessionDataSource, private val uiStateRepository: UiStateRepository, - private val activeSessionHolder: ActiveSessionHolder + private val activeSessionHolder: ActiveSessionHolder, + private val analyticsTracker: AnalyticsTracker ) : DefaultLifecycleObserver { private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) @@ -125,11 +132,23 @@ class AppStateHandler @Inject constructor( } else { setCurrentGroup(uiStateRepository.getSelectedGroup(session.sessionId), session) } + observeSyncStatus(session) } } .launchIn(coroutineScope) } + private fun observeSyncStatus(session: Session) { + session.getSyncStatusLive() + .asFlow() + .filterIsInstance() + .map { session.spaceService().getRootSpaceSummaries().size } + .distinctUntilChanged() + .onEach { spacesNumber -> + analyticsTracker.updateUserProperties(UserProperties(numSpaces = spacesNumber)) + }.launchIn(session.coroutineScope) + } + fun safeActiveSpaceId(): String? { return (selectedSpaceDataSource.currentValue?.orNull() as? RoomGroupingMethod.BySpace)?.spaceSummary?.roomId } From ff7c19537ec3df5c86de633497daea5edce01c0f Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 13:37:38 +0000 Subject: [PATCH 100/368] Translated using Weblate (Polish) Currently translated at 12.7% (7 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pl/ --- fastlane/metadata/android/pl-PL/changelogs/40103070.txt | 4 ++-- fastlane/metadata/android/pl-PL/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/pl-PL/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/pl-PL/changelogs/40104080.txt | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/pl-PL/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/pl-PL/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/pl-PL/changelogs/40104080.txt diff --git a/fastlane/metadata/android/pl-PL/changelogs/40103070.txt b/fastlane/metadata/android/pl-PL/changelogs/40103070.txt index 2cb20a9570..08d9d2193e 100644 --- a/fastlane/metadata/android/pl-PL/changelogs/40103070.txt +++ b/fastlane/metadata/android/pl-PL/changelogs/40103070.txt @@ -1,2 +1,2 @@ -Główne zmiany w tej wersji: Poprawki błędów dotyczące głównie powiadomień -Pełny changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.7 +Główne zmiany w tej wersji: Poprawki błędów dotyczące głównie powiadomień. +Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.3.7-RC2 diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104060.txt b/fastlane/metadata/android/pl-PL/changelogs/40104060.txt new file mode 100644 index 0000000000..216842ada0 --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Główne zmiany w tej wersji: Oś czasu wątku jest teraz aktywna i szybsza. Różne poprawki błędów i ulepszenia stabilności. +Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104070.txt b/fastlane/metadata/android/pl-PL/changelogs/40104070.txt new file mode 100644 index 0000000000..91c13d3d79 --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Główne zmiany w tej wersji: Różne poprawki błędów i ulepszenia stabilności. +Pełna lista zmian: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/pl-PL/changelogs/40104080.txt b/fastlane/metadata/android/pl-PL/changelogs/40104080.txt new file mode 100644 index 0000000000..5286f40de5 --- /dev/null +++ b/fastlane/metadata/android/pl-PL/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Główne zmiany w tej wersji: Oś czasu wątku jest teraz aktywna i szybsza. Różne poprawki błędów i ulepszenia stabilności. +Pełna lista zmian: https://github.com/vector-im/element-android/releases From 23cdd4fac396a4f3ce92156d3eb3261e53cd5ac7 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 5 Apr 2022 22:54:35 +0000 Subject: [PATCH 101/368] Translated using Weblate (Indonesian) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/id/ --- vector/src/main/res/values-in/strings.xml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-in/strings.xml b/vector/src/main/res/values-in/strings.xml index 29831c1377..5ca1a361d4 100644 --- a/vector/src/main/res/values-in/strings.xml +++ b/vector/src/main/res/values-in/strings.xml @@ -1742,7 +1742,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. \nCoba lagi nanti, atau tanyakan ke admin ruangan untuk mengecek jika Anda punya akses. Bergabung Saja Bergabung ke Space - Buat Space + Buat space Lewat untuk sekarang Bergabung ke space saya %1$s %2$s Mereka tidak akan menjadi bagian dari %s @@ -1784,7 +1784,7 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. Tipe space apa yang Anda ingin buat\? Space privat Anda Space publik Anda - Tambahkan Space + Tambahkan space Space privat Space publik Apakah Anda yakin untuk menghapus semua pesan yang belum dikirim di ruangan ini\? @@ -2393,4 +2393,23 @@ Di masa mendatang proses verifikasi ini akan dimutakhirkan. \n \nIni hanya transisi sekali, utasan sekarang sebagai bagian dari spesifikasi Matrix. Utasan Mencapai Beta 🎉 + Penbagian lokasi sedang dilakukan + ${app_name} Lokasi langsung + Homeserver tidak menerima nama pengguna dengan hanya angka. + Lewati + Simpan dan lanjutkan + Preferensi Anda telah disimpan. + Anda siap! + Ayo + Anda dapat mengubahnya kapan saja. + Tambahkan sebuah foto profil + Anda dapat mengubahnya nanti + Nama Tampilan + Ini akan ditampilkan ketika Anda mengirim pesan. + Pilih nama tampilan + Akun %s Anda telah dibuat. + Selamat! + Kembalikan saya ke beranda + Ubah profil + Nonaktifkan \ No newline at end of file From f2bbcddd5f219390ed7aba8af02c1ab8188f8e51 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 6 Apr 2022 08:26:44 +0000 Subject: [PATCH 102/368] Translated using Weblate (Italian) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/it/ --- vector/src/main/res/values-it/strings.xml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml index d14aa3c671..15f08ba6da 100644 --- a/vector/src/main/res/values-it/strings.xml +++ b/vector/src/main/res/values-it/strings.xml @@ -1959,7 +1959,7 @@ \nRiprova più tardi, o chiedi a un amministratore della stanza di controllare se hai l\'accesso. Unisciti comunque Unisciti a uno Spazio - Crea Spazio + Crea spazio Per il momento, salta Unisciti al mio Spazio %1$s %2$s Non faranno parte di %s @@ -1998,7 +1998,7 @@ Che tipo di Spazio vuoi creare\? Il tuo Spazio privato Il tuo Spazio pubblico - Aggiungi Spazio + Aggiungi spazio Esci dalla stanza con un ID (o dalla stanza attuale se vuota) Unisciti allo Spazio con un ID Crea uno Spazio @@ -2429,4 +2429,23 @@ \n \nSarà una transizione una-tantum, dato che le conversazioni ora sono parte delle specifiche di Matrix. I messaggi in conversazioni entrano in beta 🎉 + Portami a casa + Stai condividendo la posizione + Posizione in tempo reale di ${app_name} + L\'homeserver non accetta nomi utente con solo numeri. + Salta questo passo + Salva e continua + Le tue preferenze sono state salvate. + Tutto pronto! + Andiamo + Puoi cambiarla in qualsiasi momento. + Aggiungi un\'immagine del profilo + Puoi cambiarlo in seguito + Nome da mostrare + Verrà mostrato quando invii messaggi. + Scegli un nome da mostrare + Il tuo account %s è stato creato. + Congratulazioni! + Personalizza profilo + Disattiva \ No newline at end of file From 21231127631edd497bd29c9dfc4ccf0b9d248f9b Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 5 Apr 2022 16:51:26 +0000 Subject: [PATCH 103/368] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/pt_BR/ --- fastlane/metadata/android/pt-BR/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/pt-BR/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/pt-BR/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/pt-BR/changelogs/40104080.txt diff --git a/fastlane/metadata/android/pt-BR/changelogs/40104060.txt b/fastlane/metadata/android/pt-BR/changelogs/40104060.txt new file mode 100644 index 0000000000..17d8225c0d --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Timeline de Thread estão agora ao vivo e mais rápidas. Vários consertos de bugs e melhorias de estabilidade. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40104070.txt b/fastlane/metadata/android/pt-BR/changelogs/40104070.txt new file mode 100644 index 0000000000..fad9bfe739 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Vários consertos de bugs e melhorias de estabilidade. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40104080.txt b/fastlane/metadata/android/pt-BR/changelogs/40104080.txt new file mode 100644 index 0000000000..ca2b54f2f2 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Timeline de Thread estão agora ao vivo e mais rápidas. Vários consertos de bugs e melhorias de estabilidade. +Changelog completo: https://github.com/vector-im/element-android/releases From 8b125923e9429388074e3327a8dbbbb953221b23 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 5 Apr 2022 19:07:18 +0000 Subject: [PATCH 104/368] Translated using Weblate (Slovak) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sk/ --- vector/src/main/res/values-sk/strings.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vector/src/main/res/values-sk/strings.xml b/vector/src/main/res/values-sk/strings.xml index bd3e86e40c..0807657f8e 100644 --- a/vector/src/main/res/values-sk/strings.xml +++ b/vector/src/main/res/values-sk/strings.xml @@ -2485,4 +2485,23 @@ \n \nPôjde o jednorazový prechod, keďže vlákna sú teraz súčasťou špecifikácie Matrix. Vlákna sa blížia k beta verzii 🎉 + Váš účet %s bol vytvorený. + Prebieha zdieľanie polohy + ${app_name} Poloha v reálnom čase + Domovský server neakceptuje používateľské meno obsahujúce iba číslice. + Vynechať tento krok + Uložiť a pokračovať + Vaše nastavenia boli uložené. + Všetko je pripravené! + Poďme na to + Toto môžete kedykoľvek zmeniť. + Pridať profilový obrázok + Neskôr to môžete zmeniť + Zobrazované meno + Toto sa zobrazí pri odosielaní správ. + Vyberte si zobrazované meno + Gratulujeme! + Zober ma domov + Prispôsobiť profil + Zakázať \ No newline at end of file From 661d95cf29068d8027bd3a5f74cf91687c6e0886 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 5 Apr 2022 17:26:00 +0000 Subject: [PATCH 105/368] Translated using Weblate (Ukrainian) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/uk/ --- fastlane/metadata/android/uk/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/uk/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/uk/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/uk/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/uk/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/uk/changelogs/40104080.txt diff --git a/fastlane/metadata/android/uk/changelogs/40104060.txt b/fastlane/metadata/android/uk/changelogs/40104060.txt new file mode 100644 index 0000000000..28f051724a --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Хронологія тредів працює наживо і швидше. Усунуто різні вади й поліпшено стабільність. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/uk/changelogs/40104070.txt b/fastlane/metadata/android/uk/changelogs/40104070.txt new file mode 100644 index 0000000000..dff28b7825 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Усунуто різні вади й поліпшено стабільність. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/uk/changelogs/40104080.txt b/fastlane/metadata/android/uk/changelogs/40104080.txt new file mode 100644 index 0000000000..e5f7e7b271 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: Хронологія тредів працює наживо і швидше. Усунуто різні вади й поліпшено стабільність. +Вичерпний перелік змін: https://github.com/vector-im/element-android/releases From d538e2d9a9ac0e0a2c1d7b3ae5c1cdf29938765e Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Tue, 5 Apr 2022 17:09:57 +0000 Subject: [PATCH 106/368] Translated using Weblate (Czech) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/cs/ --- vector/src/main/res/values-cs/strings.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml index c66ff56b3e..3121f859f3 100644 --- a/vector/src/main/res/values-cs/strings.xml +++ b/vector/src/main/res/values-cs/strings.xml @@ -2485,4 +2485,23 @@ \n \nPůjde o jednorázový přechod, protože vlákna jsou nyní součástí Matrix specifikace. Vlákna se blíží k betaverzi 🎉 + Probíhá sdílení polohy + ${app_name} poloha živě + Domovský server neakceptuje uživatelská jména obsahující pouze číslice. + Přeskočit tento krok + Uložit a pokračovat + Vaše předvolby byly uloženy. + Vše je připraveno! + Jdeme na to + Tuto můžete kdykoli změnit. + Přidat profilový obrázek + Později to můžete změnit + Zobrazované jméno + Toto se zobrazí při odesílání zpráv. + Zvolte si zobrazované jméno + Váš účet %s byl založen. + Gratulujeme! + Vezmi mě domů + Přizpůsobit profil + Zakázat \ No newline at end of file From c09cb3358a1a3f86325d086db7e50da0cf2bd7b3 Mon Sep 17 00:00:00 2001 From: waclaw66 Date: Tue, 5 Apr 2022 17:02:53 +0000 Subject: [PATCH 107/368] Translated using Weblate (Czech) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/cs/ --- fastlane/metadata/android/cs-CZ/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/cs-CZ/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/cs-CZ/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/cs-CZ/changelogs/40104080.txt diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40104060.txt b/fastlane/metadata/android/cs-CZ/changelogs/40104060.txt new file mode 100644 index 0000000000..3eda022464 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Časová osa vláken je nyní živá a rychlejší. Opravy různých chyb a vylepšení stability. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40104070.txt b/fastlane/metadata/android/cs-CZ/changelogs/40104070.txt new file mode 100644 index 0000000000..cba2012c1c --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Opravy různých chyb a vylepšení stability. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40104080.txt b/fastlane/metadata/android/cs-CZ/changelogs/40104080.txt new file mode 100644 index 0000000000..61e7fd7940 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Časová osa vlákna je nyní živá a rychlejší. Opravy různých chyb a vylepšení stability. +Úplný seznam změn: https://github.com/vector-im/element-android/releases From 833798bd252b3536ccf50bc4ac7eeb50c571c7e3 Mon Sep 17 00:00:00 2001 From: Piotr Strebski Date: Tue, 5 Apr 2022 15:14:46 +0000 Subject: [PATCH 108/368] Translated using Weblate (Polish) Currently translated at 97.4% (2135 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 93 +++++++++++++++++++++-- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index e4b7b28a01..ef24d6e62b 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -1798,18 +1798,18 @@ %1$s, %2$s i %3$s %s w Ustawieniach, aby otrzymywać zaproszenia bezpośrednio w ${app_name}. Zmiana tematu - Aktualizacja Przestrzeni + Aktualizacja przestrzeni Aktualizacja pokoju Wysyłanie zdarzeń m.room.server_acl Zmiana uprawnień - Zmiana nazwy Przestrzeni + Zmiana nazwy przestrzeni Zmiana nazwy pokoju Zmiana widoczności historii Włączanie szyfrowania przestrzeni Włączanie szyfrowania pokoju - Zmiana głównego adresu Przestrzeni + Zmiana głównego adresu przestrzeni Zmiana głównego adresu pokoju - Zmiana awatara Przestrzeni + Zmiana awatara przestrzeni Zmiana awatara pokoju Modyfikowanie widgetów Powiadamianie wszystkich @@ -2354,7 +2354,7 @@ Brakujący typ wiadomości Brak zawartości - Wyślij wydarzenie niestandardowe + Wyślij zdarzenie niestandardowe Ostrzegawczy poziom zaufania Upewnij się najpierw Nie sprawdzono @@ -2363,5 +2363,86 @@ Włącz matematykę LaTeX Zakończ ankietę Czy zakończyć ankietę\? - To powstrzyma użytkowników od głosowania i wyświetli finalny wynik ankiety. + To powstrzyma użytkowników od głosowania i wyświetli ostateczny wynik ankiety. + Uwaga: aplikacja zostanie uruchomiona ponownie + Włącz wątkowanie wiadomości + Upewnij się, że odpowiednie osoby mają dostęp do firmy %s. Więcej osób możesz zaprosić później. + Wyślij niestandardowe zdarzenie stanowe + Wyślij zdarzenie stanowe + Zdarzenia stanowe + Zawartość zdarzenia + Zdarzenie stanowe wysłane! + Zdarzenie wysłane! + Nieprawidłowe zdarzenie + Zawartość wydarzenia + Klucz stanu + Konsultacja z %1$s + Serwer domowy nie akceptuje nazwy użytkownika zawierającej tylko cyfry. + Wymusza odrzucenie bieżącej sesji grupy wychodzącej w zaszyfrowanym pokoju + Pobieranie krzywego klucza + Szyfrowanie jest błędnie skonfigurowane + Przywróć szyfrowanie + Skontaktuj się z administratorem, aby przywrócić szyfrowanie do prawidłowego stanu. + Szyfrowanie zostało błędnie skonfigurowane. + Utwórz konto + Pomiń ten krok + Zapisz i kontynuuj + Twoje ustawienia zostały zapisane. + Wszystko ustawiłeś! + Chodźmy + Możesz go zmienić w każdym momencie. + Dodaj obraz profilu + Możesz zmienić ją później + Powiadomienie pokoju + Wyświetlana nazwa + Będzie ona widoczna podczas wysyłania wiadomości. + Wybierz wyświetlaną nazwę + Twoje konto %s zostało utworzone. + Gratulacje! + Zabierz mnie do domu + Spersonalizuj profil + Połącz się z serwerem + Chcesz dołączyć do istniejącego serwera\? + pominąć to pytanie + Nie wiesz jeszcze\? Możesz %s + Społeczności + Zespoły + Przyjaciele i rodzina + Pomożemy Ci się połączyć. + Z kim będziesz najczęściej rozmawiać\? + Szyfrowane od-końca-do-końca i nie wymaga numeru telefonu. Brak reklam i dataminingu. + Wybierz, gdzie prowadzone są Twoje rozmowy, dając Ci kontrolę i niezależność. Połączenie przez sieć Matrix. + Bezpieczna i niezależna komunikacja, która zapewnia ten sam poziom prywatności, co rozmowa twarzą w twarz we własnym domu. + Wiadomości dla Twojego zespołu. + Położenie + Zagadnienia prawne + Już przeglądasz ten wątek! + Wyświetl w pokoju + Odpowiedz w wątku + Polecenie „%s” jest rozpoznawane, ale nie jest obsługiwane w wątkach. + Ten serwer nie zapewnia żadnych zasad. + Polityka Twojego serwera tożsamości + Polityka Twojego serwera domowego + Polityka ${app_name} + Wątki zbliżają się do wersji beta 🎉 + Z wątku + Wskazówka: naciśnij i przytrzymaj wiadomość i użyj „%s”. + Dzięki wątkom Twoje rozmowy są aktualne i łatwe do śledzenia. + Organizuj dyskusje za pomocą wątków + Pokazuje wszystkie wątki, w których brałeś udział + Pokazuje wszystkie wątki z bieżącego pokoju + Moje wątki + Wszystkie wątki + Filtr + Wątki + Wątek + Filtruj wątki w pokoju + Szyfrowanie zostało źle skonfigurowane, więc nie możesz wysyłać wiadomości. Kliknij, aby otworzyć ustawienia. + Szyfrowanie zostało źle skonfigurowane, więc nie możesz wysyłać wiadomości. Skontaktuj się z administratorem, aby przywrócić szyfrowanie do prawidłowego stanu. + %1$s, %2$s i inni + %1$s i %2$s + Skopiuj odnośnik do wątku + Zobacz w pokoju + Wyłącz + Wyświetl wątki \ No newline at end of file From de109c9772458f1f0c2f41480e08c2c8cfcbebd9 Mon Sep 17 00:00:00 2001 From: maciejek997 Date: Tue, 5 Apr 2022 13:13:52 +0000 Subject: [PATCH 109/368] Translated using Weblate (Polish) Currently translated at 97.4% (2135 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pl/ --- vector/src/main/res/values-pl/strings.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vector/src/main/res/values-pl/strings.xml b/vector/src/main/res/values-pl/strings.xml index ef24d6e62b..662574536f 100644 --- a/vector/src/main/res/values-pl/strings.xml +++ b/vector/src/main/res/values-pl/strings.xml @@ -2445,4 +2445,14 @@ Zobacz w pokoju Wyłącz Wyświetl wątki + Utwórz ankietę + Otwórz kontakty + Wyślij naklejkę + Prześlij plik + Wyślij obrazy oraz wideo + Otwórz kamerę + Usuń ankietę + Ankieta zakończona + Oddano głos + Zakończ ankietę \ No newline at end of file From fca0b042e879f5c6fba76fccef851bfffacd572c Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 6 Apr 2022 04:13:09 +0000 Subject: [PATCH 110/368] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/zh_Hant/ --- vector/src/main/res/values-zh-rTW/strings.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml index 781591ace7..ea0c677168 100644 --- a/vector/src/main/res/values-zh-rTW/strings.xml +++ b/vector/src/main/res/values-zh-rTW/strings.xml @@ -2391,4 +2391,23 @@ \n \n這會是一次性的過渡,因為討論串現在是 Matrix 規範的一部分了。 討論串接近測試版了 🎉 + 正在進行位置分享 + ${app_name} 即時位置 + 家伺服器不接受僅有數字的使用者名稱。 + 略過此步驟 + 儲存並繼續 + 您的偏好設定已儲存。 + 準備好了! + 走吧 + 您可以在任何時候變更這個。 + 新增個人資訊照片 + 您可以在稍後變更這個 + 顯示名稱 + 這將會在您傳送訊息時顯示。 + 選擇顯示名稱 + 您的帳號 %s 已建立。 + 恭喜! + 帶我回家 + 個人化檔案 + 停用 \ No newline at end of file From 0167e3a222b7cab6396928a6efcc7d73b4f5531a Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Tue, 5 Apr 2022 12:34:02 +0000 Subject: [PATCH 111/368] Translated using Weblate (Persian) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/fa/ --- vector/src/main/res/values-fa/strings.xml | 29 ++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml index ab6bac5c1b..9a23913f76 100644 --- a/vector/src/main/res/values-fa/strings.xml +++ b/vector/src/main/res/values-fa/strings.xml @@ -1247,7 +1247,7 @@ نتیجه‌ای در پی نداشت فیلترکردن کاربران مسدود شده - %d مورد + ۱ مورد %d مورد تنها اگر مدیر کارساز، اثرانگشتی منتشر کرده که با بالایی مطابق است، بپذیرید. @@ -2430,4 +2430,31 @@ هم‌رسانی مکان کنونیم بزرگ‌نمایی به مکان کنونی سنجاق مکان گزیده روی نقشه + داریم به ارائه یک بتای عمومی برای رشته‌ها نزدیک‌تر می‌شویم. +\n +\nهمین‌طور که برایش آماده می‌شویم، نیاز به انجام چند تغییر است: رشته‌هایی که پیش از این ایجاد شده‌اند،به شکل پاسخ‌های عادی نشان داده خواهند شد. +\n +\nاین کار، یک تبدیل یک باره است؛ چرا که رشته‌ها اکنون بخشی از مشخّصات ماتریکسند. + رشته‌ها به بتا می رسند 🎉 + هم‌رساین مکان در حال انجام است + مکان زندهٔ ${app_name} + اگر می‌خواهید مکانتان را هم‌رسانی کنید، ${app_name} نیاز به دسترسی همیشگی مکان هنگام در پس‌زمینه بودن کاره دارد. +\nفقط برای مدتی که برمی‌گزینید، به مکانتان دسترسی خواهیم داشت. + کارساز خانگی، نام‌های کاربری فقط رقمی را نمی‌پذیرد. + پرش از این گام + ذخیره و ادامه + ترجیحاتتان ذخیره شد. + همه‌چیز تنظیم شده! + بزن بریم + می‌توانید هرزمانی تغییرش دهید. + گزینش نامی نمایشی + افزودن یک تصویر نمایه + می‌توانید بعدها تغییرش دهید + نام نمایشی + هنگامی که پیام می‌فرستید نشان داده خواهد شد. + حسابتان %s ایجاد شد. + تبریک! + مرا به خانه ببر + شخصی سازی نمایه + از کار انداختن \ No newline at end of file From 1a6de70d927891891d4da4000c946e16ebf2b915 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 6 Apr 2022 08:29:04 +0000 Subject: [PATCH 112/368] Translated using Weblate (Italian) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/it/ --- fastlane/metadata/android/it-IT/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/it-IT/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/it-IT/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/it-IT/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/it-IT/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/it-IT/changelogs/40104080.txt diff --git a/fastlane/metadata/android/it-IT/changelogs/40104060.txt b/fastlane/metadata/android/it-IT/changelogs/40104060.txt new file mode 100644 index 0000000000..c9ea8af2cc --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: la linea temporale per argomenti è ora attiva e più veloce. Varie correzioni e miglioramenti. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/it-IT/changelogs/40104070.txt b/fastlane/metadata/android/it-IT/changelogs/40104070.txt new file mode 100644 index 0000000000..2dfd415920 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: varie correzioni di errori e miglioramenti della stabilità. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/it-IT/changelogs/40104080.txt b/fastlane/metadata/android/it-IT/changelogs/40104080.txt new file mode 100644 index 0000000000..8427e3d3fe --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: la linea temporale per argomenti è ora attiva e più veloce. Varie correzioni e miglioramenti. +Cronologia completa: https://github.com/vector-im/element-android/releases From 5f30a66929a3faf79e0f191cabf13788174b10a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 5 Apr 2022 16:38:40 +0000 Subject: [PATCH 113/368] Translated using Weblate (Estonian) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/et/ --- fastlane/metadata/android/et/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/et/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/et/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/et/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/et/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/et/changelogs/40104080.txt diff --git a/fastlane/metadata/android/et/changelogs/40104060.txt b/fastlane/metadata/android/et/changelogs/40104060.txt new file mode 100644 index 0000000000..f506b617ed --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: jutulõngad on nüüd kasutatavad ja toimivad kiiremini, lisaks pisiparandused ja stabiilsust parandavad kohendused. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/et/changelogs/40104070.txt b/fastlane/metadata/android/et/changelogs/40104070.txt new file mode 100644 index 0000000000..ea3582678d --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: pisiparandused ja stabiilsust parandavad kohendused. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/et/changelogs/40104080.txt b/fastlane/metadata/android/et/changelogs/40104080.txt new file mode 100644 index 0000000000..a35fded9b6 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: jutulõngad on nüüd kasutatavad ja toimivad kiiremini, lisaks pisiparandused ja stabiilsust parandavad kohendused. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases From 9e39df111d8aadf26cf914503565b51f9150bd90 Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Wed, 6 Apr 2022 19:38:40 +0000 Subject: [PATCH 114/368] Translated using Weblate (Swedish) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/sv/ --- vector/src/main/res/values-sv/strings.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml index 2b4e3502f8..c6c8b45441 100644 --- a/vector/src/main/res/values-sv/strings.xml +++ b/vector/src/main/res/values-sv/strings.xml @@ -2438,4 +2438,23 @@ \nMedan vi förbereder den så behöver vi göra några ändringar: trådar skapade innan den här tidpunkten kommer att visas som vanliga svar. \n \nDet kommer att vara en engångshändelse, eftersom trådar nu är en del av Matrixspecifikationen. + Platsdelning pågår + ${app_name} Kontinuerlig plats + Hemservern accepterar inte användarnamn med bara siffror. + Hoppa över det här steget + Spara och fortsätt + Dina inställningar har sparats. + Du är redo! + Nu kör vi + Du kan ändra detta när som helst. + Lägg till en profilbild + Du kan ändra detta senare + Visningsnamn + Det här kommer att visas när du skickar meddelanden. + Välj ett visningsnamn + Ditt konto %s har skapats. + Grattis! + För mig hem + Anpassa profil + Inaktivera \ No newline at end of file From b2b1e1ca0a51a9d4df3f8468ea7db1959d921f4d Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 5 Apr 2022 18:26:33 +0000 Subject: [PATCH 115/368] Translated using Weblate (Slovak) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sk/ --- fastlane/metadata/android/sk/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/sk/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/sk/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/sk/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/sk/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/sk/changelogs/40104080.txt diff --git a/fastlane/metadata/android/sk/changelogs/40104060.txt b/fastlane/metadata/android/sk/changelogs/40104060.txt new file mode 100644 index 0000000000..53e192fe7c --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Vlákna na časovej osi sú teraz živé a rýchlejšie. Rôzne opravy chýb a vylepšenia stability. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/sk/changelogs/40104070.txt b/fastlane/metadata/android/sk/changelogs/40104070.txt new file mode 100644 index 0000000000..1e056cf32f --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Rôzne opravy chýb a vylepšenia stability. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/sk/changelogs/40104080.txt b/fastlane/metadata/android/sk/changelogs/40104080.txt new file mode 100644 index 0000000000..7f42250fb9 --- /dev/null +++ b/fastlane/metadata/android/sk/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Hlavné zmeny v tejto verzii: Vlákna na časovej osi sú teraz živé a rýchlejšie. Rôzne opravy chýb a vylepšenia stability. +Úplný zoznam zmien: https://github.com/vector-im/element-android/releases From 9e85abe8e61c6a985459a659407b85f1adf39c98 Mon Sep 17 00:00:00 2001 From: Danial Behzadi Date: Tue, 5 Apr 2022 12:21:05 +0000 Subject: [PATCH 116/368] Translated using Weblate (Persian) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/fa/ --- fastlane/metadata/android/fa/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/fa/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/fa/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/fa/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/fa/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/fa/changelogs/40104080.txt diff --git a/fastlane/metadata/android/fa/changelogs/40104060.txt b/fastlane/metadata/android/fa/changelogs/40104060.txt new file mode 100644 index 0000000000..5a1188b370 --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40104060.txt @@ -0,0 +1,2 @@ +تغییرات عمده در این نگارش: خط زمانی رشته‌ها اکنون زنده و سریع‌تر است. چندین رفع اشکال و بهبود پایداری. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/fa/changelogs/40104070.txt b/fastlane/metadata/android/fa/changelogs/40104070.txt new file mode 100644 index 0000000000..d35ded337b --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40104070.txt @@ -0,0 +1,2 @@ +تغییرات عمده در این نگارش: چندین رفع اشکال و بهبود پایداری. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/fa/changelogs/40104080.txt b/fastlane/metadata/android/fa/changelogs/40104080.txt new file mode 100644 index 0000000000..673a46a10d --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40104080.txt @@ -0,0 +1,2 @@ +تغییرات عمده در این نگارش: خط زمانی رشته‌ها اکنون زنده و سریع‌تر است. چندین رفع اشکال و بهبود پایداری. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases From 9fb71130a40675a54573ade48cdf9a399a1fe383 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 5 Apr 2022 17:13:06 +0000 Subject: [PATCH 117/368] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/pt_BR/ --- vector/src/main/res/values-pt-rBR/strings.xml | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml index d06937fb64..f682a123f4 100644 --- a/vector/src/main/res/values-pt-rBR/strings.xml +++ b/vector/src/main/res/values-pt-rBR/strings.xml @@ -1953,11 +1953,11 @@ %d entradas Limite de upload de arquivo do servidor - Qualquer pessoa num espaço com esta sala pode encontrá-la e juntar-se a ela. Somente admins desta sala podem adicioná-la a um espaço. + Qualquer pessoa num espaço com esta sala pode achá-la e se juntar a ela. Somente admins desta sala podem adicioná-la a um espaço. Membros de espaço somente - Qualquer pessoa pode encontrar a sala e juntar-se + Qualquer pessoa pode achar a sala e se juntar Pública - Somente pessoas convidadas podem encontrar e juntar-se + Somente pessoas convidadas podem achar e se juntar Privada Configuração de acesso desconhecida (%s) Qualquer pessoa pode tocar na sala, membros podem então aceitar ou rejeitar @@ -2024,7 +2024,7 @@ %d pessoas que você conhece já têm se juntado Juntar-Se a Espaço - Criar Espaço + Criar espaço Junte-se a meu espaço %1$s %2$s Elas vão ser capazes de explorar %s Convidar para %s @@ -2058,7 +2058,7 @@ Que tipo de espaço você quer criar\? Seu espaço privado Seu espaço público - Adicionar Espaço + Adicionar espaço Espaço privado Espaço público Criar um Espaço @@ -2111,8 +2111,8 @@ %d chamadas de áudio perdidas Por favor note que fazer upgrade vai fazer uma nova versão da sala. Todas as mensagens atuais vão permanecer nesta sala arquivada. - Qualquer pessoa em um espaço pai vai ser capaz de encontrar e juntar-se a esta sala - não precisa convidar manualmente todo mundo. Você vai ser capaz de mudar isto em configurações de sala a qualquer hora. - Qualquer pessoa em %s vai ser capaz de encontrar e juntar-se a esta sala - não precisa convidar todo mundo. Você vai ser capaz de mudar isto em configurações de sala a qualquer hora. + Qualquer pessoa em um espaço pai vai ser capaz de achar e se juntar a esta sala - não precisa convidar manualmente todo mundo. Você vai ser capaz de mudar isto em configurações de sala a qualquer hora. + Qualquer pessoa em %s vai ser capaz de achar e se juntar a esta sala - não precisa convidar todo mundo. Você vai ser capaz de mudar isto em configurações de sala a qualquer hora. Mensagem de Voz (%1$s) Não dá para responder ou editar enquanto mensagem de voz está ativa Não dá para gravar uma mensagem de voz @@ -2130,13 +2130,13 @@ Voz Outros espaços ou salas que você poderia não saber Espaço que você sabe que contém esta sala - Decida quem pode encontrar e juntar-se a esta sala. + Decida quem pode achar e se juntar a esta sala. Toque para editar espaços Selecionar espaços - Decida que espaços podem acessar esta sala. Se um espaço é selecionado seus membros vão ser capazes de encontrar e juntar-se a Nome de sala. + Decida que espaços podem acessar esta sala. Se um espaço é selecionado seus membros vão ser capazes de achar e se juntar a Nome de sala. Espaços que podem acessar Permitir membros de espaço a encontrar e acessar. - Membros de Escpaço %s podem encontrar, previsualizar e juntar-se. + Membros de Espaço %s podem achar, previsualizar e se juntar. Privada (Convite Somente) Para enviar mensagens de voz, por favor conceda a permissão Microfone. Notifique-me para @@ -2222,7 +2222,7 @@ Abrir Configurações de Descoberta Pesquisar por nome, ID ou mail Criar Novo Espaço - Qualquer pessoa pode encontrar o espaço e se juntar + Qualquer pessoa pode achar o espaço e se juntar Acesso a espaço Quem pode acessar\? Habilitar notificações de email para %s @@ -2392,7 +2392,7 @@ O comando \"%s\" é reconhecido mas não suportado em threads. De uma Thread Dica: Toque longo numa mensagem e use “%s”. - Threads ajudam a manter suas conversas em-tópico e fáceis de rastrear. + Threads ajudam manter suas conversas em-tópico e fáceis de rastrear. Mantenha discussões organizadas com threads Mostra todas as threads nas quais você tem participado Minhas Threads @@ -2438,4 +2438,23 @@ \n \nIsto vai ser uma transição única visto que Threads são agora parte da especificação Matrix. Threads Aproximando-Se a Beta 🎉 + ${app_name} Localização ao Vivo + Compartilhamento de localização está em progresso + O servidorcasa não aceita nome de usuária(o) com somente dígitos. + Pular este passo + Salvar e continuar + Suas preferências têm sido salvas. + Você está pronta(o)! + Vamo lá + Você pode mudar isto a qualquer hora. + Adicione uma imagem de perfil + Você pode mudar isto mais tarde + Nome de Exibição + Isto vai ser mostrado quando você enviar mensagens. + Escolha um nome de exibição + Sua conta %s tem sido criada. + Parabéns! + Me leve para casa + Personalizar perfil + Desabilitar \ No newline at end of file From e6e9681856d8898a2a57a2b946152742c4dea5c2 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 6 Apr 2022 04:04:09 +0000 Subject: [PATCH 118/368] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/zh_Hant/ --- fastlane/metadata/android/zh-TW/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/zh-TW/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/zh-TW/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/zh-TW/changelogs/40104080.txt diff --git a/fastlane/metadata/android/zh-TW/changelogs/40104060.txt b/fastlane/metadata/android/zh-TW/changelogs/40104060.txt new file mode 100644 index 0000000000..316fad3363 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40104060.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:討論串時間軸現已更新,而且更快了。多個臭蟲修復與穩定性改善。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40104070.txt b/fastlane/metadata/android/zh-TW/changelogs/40104070.txt new file mode 100644 index 0000000000..2cd9da666e --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40104070.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:多個臭蟲修復與穩定性改善。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40104080.txt b/fastlane/metadata/android/zh-TW/changelogs/40104080.txt new file mode 100644 index 0000000000..c036aa7d56 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40104080.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:討論串時間軸現已更新,而且更快了。多個臭蟲修復與穩定性改善。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases From 2b3e3cf7e69c36a08b347fe687e34c521d862447 Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Wed, 6 Apr 2022 19:27:48 +0000 Subject: [PATCH 119/368] Translated using Weblate (Swedish) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/sv/ --- fastlane/metadata/android/sv-SE/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/sv-SE/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/sv-SE/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/sv-SE/changelogs/40104080.txt diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104060.txt b/fastlane/metadata/android/sv-SE/changelogs/40104060.txt new file mode 100644 index 0000000000..16a49fa3bc --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Trådtidslinje är nu live och snabbare. Diverse buggfixar och stabilitetsförbättringar. +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104070.txt b/fastlane/metadata/android/sv-SE/changelogs/40104070.txt new file mode 100644 index 0000000000..01f79e85d6 --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Diverse buggfixar och stabilitetsförbättringar. +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/sv-SE/changelogs/40104080.txt b/fastlane/metadata/android/sv-SE/changelogs/40104080.txt new file mode 100644 index 0000000000..2ac7df2cda --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Trådtidslinje är nu live och snabbare. Diverse buggfixar och stabilitetsförbättringar. +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.4.8 From 7ae42d81c15cecd51a03f6f50466a4429701d546 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 5 Apr 2022 17:34:15 +0000 Subject: [PATCH 120/368] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2190 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/uk/ --- vector/src/main/res/values-uk/strings.xml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/vector/src/main/res/values-uk/strings.xml b/vector/src/main/res/values-uk/strings.xml index 26d090e3ef..ce1781cb9c 100644 --- a/vector/src/main/res/values-uk/strings.xml +++ b/vector/src/main/res/values-uk/strings.xml @@ -1490,7 +1490,7 @@ Невідоме налаштування доступу (%s) Будь-хто може попроситися до кімнати, учасники можуть прийняти або відхилити Усе одно приєднатися - Створити Простір + Створити простір Приєднатися до Простору Поділитися посиланням Запросити електронним листом @@ -2530,4 +2530,23 @@ \n \nЦе буде одноразовий перехід, оскільки треди — тепер частина специфікації Matrix. Незабаром бета-версія тредів 🎉 + Триває передавання місцеперебування + ${app_name} Місцеперебування наживо + Домашній сервер не приймає ім\'я користувача лише з цифр. + Пропустити цей крок + Зберегти й продовжити + Ваші налаштування збережено. + Усе налаштовано! + Поїхали + Ви можете змінити його будь-коли. + Додати зображення профілю + Ви можете змінити його пізніше + Показуване ім\'я + Його буде показано у надісланих повідомленнях. + Виберіть показуване ім\'я + Ваш обліковий запис %s створено. + Вітаємо! + На головну + Персоналізувати профіль + Вимкнути \ No newline at end of file From 051cf101d1f6e3335b7fb86ed2e2a55d259da7f6 Mon Sep 17 00:00:00 2001 From: Linerly Date: Tue, 5 Apr 2022 22:59:01 +0000 Subject: [PATCH 121/368] Translated using Weblate (Indonesian) Currently translated at 100.0% (55 of 55 strings) Translation: Element Android/Element Android Store Translate-URL: https://translate.element.io/projects/element-android/element-store/id/ --- fastlane/metadata/android/id/changelogs/40104060.txt | 2 ++ fastlane/metadata/android/id/changelogs/40104070.txt | 2 ++ fastlane/metadata/android/id/changelogs/40104080.txt | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 fastlane/metadata/android/id/changelogs/40104060.txt create mode 100644 fastlane/metadata/android/id/changelogs/40104070.txt create mode 100644 fastlane/metadata/android/id/changelogs/40104080.txt diff --git a/fastlane/metadata/android/id/changelogs/40104060.txt b/fastlane/metadata/android/id/changelogs/40104060.txt new file mode 100644 index 0000000000..3b4ce82497 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40104060.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Linimasa utasan sekarang langsung dan lebih cepat. Banyak perbaikan kutu dan perbaikan stabilitas. +Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.4.6 diff --git a/fastlane/metadata/android/id/changelogs/40104070.txt b/fastlane/metadata/android/id/changelogs/40104070.txt new file mode 100644 index 0000000000..39daf31eb6 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40104070.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Banyak perbaikan kutu dan perbaikan stabilitas. +Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.4.7 diff --git a/fastlane/metadata/android/id/changelogs/40104080.txt b/fastlane/metadata/android/id/changelogs/40104080.txt new file mode 100644 index 0000000000..0a5ae82096 --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40104080.txt @@ -0,0 +1,2 @@ +Perubahan utama dalam versi ini: Linimasa utasan sekarang langsung dan lebih cepat. Banyak perbaikan kutu dan perbaikan stabilitas. +Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases From c54a124d34ae2ae7524ab9445573723361c49cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 6 Apr 2022 17:24:09 +0000 Subject: [PATCH 122/368] Translated using Weblate (Estonian) Currently translated at 99.9% (2188 of 2190 strings) Translation: Element Android/Element Android App Translate-URL: https://translate.element.io/projects/element-android/element-app/et/ --- vector/src/main/res/values-et/strings.xml | 27 +++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml index e320550323..19b4d37102 100644 --- a/vector/src/main/res/values-et/strings.xml +++ b/vector/src/main/res/values-et/strings.xml @@ -245,7 +245,7 @@ Kas sa oled kindel\? Varunda Kui sa enne väljalogimist ei varunda oma võtmeid, siis sa kaotad ligipääsu oma krüptitud sõnumitele. - Laeme… + Laadime… Sobib Tühista Salvesta @@ -1958,7 +1958,7 @@ Kogukonnakeskused on uus võimalus siduda jututubasid ja inimesi. Lisa olemasolevaid jututubasid ja kogukonnakeskuseid Lahku kogukonnast - Lisa jututuba + Lisa jututube Tutvu jututubadega %d sinu tuttav on juba liitunud @@ -1968,7 +1968,7 @@ \nProovi hiljem uuesti või küsi jututoa haldajalt, kas sul on olemas selleks vajalikud õigused. Liitu ikkagi Liitu kogukonnakeskusega - Loo kogukonnakeskus + Loo kogukond Hetkel jäta vahele Palun liitu %1$s kogukonnakeskusega %2$s Nad ei saa osalema %s jututoas @@ -2007,7 +2007,7 @@ Missugust kogukonnakeskust sooviksid sa luua\? Sinu privaatne kogukonnakeskus Sinu avalik kogukonnakeskus - Lisa kogukonnakeskus + Lisa kogukond Lahku antud tunnusega jututoast (tunnuse puudumisel jututoast, kus hetkel asud) Liitu kogukonnakeskusega tema tunnuse alusel Loo kogukonnakeskus @@ -2438,4 +2438,23 @@ \n \nKuna jutulõngad on nüüd osa Matrix\'i spetsifikatsioonist, siis see on ühekordne muudatus. Jutulõngad on peaaegu valmis 🎉 + Asukoha reaalajas jagamine on hetkel kasutusel + Asukoht reaalajas - ${app_name} + Koduserver ei võimalda ainult numbritest koosneva kasutajanime loomist. + Jäta see samm vahele + Salvesta ja jätka + Sinu eelistused on salvestatud. + Kõik on valmis! + Alustame nüüd + Sa võid seda hiljem alati muuta. + Lisa profiilipilt + Sa võid seda hiljem muuta + Kuvatav nimi + Seda näidatakse sõnumite saatmisel. + Vali kuvatav nimi + Sinu kasutajakonto %s on nüüd olemas. + Õnnitlused! + Mine avalehele + Isikupärasta oma profiili + Lülita välja \ No newline at end of file From 15e1c7bc3713e8643f18b0e962066219fc557ece Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 8 Apr 2022 13:18:17 +0300 Subject: [PATCH 123/368] Code review fixes. --- .../sdk/internal/session/SessionModule.kt | 5 ++ .../EventRelationsAggregationProcessor.kt | 54 ++----------- ...DefaultLiveLocationAggregationProcessor.kt | 80 +++++++++++++++++++ .../LiveLocationAggregationProcessor.kt | 29 +++++++ 4 files changed, 119 insertions(+), 49 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index 531dea1d5a..6e71a5393b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -87,6 +87,8 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor +import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.DefaultLiveLocationAggregationProcessor +import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor @@ -390,4 +392,7 @@ internal abstract class SessionModule { @Binds abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor + + @Binds + abstract fun bindLiveLocationAggregationProcessor(processor: DefaultLiveLocationAggregationProcessor): LiveLocationAggregationProcessor } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index 74c4ab9d68..27a70b27b4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -32,7 +32,6 @@ import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent import org.matrix.android.sdk.api.session.room.model.VoteInfo import org.matrix.android.sdk.api.session.room.model.VoteSummary -import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent @@ -49,7 +48,6 @@ import org.matrix.android.sdk.internal.crypto.verification.toState import org.matrix.android.sdk.internal.database.helper.findRootThreadEvent import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.EventMapper -import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntity import org.matrix.android.sdk.internal.database.model.EditionOfEvent import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity @@ -63,11 +61,11 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.create import org.matrix.android.sdk.internal.database.query.getOrCreate -import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor +import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource import timber.log.Timber import javax.inject.Inject @@ -76,7 +74,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor( @UserId private val userId: String, private val stateEventDataSource: StateEventDataSource, @SessionId private val sessionId: String, - private val sessionManager: SessionManager + private val sessionManager: SessionManager, + private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor ) : EventInsertLiveProcessor { private val allowedTypes = listOf( @@ -191,7 +190,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } in EventType.BEACON_LOCATION_DATA -> { event.content.toModel(catchError = true)?.let { - handleLiveLocation(realm, event, it, roomId, isLocalEcho) + liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho) } } } @@ -254,7 +253,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } in EventType.BEACON_LOCATION_DATA -> { event.content.toModel(catchError = true)?.let { - handleLiveLocation(realm, event, it, roomId, isLocalEcho) + liveLocationAggregationProcessor.handleLiveLocation(realm, event, it, roomId, isLocalEcho) } } else -> Timber.v("UnHandled event ${event.eventId}") @@ -546,49 +545,6 @@ internal class EventRelationsAggregationProcessor @Inject constructor( } } - private fun handleLiveLocation(realm: Realm, - event: Event, - content: MessageLiveLocationContent, - roomId: String, - isLocalEcho: Boolean) { - val locationSenderId = event.senderId ?: return - - // We shouldn't process local echos - if (isLocalEcho) { - return - } - - // A beacon info state event has to be sent before sending location - val beaconInfoEntity = CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, EventType.STATE_ROOM_BEACON_INFO.first()) - if (beaconInfoEntity == null) { - Timber.v("## LIVE LOCATION. There is not any beacon info which should be emitted before sending location updates") - return - } - val beaconInfoContent = ContentMapper.map(beaconInfoEntity.root?.content)?.toModel(catchError = true) - if (beaconInfoContent == null) { - Timber.v("## LIVE LOCATION. Beacon info content is invalid") - return - } - - // Check if beacon info is outdated - if (isBeaconInfoOutdated(beaconInfoContent, content)) { - Timber.v("## LIVE LOCATION. Beacon info content is invalid") - return - } - - // Update last location info of the beacon state event - beaconInfoContent.lastLocationContent = content - beaconInfoEntity.root?.content = ContentMapper.map(beaconInfoContent.toContent()) - } - - private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent, - liveLocationContent: MessageLiveLocationContent): Boolean { - val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0 - val liveLocationEventTime = liveLocationContent.getBestTs() ?: 0 - val timeout = beaconInfoContent.getBestBeaconInfo()?.timeout ?: 0 - return liveLocationEventTime - beaconInfoStartTime > timeout - } - private fun handleInitialAggregatedRelations(realm: Realm, event: Event, roomId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt new file mode 100644 index 0000000000..54bfe45cb9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2022 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.internal.session.room.aggregation.livelocation + +import io.realm.Realm +import org.matrix.android.sdk.api.extensions.orFalse +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.toContent +import org.matrix.android.sdk.api.session.events.model.toModel +import org.matrix.android.sdk.api.session.room.model.livelocation.LiveLocationBeaconContent +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent +import org.matrix.android.sdk.internal.database.mapper.ContentMapper +import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity +import org.matrix.android.sdk.internal.database.query.getOrNull +import timber.log.Timber +import javax.inject.Inject + +internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : LiveLocationAggregationProcessor { + + override fun handleLiveLocation(realm: Realm, event: Event, content: MessageLiveLocationContent, roomId: String, isLocalEcho: Boolean) { + val locationSenderId = event.senderId ?: return + + // We shouldn't process local echos + if (isLocalEcho) { + return + } + + // A beacon info state event has to be sent before sending location + val beaconInfoEntity = CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, EventType.STATE_ROOM_BEACON_INFO.first()) + ?: CurrentStateEventEntity.getOrNull(realm, roomId, locationSenderId, EventType.STATE_ROOM_BEACON_INFO.last()) + if (beaconInfoEntity == null) { + Timber.v("## LIVE LOCATION. There is not any beacon info which should be emitted before sending location updates") + return + } + val beaconInfoContent = ContentMapper.map(beaconInfoEntity.root?.content)?.toModel(catchError = true) + if (beaconInfoContent == null) { + Timber.v("## LIVE LOCATION. Beacon info content is invalid") + return + } + + // Check if live location is ended + if (!beaconInfoContent.getBestBeaconInfo()?.isLive.orFalse()) { + Timber.v("## LIVE LOCATION. Beacon info is not live anymore") + return + } + + // Check if beacon info is outdated + if (isBeaconInfoOutdated(beaconInfoContent, content)) { + Timber.v("## LIVE LOCATION. Beacon info has timeout") + return + } + + // Update last location info of the beacon state event + beaconInfoContent.lastLocationContent = content + beaconInfoEntity.root?.content = ContentMapper.map(beaconInfoContent.toContent()) + } + + private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent, + liveLocationContent: MessageLiveLocationContent): Boolean { + val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0 + val liveLocationEventTime = liveLocationContent.getBestTs() ?: 0 + val timeout = beaconInfoContent.getBestBeaconInfo()?.timeout ?: 0 + return liveLocationEventTime - beaconInfoStartTime > timeout + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt new file mode 100644 index 0000000000..447eed21b0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2022 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.internal.session.room.aggregation.livelocation + +import io.realm.Realm +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.message.MessageLiveLocationContent + +interface LiveLocationAggregationProcessor { + fun handleLiveLocation(realm: Realm, + event: Event, + content: MessageLiveLocationContent, + roomId: String, + isLocalEcho: Boolean) +} From 28f483870f67c9c1330dd3313425b79a2b06cc8b Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Fri, 8 Apr 2022 13:26:36 +0300 Subject: [PATCH 124/368] Code review fixes. --- .../room/model/message/MessageLiveLocationContent.kt | 6 +++--- .../livelocation/DefaultLiveLocationAggregationProcessor.kt | 2 +- .../sdk/internal/session/room/send/LocalEchoEventFactory.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt index e5de46b3ef..548dc85369 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLiveLocationContent.kt @@ -42,11 +42,11 @@ data class MessageLiveLocationContent( /** * Exact time that the data in the event refers to (milliseconds since the UNIX epoch) */ - @Json(name = "org.matrix.msc3488.ts") val unstableTs: Long? = null, - @Json(name = "m.ts") val ts: Long? = null + @Json(name = "org.matrix.msc3488.ts") val unstableTimestampAsMilliseconds: Long? = null, + @Json(name = "m.ts") val timestampAsMilliseconds: Long? = null ) : MessageContent { fun getBestLocationInfo() = locationInfo ?: unstableLocationInfo - fun getBestTs() = ts ?: unstableTs + fun getBestTimestampAsMilliseconds() = timestampAsMilliseconds ?: unstableTimestampAsMilliseconds } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt index 54bfe45cb9..7d37ddc22b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt @@ -73,7 +73,7 @@ internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : L private fun isBeaconInfoOutdated(beaconInfoContent: LiveLocationBeaconContent, liveLocationContent: MessageLiveLocationContent): Boolean { val beaconInfoStartTime = beaconInfoContent.getBestTimestampAsMilliseconds() ?: 0 - val liveLocationEventTime = liveLocationContent.getBestTs() ?: 0 + val liveLocationEventTime = liveLocationContent.getBestTimestampAsMilliseconds() ?: 0 val timeout = beaconInfoContent.getBestBeaconInfo()?.timeout ?: 0 return liveLocationEventTime - beaconInfoStartTime > timeout } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index d7d74ca601..ba98bfc25b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -256,7 +256,7 @@ internal class LocalEchoEventFactory @Inject constructor( eventId = beaconInfoEventId ), unstableLocationInfo = LocationInfo(geoUri = geoUri, description = geoUri), - unstableTs = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + unstableTimestampAsMilliseconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), ) val localId = LocalEcho.createLocalEchoId() return Event( From c3cf22158b539434f21f90a80f63bc81c0bd58d0 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 30 Mar 2022 17:08:51 +0100 Subject: [PATCH 125/368] adding barebones server selection UI --- .../FtueAuthCombinedRegisterFragment.kt | 10 + ...FtueAuthCombinedServerSelectionFragment.kt | 44 +++ .../main/res/drawable/ic_choose_server.xml | 9 + vector/src/main/res/drawable/ic_ems_logo.xml | 22 ++ ...ragment_ftue_server_selection_combined.xml | 256 ++++++++++++++++++ .../layout/fragment_ftue_sign_up_combined.xml | 1 - vector/src/main/res/values/donottranslate.xml | 8 + 7 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt create mode 100644 vector/src/main/res/drawable/ic_choose_server.xml create mode 100644 vector/src/main/res/drawable/ic_ems_logo.xml create mode 100644 vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt index 3a7d51d14b..5096598386 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedRegisterFragment.kt @@ -22,6 +22,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo +import androidx.appcompat.app.AppCompatActivity import androidx.autofill.HintConstants import androidx.core.text.isDigitsOnly import androidx.core.view.isVisible @@ -29,6 +30,8 @@ import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R +import im.vector.app.core.extensions.addFragment +import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.content import im.vector.app.core.extensions.editText import im.vector.app.core.extensions.hasContentFlow @@ -36,6 +39,7 @@ import im.vector.app.core.extensions.hasSurroundingSpaces import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hidePassword import im.vector.app.core.extensions.realignPercentagesToParent +import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.databinding.FragmentFtueSignUpCombinedBinding import im.vector.app.features.login.LoginMode import im.vector.app.features.login.SSORedirectRouterActivity @@ -67,6 +71,12 @@ class FtueAuthCombinedRegisterFragment @Inject constructor() : AbstractSSOFtueAu views.createAccountRoot.realignPercentagesToParent() + views.editServerButton.debouncedClicks { + requireActivity().supportFragmentManager.commitTransaction(true) { + add(R.id.loginFragmentContainer, FtueAuthCombinedServerSelectionFragment(), null) + } + } + views.createAccountPasswordInput.editText().setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { submit() diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt new file mode 100644 index 0000000000..fdd81b99c0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthCombinedServerSelectionFragment.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 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.onboarding.ftueauth + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import im.vector.app.databinding.FragmentFtueServerSelectionCombinedBinding +import im.vector.app.features.onboarding.OnboardingAction +import im.vector.app.features.onboarding.OnboardingViewEvents +import javax.inject.Inject + +class FtueAuthCombinedServerSelectionFragment @Inject constructor() : AbstractSSOFtueAuthFragment() { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentFtueServerSelectionCombinedBinding { + return FragmentFtueServerSelectionCombinedBinding.inflate(inflater, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.chooseServerToolbar.setNavigationOnClickListener { + viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnBack)) + } + } + + override fun resetViewModel() { + + } +} diff --git a/vector/src/main/res/drawable/ic_choose_server.xml b/vector/src/main/res/drawable/ic_choose_server.xml new file mode 100644 index 0000000000..26c8e75222 --- /dev/null +++ b/vector/src/main/res/drawable/ic_choose_server.xml @@ -0,0 +1,9 @@ + + + diff --git a/vector/src/main/res/drawable/ic_ems_logo.xml b/vector/src/main/res/drawable/ic_ems_logo.xml new file mode 100644 index 0000000000..68c2aeb190 --- /dev/null +++ b/vector/src/main/res/drawable/ic_ems_logo.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml b/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml new file mode 100644 index 0000000000..8da4958007 --- /dev/null +++ b/vector/src/main/res/layout/fragment_ftue_server_selection_combined.xml @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +