From 5d2770ad699c0b5e555838a242041642cf9c2f29 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Nov 2023 17:18:58 +0100 Subject: [PATCH 001/102] version++ --- plugins/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/src/main/kotlin/Versions.kt b/plugins/src/main/kotlin/Versions.kt index 85018b4b01..8dac5b7612 100644 --- a/plugins/src/main/kotlin/Versions.kt +++ b/plugins/src/main/kotlin/Versions.kt @@ -56,7 +56,7 @@ private const val versionMinor = 3 // 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. -private const val versionPatch = 1 +private const val versionPatch = 2 object Versions { val versionCode = 4_000_000 + versionMajor * 1_00_00 + versionMinor * 1_00 + versionPatch From 14be887cac933e21b514d2461b335f75d63ca8e3 Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Fri, 10 Nov 2023 09:18:01 +0000 Subject: [PATCH 002/102] Enable seeking a recorded voice message (#1758) --- .../composer/VoiceMessageComposerPlayer.kt | 225 ++++++++++++++---- .../composer/VoiceMessageComposerPresenter.kt | 77 +++--- .../timeline/VoiceMessagePlayer.kt | 2 +- .../messages/MessagesPresenterTest.kt | 2 +- .../VoiceMessageComposerPresenterTest.kt | 44 ++-- .../components/VoiceMessagePreview.kt | 2 +- libraries/voicerecorder/api/build.gradle.kts | 2 +- .../voicerecorder/api/VoiceRecorderState.kt | 2 + 8 files changed, 261 insertions(+), 95 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt index 545a604af2..584ec96f2a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt @@ -17,96 +17,233 @@ package io.element.android.features.messages.impl.voicemessages.composer import io.element.android.libraries.mediaplayer.api.MediaPlayer +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.scan +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject /** * A media player for the voice message composer. * * @param mediaPlayer The [MediaPlayer] to use. + * @param coroutineScope */ class VoiceMessageComposerPlayer @Inject constructor( private val mediaPlayer: MediaPlayer, + private val coroutineScope: CoroutineScope, ) { - private var lastPlayedMediaPath: String? = null - private val curPlayingMediaId - get() = mediaPlayer.state.value.mediaId + companion object { + const val MIME_TYPE = "audio/ogg" + } - val state: Flow = mediaPlayer.state.map { state -> - if (lastPlayedMediaPath == null || lastPlayedMediaPath != state.mediaId) { - return@map State.NotLoaded + private var mediaPath: String? = null + + private var seekJob: Job? = null + private val seekingTo = MutableStateFlow(null) + + val state: Flow = combine(mediaPlayer.state, seekingTo) { state, seekingTo -> + state to seekingTo + }.scan(InternalState.NotLoaded) { prevState, (state, seekingTo) -> + if (mediaPath == null || mediaPath != state.mediaId) { + return@scan InternalState.NotLoaded } - State( - isPlaying = state.isPlaying, + InternalState( + playState = calcPlayState(prevState.playState, seekingTo, state), currentPosition = state.currentPosition, - duration = state.duration ?: 0L, + duration = state.duration, + seekingTo = seekingTo, + ) + }.map { + State( + playState = it.playState, + currentPosition = it.currentPosition, + progress = calcProgress(it), ) }.distinctUntilChanged() + /** + * Set the voice message to be played. + */ + suspend fun setMedia(mediaPath: String) { + this.mediaPath = mediaPath + mediaPlayer.setMedia( + uri = mediaPath, + mediaId = mediaPath, + mimeType = MIME_TYPE, + ) + } + /** * Start playing from the current position. * - * @param mediaPath The path to the media to be played. - * @param mimeType The mime type of the media file. + * Call [setMedia] before calling this method. */ - suspend fun play(mediaPath: String, mimeType: String) { - if (mediaPath == curPlayingMediaId) { - mediaPlayer.play() - } else { - lastPlayedMediaPath = mediaPath - mediaPlayer.setMedia( - uri = mediaPath, - mediaId = mediaPath, - mimeType = mimeType, - ) - mediaPlayer.play() + suspend fun play() { + val mediaPath = this.mediaPath + if (mediaPath == null) { + Timber.e("Set media before playing") + return } + + mediaPlayer.ensureMediaReady(mediaPath) + + mediaPlayer.play() } /** * Pause playback. */ fun pause() { - if (lastPlayedMediaPath == curPlayingMediaId) { + if (mediaPath == mediaPlayer.state.value.mediaId) { mediaPlayer.pause() } } + /** + * Seek to a given position in the current media. + * + * Call [setMedia] before calling this method. + * + * @param position The position to seek to between 0 and 1. + */ + suspend fun seek(position: Float) { + val mediaPath = this.mediaPath + if (mediaPath == null) { + Timber.e("Set media before seeking") + return + } + + seekJob?.cancelAndJoin() + seekingTo.value = position + seekJob = coroutineScope.launch { + val mediaState = mediaPlayer.ensureMediaReady(mediaPath) + val duration = mediaState.duration ?: return@launch + val positionMs = (duration * position).toLong() + mediaPlayer.seekTo(positionMs) + }.apply { + invokeOnCompletion { + seekingTo.value = null + } + } + } + + private suspend fun MediaPlayer.ensureMediaReady(mediaPath: String): MediaPlayer.State { + val state = state.value + if (state.mediaId == mediaPath && state.isReady) { + return state + } + + return setMedia( + uri = mediaPath, + mediaId = mediaPath, + mimeType = MIME_TYPE, + ) + } + + private fun calcPlayState(prevPlayState: PlayState, seekingTo: Float?, state: MediaPlayer.State): PlayState { + if (state.mediaId == null || state.mediaId != mediaPath) { + return PlayState.Stopped + } + + // If we were stopped and the player didn't start playing or seeking, we are still stopped. + if (prevPlayState == PlayState.Stopped && !state.isPlaying && seekingTo == null) { + return PlayState.Stopped + } + + return if (state.isPlaying) { + PlayState.Playing + } else { + PlayState.Paused + } + } + + private fun calcProgress(state: InternalState): Float { + if (state.seekingTo != null) { + return state.seekingTo + } + + if (state.playState == PlayState.Stopped) { + return 0f + } + + if (state.duration == null) { + return 0f + } + + return (state.currentPosition.toFloat() / state.duration.toFloat()) + .coerceAtMost(1f) // Current position may exceed reported duration + } + + /** + * @property playState Whether this player is currently playing. See [PlayState]. + * @property currentPosition The elapsed time of this player in milliseconds. + * @property progress The progress of this player between 0 and 1. + */ data class State( + val playState: PlayState, + val currentPosition: Long, + val progress: Float, + ) { + + companion object { + val Initial = State( + playState = PlayState.Stopped, + currentPosition = 0L, + progress = 0f, + ) + } /** * Whether this player is currently playing. */ - val isPlaying: Boolean, + val isPlaying get() = this.playState == PlayState.Playing + /** - * The elapsed time of this player in milliseconds. + * Whether this player is currently stopped. */ + val isStopped get() = this.playState == PlayState.Stopped + } + + + enum class PlayState { + /** + * The player is stopped, i.e. it has just been initialised. + */ + Stopped, + + /** + * The player is playing. + */ + Playing, + + /** + * The player has been paused. The player can also enter the paused state after seeking to a position. + */ + Paused, + } + + private data class InternalState( + val playState: PlayState, val currentPosition: Long, - /** - * The duration of this player in milliseconds. - */ - val duration: Long, + val duration: Long?, + val seekingTo: Float?, ) { companion object { - val NotLoaded = State( - isPlaying = false, + val NotLoaded = InternalState( + playState = PlayState.Stopped, currentPosition = 0L, - duration = 0L, + duration = null, + seekingTo = null, ) } - - val isLoaded get() = this != NotLoaded - - /** - * The progress of this player between 0 and 1. - */ - val progress: Float = - if (duration == 0L) - 0f - else - (currentPosition.toFloat() / duration.toFloat()) - .coerceAtMost(1f) // Current position may exceed reported duration } } + diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt index bb642226a9..d39b065e5a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.voicemessages.composer import android.Manifest import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -69,13 +70,17 @@ class VoiceMessageComposerPresenter @Inject constructor( override fun present(): VoiceMessageComposerState { val localCoroutineScope = rememberCoroutineScope() val recorderState by voiceRecorder.state.collectAsState(initial = VoiceRecorderState.Idle) + val playerState by player.state.collectAsState(initial = VoiceMessageComposerPlayer.State.Initial) val keepScreenOn by remember { derivedStateOf { recorderState is VoiceRecorderState.Recording } } val permissionState = permissionsPresenter.present() var isSending by remember { mutableStateOf(false) } - val playerState by player.state.collectAsState(initial = VoiceMessageComposerPlayer.State.NotLoaded) - val playerTime by remember(playerState, recorderState) { derivedStateOf { displayTime(playerState, recorderState) } } - val waveform by remember(recorderState) { derivedStateOf { recorderState.finishedWaveform() } } + + LaunchedEffect(recorderState) { + val recording = recorderState as? VoiceRecorderState.Finished + ?: return@LaunchedEffect + player.setMedia(recording.file.path) + } val onLifecycleEvent = { event: Lifecycle.Event -> when (event) { @@ -115,27 +120,15 @@ class VoiceMessageComposerPresenter @Inject constructor( } } } - val onPlayerEvent = { event: VoiceMessagePlayerEvent -> - when (event) { - VoiceMessagePlayerEvent.Play -> - when (val recording = recorderState) { - is VoiceRecorderState.Finished -> - localCoroutineScope.launch { - player.play( - mediaPath = recording.file.path, - mimeType = recording.mimeType, - ) - } - else -> Timber.e("Voice message player event received but no file to play") - } - VoiceMessagePlayerEvent.Pause -> { - player.pause() - } - is VoiceMessagePlayerEvent.Seek -> { - // TODO implement seeking + val onPlayerEvent = { event: VoiceMessagePlayerEvent -> localCoroutineScope.launch { + localCoroutineScope.launch { + when (event) { + VoiceMessagePlayerEvent.Play -> player.play() + VoiceMessagePlayerEvent.Pause -> player.pause() + is VoiceMessagePlayerEvent.Seek -> player.seek(event.position) } } - } + } } val onAcceptPermissionsRationale = { permissionState.eventSink(PermissionsEvents.OpenSystemSettingAndCloseDialog) @@ -189,16 +182,14 @@ class VoiceMessageComposerPresenter @Inject constructor( voiceMessageState = when (val state = recorderState) { is VoiceRecorderState.Recording -> VoiceMessageState.Recording( duration = state.elapsedTime, - levels = state.levels.toPersistentList() - ) - is VoiceRecorderState.Finished -> VoiceMessageState.Preview( - isSending = isSending, - isPlaying = playerState.isPlaying, - showCursor = playerState.isLoaded && !isSending, - playbackProgress = playerState.progress, - time = playerTime, - waveform = waveform, + levels = state.levels.toPersistentList(), ) + is VoiceRecorderState.Finished -> + previewState( + playerState = playerState, + recorderState = recorderState, + isSending = isSending + ) else -> VoiceMessageState.Idle }, showPermissionRationaleDialog = permissionState.showDialog, @@ -207,6 +198,26 @@ class VoiceMessageComposerPresenter @Inject constructor( ) } + @Composable + private fun previewState( + playerState: VoiceMessageComposerPlayer.State, + recorderState: VoiceRecorderState, + isSending: Boolean, + ): VoiceMessageState { + val showCursor by remember(playerState.isStopped, isSending) { derivedStateOf { !playerState.isStopped && !isSending }} + val playerTime by remember(playerState, recorderState) { derivedStateOf { displayTime(playerState, recorderState) } } + val waveform by remember(recorderState) { derivedStateOf { recorderState.finishedWaveform() } } + + return VoiceMessageState.Preview( + isSending = isSending, + isPlaying = playerState.isPlaying, + showCursor = showCursor, + playbackProgress = playerState.progress, + time = playerTime, + waveform = waveform, + ) + } + private fun CoroutineScope.startRecording() = launch { try { voiceRecorder.startRecord() @@ -248,7 +259,7 @@ class VoiceMessageComposerPresenter @Inject constructor( } private fun AnalyticsService.captureComposerEvent() = - analyticsService.capture( + capture( Composer( inThread = messageComposerContext.composerMode.inThread, isEditing = messageComposerContext.composerMode.isEditing, @@ -273,7 +284,7 @@ private fun displayTime( playerState: VoiceMessageComposerPlayer.State, recording: VoiceRecorderState ): Duration = when { - playerState.isLoaded -> + !playerState.isStopped -> playerState.currentPosition.milliseconds recording is VoiceRecorderState.Finished -> recording.duration diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt index 67297fc3b3..f96342a308 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt @@ -154,7 +154,7 @@ class DefaultVoiceMessagePlayer( mediaPlayer.setMedia( uri = mediaFile.path, mediaId = eventId.value, - mimeType = "audio/ogg" // Files in the voice cache have no extension so we need to set the mime type manually. + mimeType = "audio/ogg", // Files in the voice cache have no extension so we need to set the mime type manually. ) mediaPlayer.play() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 640be695e9..2969f26580 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -641,7 +641,7 @@ class MessagesPresenterTest { FakeVoiceRecorder(), analyticsService, mediaSender, - player = VoiceMessageComposerPlayer(FakeMediaPlayer()), + player = VoiceMessageComposerPlayer(FakeMediaPlayer(), this), messageComposerContext = FakeMessageComposerContext(), permissionsPresenterFactory, ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 598ceaf227..1a6ff988db 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -35,8 +35,8 @@ import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_USER_NAME -import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.matrix.test.room.FakeMatrixRoom +import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.permissions.api.PermissionsPresenter @@ -195,7 +195,6 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) - awaitItem().apply { assertThat(voiceMessageState).isEqualTo(aLoadedState()) } val finalState = awaitItem().also { assertThat(it.voiceMessageState).isEqualTo(aPlayingState()) } @@ -214,7 +213,6 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) - skipItems(1) // Loaded state awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Pause)) val finalState = awaitItem().also { assertThat(it.voiceMessageState).isEqualTo(aPausedState()) @@ -225,6 +223,33 @@ class VoiceMessageComposerPresenterTest { } } + @Test + fun `present - seek recording`() = runTest { + val presenter = createVoiceMessageComposerPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Seek(0.5f))) + awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.5f, time = 0.seconds, showCursor = true)) + } + awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.5f, time = 5.seconds, showCursor = true)) + eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Seek(0.2f))) + } + awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.2f, time = 5.seconds, showCursor = true)) + } + val finalState = awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.2f, time = 2.seconds, showCursor = true)) + } + + testPauseAndDestroy(finalState) + } + } + @Test fun `present - delete recording`() = runTest { val presenter = createVoiceMessageComposerPresenter() @@ -252,7 +277,6 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) - skipItems(1) // Loaded state awaitItem().eventSink(VoiceMessageComposerEvents.DeleteVoiceMessage) awaitItem().apply { assertThat(voiceMessageState).isEqualTo(aPausedState()) @@ -324,9 +348,9 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) - skipItems(1) // Loaded state awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) assertThat(awaitItem().voiceMessageState).isEqualTo(aPlayingState().toSendingState()) + skipItems(1) // Duplicate sending state val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) @@ -603,7 +627,7 @@ class VoiceMessageComposerPresenterTest { voiceRecorder, analyticsService, mediaSender, - player = VoiceMessageComposerPlayer(FakeMediaPlayer()), + player = VoiceMessageComposerPlayer(FakeMediaPlayer(), this), messageComposerContext = messageComposerContext, FakePermissionsPresenterFactory(permissionsPresenter), ) @@ -639,14 +663,6 @@ class VoiceMessageComposerPresenterTest { waveform = waveform.toImmutableList(), ) - private fun aLoadedState() = - aPreviewState( - isPlaying = false, - playbackProgress = 0.0f, - showCursor = true, - time = 0.seconds, - ) - private fun aPlayingState() = aPreviewState( isPlaying = true, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt index 7197c07307..222ee2aeaf 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt @@ -108,7 +108,7 @@ internal fun VoiceMessagePreview( playbackProgress = playbackProgress, showCursor = showCursor, waveform = waveform, - seekEnabled = false, // TODO enable seeking + seekEnabled = true, onSeek = onSeek, ) } diff --git a/libraries/voicerecorder/api/build.gradle.kts b/libraries/voicerecorder/api/build.gradle.kts index bed69b7d28..f704935bbd 100644 --- a/libraries/voicerecorder/api/build.gradle.kts +++ b/libraries/voicerecorder/api/build.gradle.kts @@ -14,7 +14,7 @@ * limitations under the License. */ plugins { - id("io.element.android-library") + id("io.element.android-compose-library") alias(libs.plugins.anvil) } diff --git a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt index 42035615d5..2bb4f575e3 100644 --- a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt +++ b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt @@ -16,9 +16,11 @@ package io.element.android.libraries.voicerecorder.api +import androidx.compose.runtime.Immutable import java.io.File import kotlin.time.Duration +@Immutable sealed interface VoiceRecorderState { /** * The recorder is idle and not recording. From 2f51919670e569650d1999c35c7a74d374c9685f Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Fri, 10 Nov 2023 10:45:29 +0100 Subject: [PATCH 003/102] Check for the correct media id when ensuring the MediaPlayer is ready (#1783) Just forgot to add this further condition. --- .../android/libraries/mediaplayer/impl/MediaPlayerImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt index 420b01acc7..66e4e748a5 100644 --- a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt +++ b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt @@ -116,7 +116,7 @@ class MediaPlayerImpl @Inject constructor( ) player.prepare() // Will throw TimeoutCancellationException if the player is not ready after 1 second. - return state.timeout(1.seconds).first { it.isReady } + return state.timeout(1.seconds).first { it.isReady && it.mediaId == mediaId } } override fun play() { From 2647a9861bd1f80882f364075a821d9241ce599a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 14:01:20 +0000 Subject: [PATCH 004/102] Update mobile-dev-inc/action-maestro-cloud action to v1.7.0 --- .github/workflows/maestro.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index a51cad74a4..1b5d43a13e 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -40,7 +40,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - - uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} # Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android): From 3a400b58146a4ceb4bebc50be2c4f0ce40a5d0ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 15:52:28 +0100 Subject: [PATCH 005/102] Use gradle catalog. --- build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b2e4ac3c0f..a2dbec823c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,8 +5,8 @@ import org.jetbrains.kotlin.cli.common.toBooleanLenient buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") - classpath("com.google.gms:google-services:4.4.0") + classpath(libs.kotlin.gradle.plugin) + classpath(libs.gms.google.services) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b9b775b1a3..0eab4ed226 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -68,6 +68,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref # https://developer.android.com/studio/write/java8-support#library-desugaring-versions android_desugar = "com.android.tools:desugar_jdk_libs:2.0.4" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +gms_google_services = "com.google.gms:google-services:4.4.0" # https://firebase.google.com/docs/android/setup#available-libraries google_firebase_bom = "com.google.firebase:firebase-bom:32.5.0" From ac940fb5a016acb7dabe5b2980793e2bb33de908 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 15:52:53 +0100 Subject: [PATCH 006/102] Upgrade lint to 8.3.0-alpha12 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index cf609500c2..0ece8f4e8c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -49,7 +49,7 @@ signing.element.nightly.keyPassword=Secret # Customise the Lint version to use a more recent version than the one bundled with AGP # https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html -android.experimental.lint.version=8.3.0-alpha11 +android.experimental.lint.version=8.3.0-alpha12 # Enable test fixture for all modules by default android.experimental.enableTestFixtures=true From 810203119f019450f27241cd01fac6c1eac527ef Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:23:23 +0100 Subject: [PATCH 007/102] Gradle catalog: do not use version.ref if there is only one module which use it. --- gradle/libs.versions.toml | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0eab4ed226..1833873838 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,19 +6,15 @@ android_gradle_plugin = "8.1.3" kotlin = "1.9.20" ksp = "1.9.20-1.0.14" -molecule = "1.3.0" # AndroidX core = "1.12.0" datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" -recyclerview = "1.3.2" lifecycle = "2.7.0-beta01" activity = "1.8.0" -startup = "1.1.1" media3 = "1.1.1" -browser = "1.6.0" # Compose compose_bom = "2023.10.01" @@ -38,13 +34,8 @@ coil = "2.5.0" datetime = "0.4.1" serialization_json = "1.6.0" showkase = "1.0.2" -jsoup = "1.16.2" appyx = "1.4.0" -dependencycheck = "8.4.2" -dependencyanalysis = "1.25.0" -stem = "2.3.0" sqldelight = "2.0.0" -telephoto = "0.6.2" wysiwyg = "2.16.0" # DI @@ -55,12 +46,9 @@ anvil = "2.4.8-1-8" autoservice = "1.1.1" # quality -detekt = "1.23.3" -dependencygraph = "0.12" junit = "4.13.2" androidx-test-ext-junit = "1.1.5" espresso-core = "3.5.1" -appcompat = "1.6.1" [libraries] # Project @@ -82,8 +70,8 @@ androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.6" androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayout_compose" } -androidx_recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } -androidx_browser = { module = "androidx.browser:browser", version.ref = "browser" } +androidx_recyclerview = "androidx.recyclerview:recyclerview:1.3.2" +androidx_browser = "androidx.browser:browser:1.6.0" androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } androidx_splash = "androidx.core:core-splashscreen:1.0.1" @@ -94,11 +82,11 @@ androidx_biometric = "androidx.biometric:biometric-ktx:1.2.0-alpha05" androidx_activity_activity = { module = "androidx.activity:activity", version.ref = "activity" } androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } -androidx_startup = { module = "androidx.startup:startup-runtime", version.ref = "startup" } +androidx_startup = "androidx.startup:startup-runtime:1.1.1" androidx_preference = "androidx.preference:preference:1.2.1" androidx_webkit = "androidx.webkit:webkit:1.8.0" -androidx_compose_bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose_bom" } +androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha10" # Coroutines @@ -146,9 +134,9 @@ datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "d serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } -jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } +jsoup = "org.jsoup:jsoup:1.16.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } -molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" } +molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.0" timber = "com.jakewharton.timber:timber:5.0.1" matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.67" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } @@ -161,7 +149,7 @@ sqlite = "androidx.sqlite:sqlite-ktx:2.4.0" unifiedpush = "com.github.UnifiedPush:android-connector:2.1.1" otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5" vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0" -telephoto_zoomableimage = { module = "me.saket.telephoto:zoomable-image-coil", version.ref = "telephoto" } +telephoto_zoomableimage = "me.saket.telephoto:zoomable-image-coil:0.6.2" statemachine = "com.freeletics.flowredux:compose:1.2.0" maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" @@ -195,7 +183,7 @@ android_composeCompiler = { module = "androidx.compose.compiler:compiler", versi junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } -appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +appcompat = "androidx.appcompat:appcompat:1.6.1" [bundles] @@ -208,11 +196,11 @@ kotlin_serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } anvil = { id = "com.squareup.anvil", version.ref = "anvil" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +detekt = "io.gitlab.arturbosch.detekt:1.23.3" ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1" -dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } -dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } -dependencyanalysis = { id = "com.autonomousapps.dependency-analysis", version.ref = "dependencyanalysis" } +dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" +dependencycheck = "org.owasp.dependencycheck:8.4.2" +dependencyanalysis = "com.autonomousapps.dependency-analysis:1.25.0" paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } From 88195224236d1ad66c2376241b6a12c424d2753f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:25:58 +0100 Subject: [PATCH 008/102] Replace hard-coded value by value from gradle datalog --- features/login/impl/build.gradle.kts | 2 +- libraries/matrix/api/build.gradle.kts | 2 +- libraries/matrix/impl/build.gradle.kts | 2 +- libraries/push/impl/build.gradle.kts | 2 +- libraries/pushproviders/unifiedpush/build.gradle.kts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index b071e6509f..3968ef8ab2 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -19,7 +19,7 @@ plugins { alias(libs.plugins.anvil) alias(libs.plugins.ksp) id("kotlin-parcelize") - kotlin("plugin.serialization") version "1.9.20" + kotlin("plugin.serialization") version libs.versions.kotlin } android { diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index 770f6459a0..f1fec9b60b 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -18,7 +18,7 @@ plugins { id("io.element.android-compose-library") id("kotlin-parcelize") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version "1.9.20" + kotlin("plugin.serialization") version libs.versions.kotlin } android { diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 35c68001f3..9dba63362d 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -17,7 +17,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version "1.9.20" + kotlin("plugin.serialization") version libs.versions.kotlin } android { diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 6c7844bd96..65e4c0939f 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -16,7 +16,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version "1.9.20" + kotlin("plugin.serialization") version libs.versions.kotlin } android { diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index 41bffc5461..5b90f53a08 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -16,7 +16,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version "1.9.20" + kotlin("plugin.serialization") version libs.versions.kotlin } android { From 067734ecb9c36e9c00fe8037e3a8b9935852b4c3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:39:05 +0100 Subject: [PATCH 009/102] Move appdistribution declarations to Gradle catalog --- app/build.gradle.kts | 2 +- gradle/libs.versions.toml | 3 +++ plugins/build.gradle.kts | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c8e11927d1..c4ff2bdaf9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,7 @@ plugins { alias(libs.plugins.anvil) alias(libs.plugins.ksp) alias(libs.plugins.kapt) - id("com.google.firebase.appdistribution") version "4.0.1" + alias(libs.plugins.firebaseAppDistribution) id("org.jetbrains.kotlinx.knit") version "0.4.0" id("kotlin-parcelize") // To be able to update the firebase.xml files, uncomment and build the project diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1833873838..906c2f3b8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,6 +6,7 @@ android_gradle_plugin = "8.1.3" kotlin = "1.9.20" ksp = "1.9.20-1.0.14" +firebaseAppDistribution = "4.0.1" # AndroidX core = "1.12.0" @@ -59,6 +60,7 @@ kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", v gms_google_services = "com.google.gms:google-services:4.4.0" # https://firebase.google.com/docs/android/setup#available-libraries google_firebase_bom = "com.google.firebase:firebase-bom:32.5.0" +firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } # AndroidX androidx_core = { module = "androidx.core:core", version.ref = "core" } @@ -204,6 +206,7 @@ dependencyanalysis = "com.autonomousapps.dependency-analysis:1.25.0" paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } +firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } # Version '4.3.1.3277' introduced some regressions in CI time (more than 2x slower), so make sure # this is no longer the case before upgrading. diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts index 9986d490fa..c820902905 100644 --- a/plugins/build.gradle.kts +++ b/plugins/build.gradle.kts @@ -27,7 +27,6 @@ dependencies { implementation(libs.android.gradle.plugin) implementation(libs.kotlin.gradle.plugin) implementation(platform(libs.google.firebase.bom)) - // FIXME: using the bom ^, it should not be necessary to provide the version v... - implementation("com.google.firebase:firebase-appdistribution-gradle:4.0.1") + implementation(libs.firebase.appdistribution.gradle) implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) } From 67d58bac35eacf6b8241273c9ba42fc1090717d0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:56:04 +0100 Subject: [PATCH 010/102] Move remaining hard-coded dependency to Gradle catalog. We had 2 different version of material3 library. --- anvilcodegen/build.gradle.kts | 2 +- features/rageshake/impl/build.gradle.kts | 2 +- gradle/libs.versions.toml | 10 ++++++++++ libraries/matrix/impl/build.gradle.kts | 2 +- .../pushproviders/unifiedpush/build.gradle.kts | 2 +- .../kotlin/extension/DependencyHandleScope.kt | 16 ++++++++-------- tests/konsist/build.gradle.kts | 2 +- 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/anvilcodegen/build.gradle.kts b/anvilcodegen/build.gradle.kts index 57758f8909..62a8b9d878 100644 --- a/anvilcodegen/build.gradle.kts +++ b/anvilcodegen/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { implementation(projects.anvilannotations) api(libs.anvil.compiler.api) implementation(libs.anvil.compiler.utils) - implementation("com.squareup:kotlinpoet:1.14.2") + implementation(libs.kotlinpoet) implementation(libs.dagger) compileOnly(libs.google.autoservice.annotations) kapt(libs.google.autoservice) diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index eced5c78b8..f119cbf6c8 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { api(projects.features.rageshake.api) implementation(libs.androidx.datastore.preferences) implementation(platform(libs.network.okhttp.bom)) - implementation("com.squareup.okhttp3:okhttp") + implementation(libs.network.okhttp.okhttp) implementation(libs.coil) implementation(libs.coil.compose) ksp(libs.showkase.processor) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 906c2f3b8b..f9baaa81ef 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -90,6 +90,12 @@ androidx_webkit = "androidx.webkit:webkit:1.8.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha10" +androidx_compose_ui = { module = "androidx.compose.ui:ui" } +androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx_compose_ui_tooling_preview = { module = "androidx.compose.ui:ui-tooling-preview" } +androidx_compose_ui_test_manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx_compose_material = { module = "androidx.compose.material:material" } +androidx_compose_material_icons = { module = "androidx.compose.material:material-icons-extended" } # Coroutines coroutines_core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } @@ -105,6 +111,7 @@ squareup_seismic = "com.squareup:seismic:1.0.3" # network network_okhttp_bom = "com.squareup.okhttp3:okhttp-bom:4.12.0" network_okhttp_logging = { module = "com.squareup.okhttp3:logging-interceptor" } +network_okhttp_okhttp = { module = "com.squareup.okhttp3:okhttp" } network_okhttp = { module = "com.squareup.okhttp3:okhttp" } network_retrofit = "com.squareup.retrofit2:retrofit:2.9.0" network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0" @@ -134,6 +141,7 @@ coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } +kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5" showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } jsoup = "org.jsoup:jsoup:1.16.2" @@ -157,6 +165,8 @@ maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" +kotlinpoet = "com.squareup:kotlinpoet:1.14.2" +jna = "net.java.dev.jna:jna:5.13.0@aar" # Analytics posthog = "com.posthog.android:posthog:2.0.3" diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 9dba63362d..026b06f765 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -44,7 +44,7 @@ dependencies { api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) - implementation("net.java.dev.jna:jna:5.13.0@aar") + implementation(libs.jna) implementation(libs.androidx.datastore.preferences) implementation(libs.serialization.json) diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index 5b90f53a08..dfc999c897 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -41,7 +41,7 @@ dependencies { implementation(projects.libraries.network) implementation(platform(libs.network.okhttp.bom)) - implementation("com.squareup.okhttp3:okhttp") + implementation(libs.network.okhttp.okhttp) implementation(libs.network.retrofit) implementation(libs.serialization.json) diff --git a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt index c6786ef8b3..f6ec8b7cf1 100644 --- a/plugins/src/main/kotlin/extension/DependencyHandleScope.kt +++ b/plugins/src/main/kotlin/extension/DependencyHandleScope.kt @@ -42,16 +42,16 @@ fun DependencyHandlerScope.composeDependencies(libs: LibrariesForLibs) { val composeBom = platform(libs.androidx.compose.bom) implementation(composeBom) androidTestImplementation(composeBom) - implementation("androidx.compose.ui:ui") - implementation("androidx.compose.material:material") - implementation("androidx.compose.material3:material3") - implementation("androidx.compose.material:material-icons-extended") - implementation("androidx.compose.ui:ui-tooling-preview") + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.material) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material.icons) + implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.activity.compose) - debugImplementation("androidx.compose.ui:ui-tooling") - debugImplementation("androidx.compose.ui:ui-test-manifest") + debugImplementation(libs.androidx.compose.ui.tooling) + debugImplementation(libs.androidx.compose.ui.test.manifest) implementation(libs.showkase) - implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5") + implementation(libs.kotlinx.collections.immutable) } private fun DependencyHandlerScope.addImplementationProjects( diff --git a/tests/konsist/build.gradle.kts b/tests/konsist/build.gradle.kts index 6658c0c5e0..1ce4e9f232 100644 --- a/tests/konsist/build.gradle.kts +++ b/tests/konsist/build.gradle.kts @@ -25,7 +25,7 @@ android { dependencies { val composeBom = platform(libs.androidx.compose.bom) testImplementation(composeBom) - testImplementation("androidx.compose.ui:ui-tooling-preview") + testImplementation(libs.androidx.compose.ui.tooling.preview) testImplementation(libs.test.junit) testImplementation(libs.test.konsist) testImplementation(libs.test.truth) From ecbd7c7b41c4a18930c997edc6601dbd0a8d363d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:56:35 +0100 Subject: [PATCH 011/102] Short notation --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9baaa81ef..4bf386ee87 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,7 +65,7 @@ firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdi # AndroidX androidx_core = { module = "androidx.core:core", version.ref = "core" } androidx_corektx = { module = "androidx.core:core-ktx", version.ref = "core" } -androidx_annotationjvm = { module = "androidx.annotation:annotation-jvm", version = "1.7.0" } +androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.7.0" androidx_datastore_preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx_datastore_datastore = { module = "androidx.datastore:datastore", version.ref = "datastore" } androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.6" From 485e6bde809e356714c6a5087c0b873ccd584e78 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 10 Nov 2023 16:59:36 +0100 Subject: [PATCH 012/102] Remove unused entries from the catalog. --- gradle/libs.versions.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4bf386ee87..225c793e95 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -122,13 +122,9 @@ test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" } test_arch_core = "androidx.arch.core:core-testing:2.2.0" test_junit = "junit:junit:4.13.2" test_runner = "androidx.test:runner:1.5.2" -test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0" test_junitext = "androidx.test.ext:junit:1.1.5" test_mockk = "io.mockk:mockk:1.13.8" -test_barista = "com.adevinta.android:barista:4.3.0" test_konsist = "com.lemonappdev:konsist:0.13.0" -test_hamcrest = "org.hamcrest:hamcrest:2.2" -test_orchestrator = "androidx.test:orchestrator:1.4.2" test_turbine = "app.cash.turbine:turbine:1.0.0" test_truth = "com.google.truth:truth:1.1.5" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.14" From 5850f5342dd092bd8849c533e106767a30e9f65f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 13 Nov 2023 09:58:00 +0100 Subject: [PATCH 013/102] Move Knit to Gradle catalog and use alias(libs.plugins.kotlin.serialization). Fix issue with JNA --- app/build.gradle.kts | 2 +- features/login/impl/build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- libraries/matrix/api/build.gradle.kts | 2 +- libraries/matrix/impl/build.gradle.kts | 4 ++-- libraries/push/impl/build.gradle.kts | 2 +- libraries/pushproviders/unifiedpush/build.gradle.kts | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c4ff2bdaf9..69a1877455 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -28,7 +28,7 @@ plugins { alias(libs.plugins.ksp) alias(libs.plugins.kapt) alias(libs.plugins.firebaseAppDistribution) - id("org.jetbrains.kotlinx.knit") version "0.4.0" + alias(libs.plugins.knit) id("kotlin-parcelize") // To be able to update the firebase.xml files, uncomment and build the project // id("com.google.gms.google-services") diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 3968ef8ab2..227c512043 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -19,7 +19,7 @@ plugins { alias(libs.plugins.anvil) alias(libs.plugins.ksp) id("kotlin-parcelize") - kotlin("plugin.serialization") version libs.versions.kotlin + alias(libs.plugins.kotlin.serialization) } android { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 225c793e95..3f9ede419f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -162,7 +162,6 @@ maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" kotlinpoet = "com.squareup:kotlinpoet:1.14.2" -jna = "net.java.dev.jna:jna:5.13.0@aar" # Analytics posthog = "com.posthog.android:posthog:2.0.3" @@ -213,6 +212,7 @@ paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } firebaseAppDistribution = { id = "com.google.firebase.appdistribution", version.ref = "firebaseAppDistribution" } +knit = { id = "org.jetbrains.kotlinx.knit", version = "0.4.0" } # Version '4.3.1.3277' introduced some regressions in CI time (more than 2x slower), so make sure # this is no longer the case before upgrading. diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index f1fec9b60b..f8a600df1e 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -18,7 +18,7 @@ plugins { id("io.element.android-compose-library") id("kotlin-parcelize") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version libs.versions.kotlin + alias(libs.plugins.kotlin.serialization) } android { diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 026b06f765..81b5f70a11 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -17,7 +17,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version libs.versions.kotlin + alias(libs.plugins.kotlin.serialization) } android { @@ -44,7 +44,7 @@ dependencies { api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) - implementation(libs.jna) + implementation("net.java.dev.jna:jna:5.13.0@aar") implementation(libs.androidx.datastore.preferences) implementation(libs.serialization.json) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 65e4c0939f..05f552dd9a 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -16,7 +16,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version libs.versions.kotlin + alias(libs.plugins.kotlin.serialization) } android { diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index dfc999c897..733309e00d 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -16,7 +16,7 @@ plugins { id("io.element.android-library") alias(libs.plugins.anvil) - kotlin("plugin.serialization") version libs.versions.kotlin + alias(libs.plugins.kotlin.serialization) } android { From 93fc94cb8f26604b33d94cd37a46699a5d4d3a56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:44:26 +0000 Subject: [PATCH 014/102] Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.3.6 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f9ede419f..39033f03e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -137,7 +137,7 @@ coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } -kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5" +kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.6" showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } showkase_processor = { module = "com.airbnb.android:showkase-processor", version.ref = "showkase" } jsoup = "org.jsoup:jsoup:1.16.2" From 3bf34eae0158397ab7ace19e5ed29683b4969653 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 13 Nov 2023 11:31:34 +0000 Subject: [PATCH 015/102] Sync Strings (#1789) Co-authored-by: bmarty --- .../impl/src/main/res/values-ru/translations.xml | 4 ++-- .../impl/src/main/res/values-ru/translations.xml | 4 ++-- .../impl/src/main/res/values-cs/translations.xml | 2 +- .../impl/src/main/res/values-de/translations.xml | 3 --- .../impl/src/main/res/values-es/translations.xml | 3 --- .../impl/src/main/res/values-fr/translations.xml | 2 +- .../impl/src/main/res/values-it/translations.xml | 3 --- .../impl/src/main/res/values-ro/translations.xml | 3 --- .../impl/src/main/res/values-ru/translations.xml | 2 +- .../impl/src/main/res/values-sk/translations.xml | 2 +- .../src/main/res/values-zh-rTW/translations.xml | 3 --- .../logout/impl/src/main/res/values/localazy.xml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 4 ++-- .../impl/src/main/res/values-ru/translations.xml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 4 +++- .../impl/src/main/res/values-sk/translations.xml | 2 ++ .../impl/src/main/res/values-cs/translations.xml | 1 + .../impl/src/main/res/values-ru/translations.xml | 3 ++- .../impl/src/main/res/values-sk/translations.xml | 1 + .../impl/src/main/res/values-ru/translations.xml | 4 ++++ .../impl/src/main/res/values-sk/translations.xml | 4 ++++ .../push/impl/src/main/res/values/localazy.xml | 4 ++++ .../src/main/res/values-cs/translations.xml | 2 ++ .../src/main/res/values-ru/translations.xml | 14 ++++++++------ .../src/main/res/values-sk/translations.xml | 6 +++++- .../ui-strings/src/main/res/values/localazy.xml | 4 +++- 27 files changed, 52 insertions(+), 38 deletions(-) diff --git a/features/lockscreen/impl/src/main/res/values-ru/translations.xml b/features/lockscreen/impl/src/main/res/values-ru/translations.xml index 1391e5f1a1..7b2c61176e 100644 --- a/features/lockscreen/impl/src/main/res/values-ru/translations.xml +++ b/features/lockscreen/impl/src/main/res/values-ru/translations.xml @@ -24,14 +24,14 @@ "Сэкономьте время и используйте %1$s для разблокировки приложения" "Выберите PIN-код" "Подтвердите PIN-код" - "Из соображений безопасности вы не можешь выбрать это в качестве PIN-кода." + "Из соображений безопасности вы не можешь выбрать это в качестве PIN-кода" "Выберите другой PIN-код" "Заблокируйте %1$s, чтобы повысить безопасность ваших чатов. Выберите что-нибудь незабываемое. Если вы забудете этот PIN-код, вы выйдете из приложения." "Повторите PIN-код" "PIN-коды не совпадают" - "Чтобы продолжить, вам необходимо повторно войти в систему и создать новый PIN-код." + "Чтобы продолжить, вам необходимо повторно войти в систему и создать новый PIN-код" "Вы выходите из системы" "Использовать биометрию" "Использовать PIN-код" diff --git a/features/login/impl/src/main/res/values-ru/translations.xml b/features/login/impl/src/main/res/values-ru/translations.xml index 1751364597..0917813950 100644 --- a/features/login/impl/src/main/res/values-ru/translations.xml +++ b/features/login/impl/src/main/res/values-ru/translations.xml @@ -35,8 +35,8 @@ "В настоящее время существует высокий спрос на %1$s на %2$s. Вернитесь в приложение через несколько дней и попробуйте снова. Спасибо за терпение!" - "Почти готово!" - "Вы зарегистрированы!" + "Почти готово." + "Вы зарегистрированы." "Matrix — это открытая сеть для безопасной децентрализованной связи." "Добро пожаловать в %1$s!" diff --git a/features/logout/impl/src/main/res/values-cs/translations.xml b/features/logout/impl/src/main/res/values-cs/translations.xml index ac228a305f..f772ee92a4 100644 --- a/features/logout/impl/src/main/res/values-cs/translations.xml +++ b/features/logout/impl/src/main/res/values-cs/translations.xml @@ -1,7 +1,6 @@ "Opravdu se chcete odhlásit?" - "Odhlásit se" "Odhlašování…" "Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, ztratíte přístup ke svým šifrovaným zprávám." "Vypnuli jste zálohování" @@ -14,5 +13,6 @@ "Chystáte se odhlásit z poslední relace. Pokud se nyní odhlásíte, můžete ztratit přístup k šifrovaným zprávám." "Uložili jste si klíč pro obnovení?" "Odhlásit se" + "Odhlásit se" "Odhlásit se" diff --git a/features/logout/impl/src/main/res/values-de/translations.xml b/features/logout/impl/src/main/res/values-de/translations.xml index f1eb199d95..2b86bd3a3d 100644 --- a/features/logout/impl/src/main/res/values-de/translations.xml +++ b/features/logout/impl/src/main/res/values-de/translations.xml @@ -1,8 +1,5 @@ "Bist du sicher, dass du dich abmelden willst?" - "Abmelden" "Abmelden…" - "Abmelden" - "Abmelden" diff --git a/features/logout/impl/src/main/res/values-es/translations.xml b/features/logout/impl/src/main/res/values-es/translations.xml index 5ac0656935..84f08fac3c 100644 --- a/features/logout/impl/src/main/res/values-es/translations.xml +++ b/features/logout/impl/src/main/res/values-es/translations.xml @@ -1,8 +1,5 @@ "¿Estás seguro de que quieres cerrar sesión?" - "Cerrar sesión" "Cerrando sesión…" - "Cerrar sesión" - "Cerrar sesión" diff --git a/features/logout/impl/src/main/res/values-fr/translations.xml b/features/logout/impl/src/main/res/values-fr/translations.xml index c309c3166f..0282e3199c 100644 --- a/features/logout/impl/src/main/res/values-fr/translations.xml +++ b/features/logout/impl/src/main/res/values-fr/translations.xml @@ -1,7 +1,6 @@ "Êtes-vous sûr de vouloir vous déconnecter ?" - "Se déconnecter" "Déconnexion…" "Vous êtes en train de vous déconnecter de votre dernière session. Si vous vous déconnectez maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées." "Vous avez désactivé la sauvegarde" @@ -14,5 +13,6 @@ "Vous êtes sur le point de vous déconnecter de votre dernière session. Si vous le faites maintenant, vous perdrez l’accès à l’historique de vos discussions chiffrées." "Avez-vous sauvegardé votre clé de récupération?" "Se déconnecter" + "Se déconnecter" "Se déconnecter" diff --git a/features/logout/impl/src/main/res/values-it/translations.xml b/features/logout/impl/src/main/res/values-it/translations.xml index 4e8217a7f2..73c1fbd3a7 100644 --- a/features/logout/impl/src/main/res/values-it/translations.xml +++ b/features/logout/impl/src/main/res/values-it/translations.xml @@ -1,8 +1,5 @@ "Sei sicuro di voler uscire?" - "Esci" "Uscita in corso…" - "Esci" - "Esci" diff --git a/features/logout/impl/src/main/res/values-ro/translations.xml b/features/logout/impl/src/main/res/values-ro/translations.xml index 4b2c7fbe7b..f6e47327c1 100644 --- a/features/logout/impl/src/main/res/values-ro/translations.xml +++ b/features/logout/impl/src/main/res/values-ro/translations.xml @@ -1,8 +1,5 @@ "Sunteți sigur că vreți să vă deconectați?" - "Deconectați-vă" "Deconectare în curs…" - "Deconectați-vă" - "Deconectați-vă" diff --git a/features/logout/impl/src/main/res/values-ru/translations.xml b/features/logout/impl/src/main/res/values-ru/translations.xml index ab2e3ba617..14f195fef6 100644 --- a/features/logout/impl/src/main/res/values-ru/translations.xml +++ b/features/logout/impl/src/main/res/values-ru/translations.xml @@ -1,7 +1,6 @@ "Вы уверены, что вы хотите выйти?" - "Выйти" "Выполняется выход…" "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы потеряете доступ к зашифрованным сообщениям." "Вы отключили резервное копирование" @@ -14,5 +13,6 @@ "Вы собираетесь выйти из последнего сеанса. Если вы выйдете из системы сейчас, вы можете потерять доступ к зашифрованным сообщениям." "Вы сохранили свой ключ восстановления?" "Выйти" + "Выйти" "Выйти" diff --git a/features/logout/impl/src/main/res/values-sk/translations.xml b/features/logout/impl/src/main/res/values-sk/translations.xml index fcd78f2022..a66630b7ff 100644 --- a/features/logout/impl/src/main/res/values-sk/translations.xml +++ b/features/logout/impl/src/main/res/values-sk/translations.xml @@ -1,7 +1,6 @@ "Ste si istí, že sa chcete odhlásiť?" - "Odhlásiť sa" "Prebieha odhlasovanie…" "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, stratíte prístup k svojim šifrovaným správam." "Vypli ste zálohovanie" @@ -14,5 +13,6 @@ "Chystáte sa odhlásiť z vašej poslednej relácie. Ak sa teraz odhlásite, môžete stratiť prístup k svojim šifrovaným správam." "Uložili ste si kľúč na obnovenie?" "Odhlásiť sa" + "Odhlásiť sa" "Odhlásiť sa" diff --git a/features/logout/impl/src/main/res/values-zh-rTW/translations.xml b/features/logout/impl/src/main/res/values-zh-rTW/translations.xml index df722a9467..a4ca1b030d 100644 --- a/features/logout/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/logout/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,8 +1,5 @@ "您確定要登出嗎?" - "登出" "正在登出…" - "登出" - "登出" diff --git a/features/logout/impl/src/main/res/values/localazy.xml b/features/logout/impl/src/main/res/values/localazy.xml index 9296381c87..55a7131cb9 100644 --- a/features/logout/impl/src/main/res/values/localazy.xml +++ b/features/logout/impl/src/main/res/values/localazy.xml @@ -1,7 +1,6 @@ "Are you sure you want to sign out?" - "Sign out" "Signing out…" "You are about to sign out of your last session. If you sign out now, you will lose access to your encrypted messages." "You have turned off backup" @@ -14,5 +13,6 @@ "You are about to sign out of your last session. If you sign out now, you might lose access to your encrypted messages." "Have you saved your recovery key?" "Sign out" + "Sign out" "Sign out" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 0fd2ddfe4d..c3af66d8ad 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -14,7 +14,7 @@ "Местоположение" "Опрос" "Форматирование текста" - "В настоящее время история сообщений недоступна в этой комнате" + "В настоящее время история сообщений недоступна в этой комнате." "История сообщений в этой комнате недоступна. Проверьте это устройство, чтобы увидеть историю сообщений." "Не удалось получить данные о пользователе" "Хотите пригласить их снова?" @@ -25,7 +25,7 @@ "Включение этого параметра отменяет настройки по умолчанию" "Уведомить меня в этом чате" "Вы можете изменить его в своем %1$s." - "Основные Настройки" + "основные настройки" "Настройка по умолчанию" "Удалить пользовательскую настройку" "Произошла ошибка при загрузке настроек уведомлений." diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index ed2d9b4d54..a1fd3f3bc6 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -1,7 +1,7 @@ "Базовый URL сервера звонков Element" - "Задайте свой сервер звонков Element" + "Задайте свой сервер Element Call." "Адрес указан неверно, удостоверьтесь, что вы указали протокол (http/https) и правильный адрес." "Режим разработчика" "Предоставьте разработчикам доступ к функциям и функциональным возможностям." diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 920fb1a2f8..d8e15924cc 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -29,7 +29,7 @@ "Включение этого параметра отменяет настройки по умолчанию" "Уведомить меня в этом чате" "Вы можете изменить его в своем %1$s." - "Основные Настройки" + "основные настройки" "Настройка по умолчанию" "Удалить пользовательскую настройку" "Произошла ошибка при загрузке настроек уведомлений." diff --git a/features/securebackup/impl/src/main/res/values-ru/translations.xml b/features/securebackup/impl/src/main/res/values-ru/translations.xml index 34a117c53b..fd2bb28208 100644 --- a/features/securebackup/impl/src/main/res/values-ru/translations.xml +++ b/features/securebackup/impl/src/main/res/values-ru/translations.xml @@ -22,7 +22,9 @@ "Ключ восстановления изменен" "Изменить ключ восстановления?" "Введите ключ восстановления, чтобы подтвердить доступ к резервной копии чата." - "Введите 48-значный код." + "Пожалуйста, попробуйте еще раз, чтобы подтвердить доступ к резервной копии чата." + "Неверный ключ восстановления" + "Введите 48 значный код." "Вход…" "Ключ восстановления подтвержден" "Подтвердите ключ восстановления" diff --git a/features/securebackup/impl/src/main/res/values-sk/translations.xml b/features/securebackup/impl/src/main/res/values-sk/translations.xml index 7c702d0be2..ffc7e78c87 100644 --- a/features/securebackup/impl/src/main/res/values-sk/translations.xml +++ b/features/securebackup/impl/src/main/res/values-sk/translations.xml @@ -22,6 +22,8 @@ "Kľúč na obnovenie bol zmenený" "Zmeniť kľúč na obnovenie?" "Zadajte kľúč na obnovenie a potvrďte prístup k zálohe konverzácie." + "Skúste prosím znova potvrdiť prístup k vašej zálohe konverzácie." + "Nesprávny kľúč na obnovenie" "Zadajte 48-znakový kód." "Zadať…" "Kľúč na obnovu potvrdený" diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml index 83e2c9316f..60e9cf17ce 100644 --- a/features/verifysession/impl/src/main/res/values-cs/translations.xml +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -9,6 +9,7 @@ "Opakovat ověření" "Jsem připraven" "Čekání na shodu" + "Porovnejte jedinečnou sadu emotikonů." "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí." "Neshodují se" "Shodují se" diff --git a/features/verifysession/impl/src/main/res/values-ru/translations.xml b/features/verifysession/impl/src/main/res/values-ru/translations.xml index 2de79c6ac2..564f839591 100644 --- a/features/verifysession/impl/src/main/res/values-ru/translations.xml +++ b/features/verifysession/impl/src/main/res/values-ru/translations.xml @@ -2,13 +2,14 @@ "Кажется, что-то не так. Время ожидания запроса истекло, либо запрос был отклонен." "Убедитесь, что приведенные ниже смайлики совпадают со смайликами, показанными во время другого сеанса." - "Сравните смайлики" + "Сравните емодзи" "Ваш новый сеанс подтвержден. У него есть доступ к вашим зашифрованным сообщениям, и другие пользователи увидят его как доверенное." "Чтобы получить доступ к зашифрованной истории сообщений, докажите, что это вы." "Открыть существующий сеанс" "Повторить проверку" "Я готов" "Ожидание соответствия" + "Сравните уникальный набор эмодзи." "Сравните уникальные смайлики, убедившись, что они расположены в том же порядке." "Они не совпадают" "Они совпадают" diff --git a/features/verifysession/impl/src/main/res/values-sk/translations.xml b/features/verifysession/impl/src/main/res/values-sk/translations.xml index c00a995266..4b13701466 100644 --- a/features/verifysession/impl/src/main/res/values-sk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-sk/translations.xml @@ -9,6 +9,7 @@ "Zopakovať overenie" "Som pripravený/á" "Čaká sa na zhodu" + "Porovnajte jedinečnú sadu emotikonov." "Porovnajte jedinečné emotikony a uistite sa, že sú zobrazené v rovnakom poradí." "Nezhodujú sa" "Zhodujú sa" diff --git a/libraries/push/impl/src/main/res/values-ru/translations.xml b/libraries/push/impl/src/main/res/values-ru/translations.xml index 697a0f01d8..aec7180374 100644 --- a/libraries/push/impl/src/main/res/values-ru/translations.xml +++ b/libraries/push/impl/src/main/res/values-ru/translations.xml @@ -8,6 +8,10 @@ "Присоединиться" "Отклонить" "Пригласил вас в чат" + "%1$s упомянул вас. +%2$s" + "Вас уже упомянули. +%1$s" "Новые сообщения" "Отреагировал на %1$s" "Отметить как прочитанное" diff --git a/libraries/push/impl/src/main/res/values-sk/translations.xml b/libraries/push/impl/src/main/res/values-sk/translations.xml index 16eb94685d..0bc3338204 100644 --- a/libraries/push/impl/src/main/res/values-sk/translations.xml +++ b/libraries/push/impl/src/main/res/values-sk/translations.xml @@ -8,6 +8,10 @@ "Pripojiť sa" "Zamietnuť" "Vás pozval/a na konverzáciu" + "%1$s vás spomenul/-a. +%2$s" + "Boli ste spomenutí. +%1$s" "Nové správy" "Reagoval/a s %1$s" "Označiť ako prečítané" diff --git a/libraries/push/impl/src/main/res/values/localazy.xml b/libraries/push/impl/src/main/res/values/localazy.xml index 987728304a..5cadfb90ac 100644 --- a/libraries/push/impl/src/main/res/values/localazy.xml +++ b/libraries/push/impl/src/main/res/values/localazy.xml @@ -8,6 +8,10 @@ "Join" "Reject" "Invited you to chat" + "%1$s mentioned you. +%2$s" + "You have been mentioned. +%1$s" "New Messages" "Reacted with %1$s" "Mark as read" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 55e1f01e06..a1d01dc467 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -14,6 +14,7 @@ "Zahájit hovor" "Uživatelské menu" "Nahrajte hlasovou zprávu. Dvojitě klepněte a podržte pro záznam. Uvolněním ukončíte nahrávání." + "Zastavit nahrávání" "Přijmout" "Přidat na časovou osu" "Zpět" @@ -172,6 +173,7 @@ "Hlasová zpráva" "Čekání…" "Čekání na dešifrovací klíč" + "Nahlásit problém" "Opravdu chcete ukončit toto hlasování?" "Hlasování: %1$s" "Potvrzení" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index ddac747bcd..7f94e7e05b 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -13,7 +13,8 @@ "Показать пароль" "Начать звонок" "Меню пользователя" - "Запишите голосовое сообщение. Дважды нажмите и удерживайте, чтобы записать. Отпустите, чтобы закончить запись." + "Записать голосовое сообщение." + "Остановить запись" "Разрешить" "Добавить в хронологию" "Назад" @@ -76,7 +77,7 @@ "Все равно выйти" "Пропустить" "Начать" - "Начать чат " + "Начать чат" "Начать подтверждение" "Нажмите, чтобы загрузить карту" "Сделать фото" @@ -115,6 +116,7 @@ "Ссылка скопирована в буфер обмена" "Загрузка…" "Сообщение" + "Действия с сообщением" "Оформление сообщений" "Сообщение удалено" "Современный" @@ -172,8 +174,10 @@ "Голосовое сообщение" "Ожидание…" "Ожидание ключа расшифровки" + "Сообщить о проблеме" "Вы действительно хотите завершить данный опрос?" "Опрос: %1$s" + "Подтверждение устройства" "Подтверждение" "Предупреждение" "Деятельность" @@ -228,9 +232,7 @@ "Дополнительные параметры" "Аудио и видео звонки" "Несоответствие конфигурации" - "Мы упростили настройки уведомлений, чтобы упростить поиск опций. - -Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. + "Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. Если вы продолжите, некоторые настройки могут быть изменены." "Прямые чаты" @@ -249,7 +251,7 @@ "Уведомить меня" "Уведомить меня в @room" "Чтобы получать уведомления, измените свой %1$s." - "Настройки системы" + "настройки системы" "Системные уведомления выключены" "Уведомления" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index b9cabf3cf8..5aee90c0c3 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -13,7 +13,8 @@ "Zobraziť heslo" "Začať hovor" "Používateľské menu" - "Nahrávanie hlasovej správy. Nahrávanie spustíte dvojitým ťuknutím a podržaním. Uvoľnením nahrávanie ukončíte." + "Nahrať hlasovú správu." + "Zastaviť nahrávanie" "Prijať" "Pridať na časovú os" "Späť" @@ -115,6 +116,7 @@ "Odkaz bol skopírovaný do schránky" "Načítava sa…" "Správa" + "Akcie správy" "Štýl správ" "Správa odstránená" "Moderné" @@ -172,8 +174,10 @@ "Hlasová správa" "Čaká sa…" "Čaká sa na dešifrovací kľúč" + "Nahlásiť problém" "Ste si istí, že chcete ukončiť túto anketu?" "Anketa: %1$s" + "Overiť zariadenie" "Potvrdenie" "Upozornenie" "Aktivity" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 0aaeeb2b7b..ec09f81b3d 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -13,7 +13,8 @@ "Show password" "Start a call" "User menu" - "Record voice message. Double tap and hold to record. Release to end recording." + "Record voice message." + "Stop recording" "Accept" "Add to timeline" "Back" @@ -115,6 +116,7 @@ "Link copied to clipboard" "Loading…" "Message" + "Message actions" "Message layout" "Message removed" "Modern" From 58e05d139c45472a3daf23fe817e97f09274de76 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Mon, 13 Nov 2023 15:52:53 +0100 Subject: [PATCH 016/102] Always ensure media temp dir exists (#1790) ## Type of change - [ ] Feature - [x] Bugfix - [ ] Technical - [ ] Other : ## Content `RustMediaLoader` creates the "${cacheDir}/temp/media" dir only once at class creation. Unfortunately when clearing an app's cache this directory will be deleted and the app's process won't be killed, so subsequent usages of the same instance of `RustMediaLoader` will not work because `cacheDirectory` does not exists. This fix makes sure that such directory is always checked and created if needed. ## Motivation and context Fixes https://github.com/vector-im/element-x-android/issues/1788 ## Screenshots / GIFs ## Tests - Step 1 - Step 2 - Step ... ## Tested devices - [ ] Physical - [ ] Emulator - OS version(s): ## Checklist - [ ] Changes have been tested on an Android device or Android emulator with API 23 - [ ] UI change has been tested on both light and dark themes - [ ] Accessibility has been taken into account. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#accessibility - [ ] Pull request is based on the develop branch - [ ] Pull request includes a new file under ./changelog.d. See https://github.com/vector-im/element-x-android/blob/develop/CONTRIBUTING.md#changelog - [ ] Pull request includes screenshots or videos if containing UI changes - [ ] Pull request includes a [sign off](https://matrix-org.github.io/synapse/latest/development/contributing_guide.html#sign-off) - [ ] You've made a self review of your PR --- changelog.d/1790.bugfix | 1 + .../features/rageshake/impl/logs/VectorFileLogger.kt | 10 ++++------ .../libraries/matrix/impl/media/RustMediaLoader.kt | 9 ++++----- 3 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 changelog.d/1790.bugfix diff --git a/changelog.d/1790.bugfix b/changelog.d/1790.bugfix new file mode 100644 index 0000000000..db8ea93406 --- /dev/null +++ b/changelog.d/1790.bugfix @@ -0,0 +1 @@ +Always ensure media temp dir exists diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt index 6b5d7c9096..e848082e91 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/logs/VectorFileLogger.kt @@ -37,7 +37,7 @@ import java.util.logging.Logger * Will be planted in Timber. */ class VectorFileLogger( - context: Context, + private val context: Context, // private val vectorPreferences: VectorPreferences private val dispatcher: CoroutineDispatcher = Dispatchers.IO, ) : Timber.Tree() { @@ -66,7 +66,9 @@ class VectorFileLogger( } private val fileHandler: FileHandler? - private val cacheDirectory = File(context.cacheDir, "logs") + private val cacheDirectory get() = File(context.cacheDir, "logs").apply { + if (!exists()) mkdirs() + } private var fileNamePrefix = "logs" private val prioPrefixes = mapOf( @@ -79,10 +81,6 @@ class VectorFileLogger( ) init { - if (!cacheDirectory.exists()) { - cacheDirectory.mkdirs() - } - for (i in 0..15) { val file = File(cacheDirectory, "elementLogs.${i}.txt") file.safeDelete() diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt index 12a273f03c..fab0146f9d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaLoader.kt @@ -30,18 +30,17 @@ import java.io.File import org.matrix.rustcomponents.sdk.MediaSource as RustMediaSource class RustMediaLoader( - baseCacheDirectory: File, + private val baseCacheDirectory: File, dispatchers: CoroutineDispatchers, private val innerClient: Client, ) : MatrixMediaLoader { @OptIn(ExperimentalCoroutinesApi::class) private val mediaDispatcher = dispatchers.io.limitedParallelism(32) - private val cacheDirectory = File(baseCacheDirectory, "temp/media").apply { - if (!exists()) { - mkdirs() + private val cacheDirectory + get() = File(baseCacheDirectory, "temp/media").apply { + if (!exists()) mkdirs() // Must always ensure that this directory exists because "Clear cache" does not restart an app's process. } - } @OptIn(ExperimentalUnsignedTypes::class) override suspend fun loadMediaContent(source: MediaSource): Result = From d648f85d752ed84228a5a733c6cd131e6e5dd859 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 13 Nov 2023 16:35:11 +0100 Subject: [PATCH 017/102] Add active call icon to room list summaries (#1792) Co-authored-by: ElementBot --- changelog.d/1158.feature | 1 + .../roomlist/impl/components/RoomSummaryRow.kt | 14 +++++++++++--- .../roomlist/impl/datasource/RoomListDataSource.kt | 1 + .../roomlist/impl/model/RoomListRoomSummary.kt | 1 + .../impl/model/RoomListRoomSummaryProvider.kt | 1 + .../libraries/matrix/api/roomlist/RoomSummary.kt | 1 + .../impl/roomlist/RoomSummaryDetailsFactory.kt | 1 + ...omSummaryRow-Day-8_8_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...omSummaryRow-Day-8_8_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...omSummaryRow-Day-8_8_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...omSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png | 3 +++ ...SummaryRow-Night-8_9_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...SummaryRow-Night-8_9_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...SummaryRow-Night-8_9_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...SummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png | 3 +++ ...hResultContent-Day-9_9_null,NEXUS_5,1.0,en].png | 4 ++-- ...sultContent-Night-9_10_null,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...omListView-Night-2_3_null_9,NEXUS_5,1.0,en].png | 4 ++-- 33 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 changelog.d/1158.feature create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Day-8_8_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl.components_RoomSummaryRow_null_RoomSummaryRow-Night-8_9_null_8,NEXUS_5,1.0,en].png diff --git a/changelog.d/1158.feature b/changelog.d/1158.feature new file mode 100644 index 0000000000..a3ce14a877 --- /dev/null +++ b/changelog.d/1158.feature @@ -0,0 +1 @@ +Add ongoing call indicator to rooms lists items. diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index 2016cf5a6b..a437dd339c 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -172,14 +172,22 @@ private fun RowScope.LastMessageAndIndicatorRow(room: RoomListRoomSummary) { // Unread Row( + modifier = Modifier.height(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { + // Video call + if (room.hasOngoingCall) { + Icon( + modifier = Modifier.size(16.dp), + resourceId = CommonDrawables.ic_compound_video_call, + contentDescription = null, + tint = ElementTheme.colors.unreadIndicator, + ) + } NotificationIcon(room) if (room.hasUnread) { - UnreadIndicatorAtom( - modifier = Modifier.padding(vertical = 3.dp), - ) + UnreadIndicatorAtom() } } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 6a9c152af6..b8af0a60cc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -161,6 +161,7 @@ class RoomListDataSource @Inject constructor( }.orEmpty(), avatarData = avatarData, notificationMode = roomSummary.details.notificationMode, + hasOngoingCall = roomSummary.details.hasOngoingCall, ) } null -> null diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt index bb3405c2d3..3a445e6cdc 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummary.kt @@ -33,4 +33,5 @@ data class RoomListRoomSummary constructor( val avatarData: AvatarData = AvatarData(id, name, size = AvatarSize.RoomListItem), val isPlaceholder: Boolean = false, val notificationMode: RoomNotificationMode? = null, + val hasOngoingCall: Boolean = false, ) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt index f2db8a376c..2649704427 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt @@ -40,6 +40,7 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider Date: Tue, 14 Nov 2023 10:26:16 +0100 Subject: [PATCH 018/102] Move PushConfig to the `appconfig` module. --- .../src/main/kotlin/io/element/android/appconfig}/PushConfig.kt | 2 +- libraries/push/impl/build.gradle.kts | 1 + .../io/element/android/libraries/push/impl/PushersManager.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) rename {libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/config => appconfig/src/main/kotlin/io/element/android/appconfig}/PushConfig.kt (93%) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/config/PushConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/PushConfig.kt similarity index 93% rename from libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/config/PushConfig.kt rename to appconfig/src/main/kotlin/io/element/android/appconfig/PushConfig.kt index 823dd7f693..1836a5acb6 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/config/PushConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/PushConfig.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.libraries.push.impl.config +package io.element.android.appconfig object PushConfig { /** diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 05f552dd9a..c67d805fb6 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(libs.serialization.json) implementation(libs.coil) + implementation(projects.appconfig) implementation(projects.libraries.architecture) implementation(projects.libraries.core) implementation(projects.libraries.designsystem) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt index b49f8f4299..d2650f853d 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.push.impl import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.PushConfig import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope @@ -24,7 +25,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.pusher.SetHttpPusherData -import io.element.android.libraries.push.impl.config.PushConfig import io.element.android.libraries.push.impl.pushgateway.PushGatewayNotifyRequest import io.element.android.libraries.pushproviders.api.PusherSubscriber import io.element.android.libraries.pushstore.api.UserPushStoreFactory From 3628a0359c370306f32c28eda16a5da7a1dcb666 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 10:31:25 +0100 Subject: [PATCH 019/102] Create VoiceMessageConfig in `appconfig` module. --- .../android/appconfig/VoiceMessageConfig.kt | 23 +++++++++++++++++++ libraries/voicerecorder/impl/build.gradle.kts | 1 + .../voicerecorder/impl/VoiceRecorderImpl.kt | 3 ++- .../impl/VoiceRecorderImplTest.kt | 7 +++--- 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 appconfig/src/main/kotlin/io/element/android/appconfig/VoiceMessageConfig.kt diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/VoiceMessageConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/VoiceMessageConfig.kt new file mode 100644 index 0000000000..a840079a5e --- /dev/null +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/VoiceMessageConfig.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 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 io.element.android.appconfig + +import kotlin.time.Duration.Companion.minutes + +object VoiceMessageConfig { + val maxVoiceMessageDuration = 30.minutes +} diff --git a/libraries/voicerecorder/impl/build.gradle.kts b/libraries/voicerecorder/impl/build.gradle.kts index 6ebfb28997..d73f06b868 100644 --- a/libraries/voicerecorder/impl/build.gradle.kts +++ b/libraries/voicerecorder/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { api(libs.opusencoder) implementation(libs.dagger) + implementation(projects.appconfig) implementation(projects.libraries.matrix.api) implementation(projects.libraries.core) implementation(projects.libraries.di) diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt index 042ecacfbe..bc4e19d13e 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.voicerecorder.impl import android.Manifest import androidx.annotation.RequiresPermission import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.appconfig.VoiceMessageConfig import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.di.RoomScope @@ -94,7 +95,7 @@ class VoiceRecorderImpl @Inject constructor( val elapsedTime = startedAt.elapsedNow() - if (elapsedTime > 30.minutes) { + if (elapsedTime > VoiceMessageConfig.maxVoiceMessageDuration) { Timber.w("Voice message time limit reached") stopRecord(false) return@record diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt index e3b2100ccc..d67a44f6f3 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt @@ -20,6 +20,7 @@ import android.media.AudioFormat import android.media.MediaRecorder import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.appconfig.VoiceMessageConfig import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.libraries.voicerecorder.impl.audio.Audio import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig @@ -77,8 +78,8 @@ class VoiceRecorderImplTest { voiceRecorder.startRecord() assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(0.minutes, listOf(1.0f))) - timeSource += 30.minutes - assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(30.minutes, listOf())) + timeSource += VoiceMessageConfig.maxVoiceMessageDuration + assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Recording(VoiceMessageConfig.maxVoiceMessageDuration, listOf())) timeSource += 1.milliseconds assertThat(awaitItem()).isEqualTo( @@ -86,7 +87,7 @@ class VoiceRecorderImplTest { file = File(FILE_PATH), mimeType = "audio/ogg", waveform = List(100) { 1f }, - duration = 30.minutes, + duration = VoiceMessageConfig.maxVoiceMessageDuration, ) ) } From d9f5fdccbc9dbb0f429df36f964453f104b1867f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 10:47:54 +0100 Subject: [PATCH 020/102] Localazy: move some strings to the `:features:analytics:api` module. --- .../api/preferences/AnalyticsPreferencesView.kt | 10 +++++----- .../api/src/main/res/values-cs/translations.xml | 7 +++++++ .../api/src/main/res/values-de/translations.xml | 7 +++++++ .../api/src/main/res/values-fr/translations.xml | 7 +++++++ .../api/src/main/res/values-ro/translations.xml | 7 +++++++ .../api/src/main/res/values-ru/translations.xml | 7 +++++++ .../api/src/main/res/values-sk/translations.xml | 7 +++++++ .../api/src/main/res/values-zh-rTW/translations.xml | 7 +++++++ .../analytics/api/src/main/res/values/localazy.xml | 7 +++++++ .../ui-strings/src/main/res/values-cs/translations.xml | 6 +----- .../ui-strings/src/main/res/values-de/translations.xml | 4 ---- .../ui-strings/src/main/res/values-fr/translations.xml | 4 ---- .../ui-strings/src/main/res/values-ro/translations.xml | 4 ---- .../ui-strings/src/main/res/values-ru/translations.xml | 6 +----- .../ui-strings/src/main/res/values-sk/translations.xml | 6 +----- .../src/main/res/values-zh-rTW/translations.xml | 4 ---- libraries/ui-strings/src/main/res/values/localazy.xml | 6 +----- tools/localazy/config.json | 6 ++++++ 18 files changed, 71 insertions(+), 41 deletions(-) create mode 100644 features/analytics/api/src/main/res/values-cs/translations.xml create mode 100644 features/analytics/api/src/main/res/values-de/translations.xml create mode 100644 features/analytics/api/src/main/res/values-fr/translations.xml create mode 100644 features/analytics/api/src/main/res/values-ro/translations.xml create mode 100644 features/analytics/api/src/main/res/values-ru/translations.xml create mode 100644 features/analytics/api/src/main/res/values-sk/translations.xml create mode 100644 features/analytics/api/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/analytics/api/src/main/res/values/localazy.xml diff --git a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt index 5052f0927f..9678068a3c 100644 --- a/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt +++ b/features/analytics/api/src/main/kotlin/io/element/android/features/analytics/api/preferences/AnalyticsPreferencesView.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.analytics.api.AnalyticsOptInEvents +import io.element.android.features.analytics.api.R import io.element.android.libraries.designsystem.components.LINK_TAG import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -30,7 +31,6 @@ import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithSt import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ListSupportingText import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.ui.strings.CommonStrings @Composable fun AnalyticsPreferencesView( @@ -42,18 +42,18 @@ fun AnalyticsPreferencesView( } val supportingText = stringResource( - id = CommonStrings.screen_analytics_settings_help_us_improve, + id = R.string.screen_analytics_settings_help_us_improve, state.applicationName ) val linkText = buildAnnotatedStringWithStyledPart( - CommonStrings.screen_analytics_settings_read_terms, - CommonStrings.screen_analytics_settings_read_terms_content_link, + R.string.screen_analytics_settings_read_terms, + R.string.screen_analytics_settings_read_terms_content_link, tagAndLink = LINK_TAG to state.policyUrl, ) Column(modifier) { ListItem( headlineContent = { - Text(stringResource(id = CommonStrings.screen_analytics_settings_share_data)) + Text(stringResource(id = R.string.screen_analytics_settings_share_data)) }, supportingContent = { Text(supportingText) diff --git a/features/analytics/api/src/main/res/values-cs/translations.xml b/features/analytics/api/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..a60d94b797 --- /dev/null +++ b/features/analytics/api/src/main/res/values-cs/translations.xml @@ -0,0 +1,7 @@ + + + "Sdílet analytická data" + "Sdílejte anonymní údaje o používání, které nám pomohou identifikovat problémy." + "Můžete si přečíst všechny naše podmínky %1$s." + "zde" + diff --git a/features/analytics/api/src/main/res/values-de/translations.xml b/features/analytics/api/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..135eac3d21 --- /dev/null +++ b/features/analytics/api/src/main/res/values-de/translations.xml @@ -0,0 +1,7 @@ + + + "Analysedaten teilen" + "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen." + "Du kannst alle unsere Bedingungen lesen %1$s." + "hier" + diff --git a/features/analytics/api/src/main/res/values-fr/translations.xml b/features/analytics/api/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..aa0fe9a14f --- /dev/null +++ b/features/analytics/api/src/main/res/values-fr/translations.xml @@ -0,0 +1,7 @@ + + + "Partagez des données de statistiques d’utilisation" + "Partagez des données d’utilisation anonymes pour nous aider à identifier les problèmes." + "Vous pouvez lire toutes nos conditions %1$s." + "ici" + diff --git a/features/analytics/api/src/main/res/values-ro/translations.xml b/features/analytics/api/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..e03a6cfb36 --- /dev/null +++ b/features/analytics/api/src/main/res/values-ro/translations.xml @@ -0,0 +1,7 @@ + + + "Partajați datele analitice" + "Distribuiți date anonime de utilizare pentru a ne ajuta să identificăm probleme." + "Puteți citi toate condițiile noastre %1$s." + "aici" + diff --git a/features/analytics/api/src/main/res/values-ru/translations.xml b/features/analytics/api/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..0617f21bbf --- /dev/null +++ b/features/analytics/api/src/main/res/values-ru/translations.xml @@ -0,0 +1,7 @@ + + + "Делитесь данными аналитики" + "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." + "Вы можете ознакомиться со всеми нашими условиями %1$s." + "здесь" + diff --git a/features/analytics/api/src/main/res/values-sk/translations.xml b/features/analytics/api/src/main/res/values-sk/translations.xml new file mode 100644 index 0000000000..a930b315ee --- /dev/null +++ b/features/analytics/api/src/main/res/values-sk/translations.xml @@ -0,0 +1,7 @@ + + + "Zdieľať analytické údaje" + "Zdieľajte anonymné údaje o používaní, aby sme mohli identifikovať problémy." + "Môžete si prečítať všetky naše podmienky %1$s." + "tu" + diff --git a/features/analytics/api/src/main/res/values-zh-rTW/translations.xml b/features/analytics/api/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..db8f91ceea --- /dev/null +++ b/features/analytics/api/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,7 @@ + + + "分享分析數據" + "分享匿名的使用數據以協助我們釐清問題。" + "您可以到%1$s閱讀我們的條款。" + "這裡" + diff --git a/features/analytics/api/src/main/res/values/localazy.xml b/features/analytics/api/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..8ae2a2d3d8 --- /dev/null +++ b/features/analytics/api/src/main/res/values/localazy.xml @@ -0,0 +1,7 @@ + + + "Share analytics data" + "Share anonymous usage data to help us identify issues." + "You can read all our terms %1$s." + "here" + diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index a1d01dc467..59a52044da 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -135,6 +135,7 @@ "Obnovování…" "Odpověď na %1$s" "Nahlásit chybu" + "Nahlásit problém" "Zpráva odeslána" "Editor formátovaného textu" "Místnost" @@ -173,7 +174,6 @@ "Hlasová zpráva" "Čekání…" "Čekání na dešifrovací klíč" - "Nahlásit problém" "Opravdu chcete ukončit toto hlasování?" "Hlasování: %1$s" "Potvrzení" @@ -223,7 +223,6 @@ "Toto je začátek %1$s." "Toto je začátek této konverzace." "Nové" - "Sdílet analytická data" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." @@ -268,8 +267,5 @@ Pokud budete pokračovat, některá nastavení se mohou změnit." "en" "Chyba" "Úspěch" - "Sdílejte anonymní údaje o používání, které nám pomohou identifikovat problémy." - "Můžete si přečíst všechny naše podmínky %1$s." - "zde" "Zablokovat uživatele" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index dd84815254..2983bc0be4 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -191,7 +191,6 @@ "Dies ist der Anfang von %1$s." "Dies ist der Anfang dieses Gesprächs." "Neu" - "Analysedaten teilen" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." @@ -232,8 +231,5 @@ "en" "Fehler" "Erfolg" - "Teile anonyme Nutzungsdaten, um uns bei der Identifizierung von Problemen zu helfen." - "Du kannst alle unsere Bedingungen lesen %1$s." - "hier" "Benutzer sperren" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index f655b1eafa..ed8ff54abf 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -218,7 +218,6 @@ "Ceci est le début de %1$s." "Ceci est le début de cette conversation." "Nouveau" - "Partagez des données de statistiques d’utilisation" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." @@ -263,8 +262,5 @@ Si vous continuez, il est possible que certains de vos paramètres soient modifi "Ang." "Erreur" "Succès" - "Partagez des données d’utilisation anonymes pour nous aider à identifier les problèmes." - "Vous pouvez lire toutes nos conditions %1$s." - "ici" "Bloquer l’utilisateur" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 566133a6a5..00825d232a 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -175,7 +175,6 @@ "Acesta este începutul conversației %1$s." "Acesta este începutul acestei conversații." "Nou" - "Partajați datele analitice" "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Încărcarea fișierelor media a eșuat, încercați din nou." @@ -220,8 +219,5 @@ Dacă continuați, unele dintre setările dumneavoastră pot fi modificate.""ro" "Eroare" "Succes" - "Distribuiți date anonime de utilizare pentru a ne ajuta să identificăm probleme." - "Puteți citi toate condițiile noastre %1$s." - "aici" "Blocați utilizatorul" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 7f94e7e05b..aca6c68a85 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -136,6 +136,7 @@ "Обновление…" "Отвечает на %1$s" "Сообщить об ошибке" + "Сообщить о проблеме" "Отчет отправлен" "Редактор форматированного текста" "Комната" @@ -174,7 +175,6 @@ "Голосовое сообщение" "Ожидание…" "Ожидание ключа расшифровки" - "Сообщить о проблеме" "Вы действительно хотите завершить данный опрос?" "Опрос: %1$s" "Подтверждение устройства" @@ -225,7 +225,6 @@ "Это начало %1$s." "Это начало разговора." "Новый" - "Делитесь данными аналитики" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." @@ -268,8 +267,5 @@ "en" "Ошибка" "Успешно" - "Предоставлять анонимные данные об использовании, чтобы помочь нам выявить проблемы." - "Вы можете ознакомиться со всеми нашими условиями %1$s." - "здесь" "Заблокировать пользователя" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 5aee90c0c3..3f4d241808 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -136,6 +136,7 @@ "Obnovuje sa…" "Odpoveď na %1$s" "Nahlásiť chybu" + "Nahlásiť problém" "Nahlásenie bolo odoslané" "Rozšírený textový editor" "Miestnosť" @@ -174,7 +175,6 @@ "Hlasová správa" "Čaká sa…" "Čaká sa na dešifrovací kľúč" - "Nahlásiť problém" "Ste si istí, že chcete ukončiť túto anketu?" "Anketa: %1$s" "Overiť zariadenie" @@ -225,7 +225,6 @@ "Toto je začiatok %1$s." "Toto je začiatok tejto konverzácie." "Nové" - "Zdieľať analytické údaje" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." @@ -270,8 +269,5 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""sk" "Chyba" "Úspech" - "Zdieľajte anonymné údaje o používaní, aby sme mohli identifikovať problémy." - "Môžete si prečítať všetky naše podmienky %1$s." - "tu" "Zablokovať používateľa" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 37b372f46b..f4037bd603 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -180,7 +180,6 @@ "檢舉這個內容的原因" "新訊息" - "分享分析數據" "無法上傳媒體檔案,請稍後再試。" "其他設定" "私訊" @@ -205,8 +204,5 @@ "zh-tw" "錯誤" "成功" - "分享匿名的使用數據以協助我們釐清問題。" - "您可以到%1$s閱讀我們的條款。" - "這裡" "封鎖使用者" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ec09f81b3d..4f18758bfb 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -136,6 +136,7 @@ "Refreshing…" "Replying to %1$s" "Report a bug" + "Report a problem" "Report submitted" "Rich text editor" "Room" @@ -174,7 +175,6 @@ "Voice message" "Waiting…" "Waiting for this message" - "Report a problem" "Are you sure you want to end this poll?" "Poll: %1$s" "Verify device" @@ -222,7 +222,6 @@ "This is the beginning of %1$s." "This is the beginning of this conversation." "New" - "Share analytics data" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." @@ -266,8 +265,5 @@ If you proceed, some of your settings may change." "en" "Error" "Success" - "Share anonymous usage data to help us identify issues." - "You can read all our terms %1$s." - "here" "Block user" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 4fa301b914..08d5e35456 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -130,6 +130,12 @@ "screen_analytics_prompt.*" ] }, + { + "name": ":features:analytics:api", + "includeRegex": [ + "screen_analytics_settings_.*" + ] + }, { "name": ":features:ftue:impl", "includeRegex": [ From 2ebe7d336549f0f83776a91ee407f217f860078d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 10:53:30 +0100 Subject: [PATCH 021/102] Localazy: move `emoji_picker_category_` strings to the `:features:messages:impl` module. --- .../customreaction/EmojibaseExtensions.kt | 44 +++++++++---------- .../src/main/res/values-cs/translations.xml | 8 ++++ .../src/main/res/values-de/translations.xml | 8 ++++ .../src/main/res/values-es/translations.xml | 8 ++++ .../src/main/res/values-fr/translations.xml | 8 ++++ .../src/main/res/values-it/translations.xml | 8 ++++ .../src/main/res/values-ro/translations.xml | 8 ++++ .../src/main/res/values-ru/translations.xml | 8 ++++ .../src/main/res/values-sk/translations.xml | 8 ++++ .../main/res/values-zh-rTW/translations.xml | 8 ++++ .../impl/src/main/res/values/localazy.xml | 8 ++++ .../src/main/res/values-cs/translations.xml | 8 ---- .../src/main/res/values-de/translations.xml | 8 ---- .../src/main/res/values-es/translations.xml | 8 ---- .../src/main/res/values-fr/translations.xml | 8 ---- .../src/main/res/values-it/translations.xml | 8 ---- .../src/main/res/values-ro/translations.xml | 8 ---- .../src/main/res/values-ru/translations.xml | 8 ---- .../src/main/res/values-sk/translations.xml | 8 ---- .../main/res/values-zh-rTW/translations.xml | 8 ---- .../src/main/res/values/localazy.xml | 8 ---- tools/localazy/config.json | 3 +- 22 files changed, 103 insertions(+), 104 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojibaseExtensions.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojibaseExtensions.kt index fb111cce97..bf1c69e612 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojibaseExtensions.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojibaseExtensions.kt @@ -28,31 +28,29 @@ import androidx.compose.material.icons.outlined.EmojiSymbols import androidx.compose.material.icons.outlined.EmojiTransportation import androidx.compose.ui.graphics.vector.ImageVector import io.element.android.emojibasebindings.EmojibaseCategory -import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.features.messages.impl.R @get:StringRes -val EmojibaseCategory.title: Int get() = - when(this){ - EmojibaseCategory.People -> CommonStrings.emoji_picker_category_people - EmojibaseCategory.Nature -> CommonStrings.emoji_picker_category_nature - EmojibaseCategory.Foods -> CommonStrings.emoji_picker_category_foods - EmojibaseCategory.Activity -> CommonStrings.emoji_picker_category_activity - EmojibaseCategory.Places -> CommonStrings.emoji_picker_category_places - EmojibaseCategory.Objects -> CommonStrings.emoji_picker_category_objects - EmojibaseCategory.Symbols -> CommonStrings.emoji_picker_category_symbols - EmojibaseCategory.Flags -> CommonStrings.emoji_picker_category_flags +val EmojibaseCategory.title: Int + get() = when (this) { + EmojibaseCategory.People -> R.string.emoji_picker_category_people + EmojibaseCategory.Nature -> R.string.emoji_picker_category_nature + EmojibaseCategory.Foods -> R.string.emoji_picker_category_foods + EmojibaseCategory.Activity -> R.string.emoji_picker_category_activity + EmojibaseCategory.Places -> R.string.emoji_picker_category_places + EmojibaseCategory.Objects -> R.string.emoji_picker_category_objects + EmojibaseCategory.Symbols -> R.string.emoji_picker_category_symbols + EmojibaseCategory.Flags -> R.string.emoji_picker_category_flags } val EmojibaseCategory.icon: ImageVector - get() = - when(this){ - EmojibaseCategory.People -> Icons.Outlined.EmojiPeople - EmojibaseCategory.Nature -> Icons.Outlined.EmojiNature - EmojibaseCategory.Foods -> Icons.Outlined.EmojiFoodBeverage - EmojibaseCategory.Activity -> Icons.Outlined.EmojiEvents - EmojibaseCategory.Places -> Icons.Outlined.EmojiTransportation - EmojibaseCategory.Objects -> Icons.Outlined.EmojiObjects - EmojibaseCategory.Symbols -> Icons.Outlined.EmojiSymbols - EmojibaseCategory.Flags -> Icons.Outlined.EmojiFlags - } - + get() = when (this) { + EmojibaseCategory.People -> Icons.Outlined.EmojiPeople + EmojibaseCategory.Nature -> Icons.Outlined.EmojiNature + EmojibaseCategory.Foods -> Icons.Outlined.EmojiFoodBeverage + EmojibaseCategory.Activity -> Icons.Outlined.EmojiEvents + EmojibaseCategory.Places -> Icons.Outlined.EmojiTransportation + EmojibaseCategory.Objects -> Icons.Outlined.EmojiObjects + EmojibaseCategory.Symbols -> Icons.Outlined.EmojiSymbols + EmojibaseCategory.Flags -> Icons.Outlined.EmojiFlags + } diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 5eeb2c2264..09c0aa4929 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -1,5 +1,13 @@ + "Aktivity" + "Vlajky" + "Jídlo a nápoje" + "Zvířata a příroda" + "Předměty" + "Smajlíci a lidé" + "Cestování a místa" + "Symboly" "%1$d změna místnosti" "%1$d změny místnosti" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 161868614c..c561551060 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -1,5 +1,13 @@ + "Aktivitäten" + "Flaggen" + "Essen & Trinken" + "Tiere & Natur" + "Objekte" + "Smileys & Menschen" + "Reisen & Orte" + "Symbole" "%1$d Raumänderung" "%1$d Raumänderungen" diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index fe186df358..f411e6152f 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -1,5 +1,13 @@ + "Actividades" + "Banderas" + "Comida y bebida" + "Animales y naturaleza" + "Objetos" + "Emojis y personas" + "Viajes y lugares" + "Símbolos" "%1$d cambio en la sala" "%1$d cambios en la sala" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index f68a29ce16..13376219da 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -1,5 +1,13 @@ + "Activités" + "Drapeaux" + "Nourriture et boissons" + "Animaux et nature" + "Objets" + "Émoticônes et personnes" + "Voyages & lieux" + "Symboles" "%1$d changement dans le salon" "%1$d changements dans le salon" diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml index 1b0a2c99a3..295be40922 100644 --- a/features/messages/impl/src/main/res/values-it/translations.xml +++ b/features/messages/impl/src/main/res/values-it/translations.xml @@ -1,5 +1,13 @@ + "Attività" + "Bandiere" + "Cibi & Bevande" + "Animali & Natura" + "Oggetti" + "Faccine & Persone" + "Viaggi & Luoghi" + "Simboli" "%1$d modifica alla stanza" "%1$d modifiche alla stanza" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index 16e4867196..d096387b16 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -1,5 +1,13 @@ + "Activități" + "Steaguri" + "Mâncare & Băutură" + "Animale și Natură" + "Obiecte" + "Fețe zâmbitoare & Oameni" + "Călătorii & Locuri" + "Simboluri" "%1$d schimbare a camerii" "%1$d schimbări ale camerei" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index c3af66d8ad..881c8c929f 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -1,5 +1,13 @@ + "Деятельность" + "Флаги" + "Еда и напитки" + "Животные и природа" + "Объекты" + "Смайлы и люди" + "Путешествия и места" + "Символы" "%1$d изменение в комнате" "%1$d изменения в комнате" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 8800aa7a22..b0f20c938d 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -1,5 +1,13 @@ + "Aktivity" + "Vlajky" + "Jedlo a nápoje" + "Zvieratá a príroda" + "Predmety" + "Smajlíky a ľudia" + "Cestovanie a miesta" + "Symboly" "%1$d zmena miestnosti" "%1$d zmeny miestnosti" diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml index d3e9c9bce9..945d815ce6 100644 --- a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml @@ -1,5 +1,13 @@ + "活動" + "旗幟" + "食物與飲料" + "動物與大自然" + "物品" + "表情與人物" + "旅行與景點" + "標誌" "%1$d 個聊天室變更" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index 17f791e752..cbf6c3ec16 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -1,5 +1,13 @@ + "Activities" + "Flags" + "Food & Drink" + "Animals & Nature" + "Objects" + "Smileys & People" + "Travel & Places" + "Symbols" "%1$d room change" "%1$d room changes" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 59a52044da..ee44b50929 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -178,14 +178,6 @@ "Hlasování: %1$s" "Potvrzení" "Upozornění" - "Aktivity" - "Vlajky" - "Jídlo a nápoje" - "Zvířata a příroda" - "Předměty" - "Smajlíci a lidé" - "Cestování a místa" - "Symboly" "Vytvoření trvalého odkazu se nezdařilo" "%1$s nemohl načíst mapu. Zkuste to prosím později." "Načítání zpráv se nezdařilo" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 2983bc0be4..9cee0be1a4 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -155,14 +155,6 @@ "Umfrage: %1$s" "Bestätigung" "Warnung" - "Aktivitäten" - "Flaggen" - "Essen & Trinken" - "Tiere & Natur" - "Objekte" - "Smileys & Menschen" - "Reisen & Orte" - "Symbole" "Fehler beim Erstellen des Permalinks" "%1$s konnte die Karte nicht laden. Bitte versuche es später erneut." "Fehler beim Laden der Nachrichten" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 8bc500d7fe..75948bd37f 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -94,14 +94,6 @@ "Esperando…" "Confirmar" "Atención" - "Actividades" - "Banderas" - "Comida y bebida" - "Animales y naturaleza" - "Objetos" - "Emojis y personas" - "Viajes y lugares" - "Símbolos" "No se pudo crear el enlace permanente" "Error al cargar mensajes" "Algunos mensajes no se han enviado" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index ed8ff54abf..44b165a44e 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -176,14 +176,6 @@ "Sondage : %1$s" "Confirmation" "Attention" - "Activités" - "Drapeaux" - "Nourriture et boissons" - "Animaux et nature" - "Objets" - "Émoticônes et personnes" - "Voyages & lieux" - "Symboles" "Échec de la création du permalien" "%1$s n’a pas pu charger la carte. Veuillez réessayer ultérieurement." "Échec du chargement des messages" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 303ab94c50..4d80e95faf 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -94,14 +94,6 @@ "In attesa…" "Conferma" "Attenzione" - "Attività" - "Bandiere" - "Cibi & Bevande" - "Animali & Natura" - "Oggetti" - "Faccine & Persone" - "Viaggi & Luoghi" - "Simboli" "Impossibile creare il collegamento permanente" "Caricamento dei messaggi non riuscito" "Alcuni messaggi non sono stati inviati" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 00825d232a..8b0bb4f61a 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -137,14 +137,6 @@ "Se aşteaptă…" "Confirmare" "Avertisment" - "Activități" - "Steaguri" - "Mâncare & Băutură" - "Animale și Natură" - "Obiecte" - "Fețe zâmbitoare & Oameni" - "Călătorii & Locuri" - "Simboluri" "Crearea permalink-ului a eșuat" "%1$s nu a putut încărca harta. Vă rugăm să încercați din nou mai târziu." "Încărcarea mesajelor a eșuat" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index aca6c68a85..4caabe371e 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -180,14 +180,6 @@ "Подтверждение устройства" "Подтверждение" "Предупреждение" - "Деятельность" - "Флаги" - "Еда и напитки" - "Животные и природа" - "Объекты" - "Смайлы и люди" - "Путешествия и места" - "Символы" "Не удалось создать постоянную ссылку" "Не удалось загрузить карту %1$s. Пожалуйста, повторите попытку позже." "Не удалось загрузить сообщения" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 3f4d241808..398bdce0c4 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -180,14 +180,6 @@ "Overiť zariadenie" "Potvrdenie" "Upozornenie" - "Aktivity" - "Vlajky" - "Jedlo a nápoje" - "Zvieratá a príroda" - "Predmety" - "Smajlíky a ľudia" - "Cestovanie a miesta" - "Symboly" "Nepodarilo sa vytvoriť trvalý odkaz" "%1$s nedokázal načítať mapu. Skúste to prosím neskôr." "Načítanie správ zlyhalo" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index f4037bd603..0fd4347298 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -152,14 +152,6 @@ "投票:%1$s" "確認" "警告" - "活動" - "旗幟" - "食物與飲料" - "動物與大自然" - "物品" - "表情與人物" - "旅行與景點" - "標誌" "無法建立永久連結" "%1$s無法載入地圖。請稍後再試。" "無法載入訊息" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 4f18758bfb..88524e9375 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -180,14 +180,6 @@ "Verify device" "Confirmation" "Warning" - "Activities" - "Flags" - "Food & Drink" - "Animals & Nature" - "Objects" - "Smileys & People" - "Travel & Places" - "Symbols" "Failed creating the permalink" "%1$s could not load the map. Please try again later." "Failed loading messages" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 08d5e35456..4032f55536 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -116,7 +116,8 @@ "screen_room_.*", "screen\\.room\\..*", "screen_dm_details_.*", - "room_timeline_state_changes" + "room_timeline_state_changes", + "emoji_picker_category_.*" ], "excludeRegex": [ "screen_room_details_.*", From 0adc3b1f1dc0464711ec1f7e8716d3352e4812da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 11:12:35 +0100 Subject: [PATCH 022/102] Localazy: move `screen_notification_settings_` strings to the `:features:preferences:impl` module. Need to also import `screen_notification_settings_edit_failed_updating_default_mode` to the module `:features:roomdetails:impl`. --- .../notifications/NotificationSettingsView.kt | 33 ++++++++++--------- .../edit/DefaultNotificationSettingOption.kt | 6 ++-- .../EditDefaultNotificationSettingView.kt | 17 +++++----- .../impl/root/PreferencesRootView.kt | 3 +- .../src/main/res/values-cs/translations.xml | 27 +++++++++++++++ .../src/main/res/values-de/translations.xml | 23 +++++++++++++ .../src/main/res/values-fr/translations.xml | 27 +++++++++++++++ .../src/main/res/values-ro/translations.xml | 30 +++++++++++++++++ .../src/main/res/values-ru/translations.xml | 25 ++++++++++++++ .../src/main/res/values-sk/translations.xml | 27 +++++++++++++++ .../main/res/values-zh-rTW/translations.xml | 12 +++++++ .../impl/src/main/res/values/localazy.xml | 25 ++++++++++++++ .../RoomNotificationSettingsView.kt | 4 +-- ...UserDefinedRoomNotificationSettingsView.kt | 4 +-- .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-sk/translations.xml | 1 + .../main/res/values-zh-rTW/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../impl/components/RoomSummaryRow.kt | 4 +-- .../src/main/res/values-cs/translations.xml | 27 --------------- .../src/main/res/values-de/translations.xml | 23 ------------- .../src/main/res/values-fr/translations.xml | 27 --------------- .../src/main/res/values-ro/translations.xml | 27 --------------- .../src/main/res/values-ru/translations.xml | 25 -------------- .../src/main/res/values-sk/translations.xml | 27 --------------- .../main/res/values-zh-rTW/translations.xml | 12 ------- .../src/main/res/values/localazy.xml | 25 -------------- tools/localazy/config.json | 6 ++-- 32 files changed, 245 insertions(+), 229 deletions(-) create mode 100644 features/preferences/impl/src/main/res/values-ro/translations.xml diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt index f93ecad3fd..167dc9b1c1 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/NotificationSettingsView.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.lifecycle.Lifecycle +import io.element.android.features.preferences.impl.R import io.element.android.libraries.androidutils.system.startNotificationSettingsIntent import io.element.android.libraries.designsystem.atomic.molecules.DialogLikeBannerMolecule import io.element.android.libraries.designsystem.components.async.AsyncView @@ -57,7 +58,7 @@ fun NotificationSettingsView( PreferencePage( modifier = modifier, onBackPressed = onBackPressed, - title = stringResource(id = CommonStrings.screen_notification_settings_title) + title = stringResource(id = R.string.screen_notification_settings_title) ) { when (state.matrixSettings) { @@ -80,7 +81,7 @@ fun NotificationSettingsView( } AsyncView( async = state.changeNotificationSettingAction, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(NotificationSettingsEvents.ClearNotificationChangeError) }, onSuccess = {}, ) @@ -103,10 +104,10 @@ private fun NotificationSettingsContentView( if (systemSettings.appNotificationsEnabled && !systemSettings.systemNotificationsEnabled) { PreferenceText( iconResourceId = CommonDrawables.ic_compound_notifications_solid_off, - title = stringResource(id = CommonStrings.screen_notification_settings_system_notifications_turned_off), + title = stringResource(id = R.string.screen_notification_settings_system_notifications_turned_off), subtitle = stringResource( - id = CommonStrings.screen_notification_settings_system_notifications_action_required, - stringResource(id = CommonStrings.screen_notification_settings_system_notifications_action_required_content_link) + id = R.string.screen_notification_settings_system_notifications_action_required, + stringResource(id = R.string.screen_notification_settings_system_notifications_action_required_content_link) ), onClick = { context.startNotificationSettingsIntent() @@ -116,31 +117,31 @@ private fun NotificationSettingsContentView( PreferenceSwitch( modifier = modifier, - title = stringResource(id = CommonStrings.screen_notification_settings_enable_notifications), + title = stringResource(id = R.string.screen_notification_settings_enable_notifications), isChecked = systemSettings.appNotificationsEnabled, switchAlignment = Alignment.Top, onCheckedChange = onNotificationsEnabledChanged ) if (systemSettings.appNotificationsEnabled) { - PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_notification_section_title)) { + PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_notification_section_title)) { PreferenceText( - title = stringResource(id = CommonStrings.screen_notification_settings_group_chats), + title = stringResource(id = R.string.screen_notification_settings_group_chats), subtitle = getTitleForRoomNotificationMode(mode = matrixSettings.defaultGroupNotificationMode), onClick = onGroupChatsClicked ) PreferenceText( - title = stringResource(id = CommonStrings.screen_notification_settings_direct_chats), + title = stringResource(id = R.string.screen_notification_settings_direct_chats), subtitle = getTitleForRoomNotificationMode(mode = matrixSettings.defaultOneToOneNotificationMode), onClick = onDirectChatsClicked ) } - PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_mode_mentions)) { + PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_mode_mentions)) { PreferenceSwitch( modifier = Modifier, - title = stringResource(id = CommonStrings.screen_notification_settings_room_mention_label), + title = stringResource(id = R.string.screen_notification_settings_room_mention_label), isChecked = matrixSettings.atRoomNotificationsEnabled, switchAlignment = Alignment.Top, onCheckedChange = onMentionNotificationsChanged @@ -162,8 +163,8 @@ private fun NotificationSettingsContentView( @Composable private fun getTitleForRoomNotificationMode(mode: RoomNotificationMode?) = when (mode) { - RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages) - RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords) + RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages) + RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords) RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute) null -> "" } @@ -177,8 +178,8 @@ private fun InvalidNotificationSettingsView( ) { DialogLikeBannerMolecule( modifier = modifier, - title = stringResource(CommonStrings.screen_notification_settings_configuration_mismatch), - content = stringResource(CommonStrings.screen_notification_settings_configuration_mismatch_description), + title = stringResource(R.string.screen_notification_settings_configuration_mismatch), + content = stringResource(R.string.screen_notification_settings_configuration_mismatch_description), onSubmitClicked = onContinueClicked, onDismissClicked = null, ) @@ -186,7 +187,7 @@ private fun InvalidNotificationSettingsView( if (showError) { ErrorDialog( title = stringResource(id = CommonStrings.dialog_title_error), - content = stringResource(id = CommonStrings.screen_notification_settings_failed_fixing_configuration), + content = stringResource(id = R.string.screen_notification_settings_failed_fixing_configuration), onDismiss = onDismissError ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt index c7d2ac507d..3201243e75 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/DefaultNotificationSettingOption.kt @@ -27,13 +27,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp +import io.element.android.features.preferences.impl.R import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.theme.components.RadioButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.ui.strings.CommonStrings @Composable fun DefaultNotificationSettingOption( @@ -43,8 +43,8 @@ fun DefaultNotificationSettingOption( onOptionSelected: (RoomNotificationMode) -> Unit = {}, ) { val subtitle = when(mode) { - RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages) - RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords) + RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages) + RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords) else -> "" } Row( diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt index dbbd90791b..415d5e01da 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/edit/EditDefaultNotificationSettingView.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.preferences.impl.R import io.element.android.libraries.designsystem.components.async.AsyncView import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -49,9 +50,9 @@ fun EditDefaultNotificationSettingView( modifier: Modifier = Modifier, ) { val title = if (state.isOneToOne) { - CommonStrings.screen_notification_settings_direct_chats + R.string.screen_notification_settings_direct_chats } else { - CommonStrings.screen_notification_settings_group_chats + R.string.screen_notification_settings_group_chats } PreferencePage( modifier = modifier, @@ -63,9 +64,9 @@ fun EditDefaultNotificationSettingView( val validModes = listOf(RoomNotificationMode.ALL_MESSAGES, RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY) val categoryTitle = if (state.isOneToOne) { - CommonStrings.screen_notification_settings_edit_screen_direct_section_header + R.string.screen_notification_settings_edit_screen_direct_section_header } else { - CommonStrings.screen_notification_settings_edit_screen_group_section_header + R.string.screen_notification_settings_edit_screen_group_section_header } PreferenceCategory(title = stringResource(id = categoryTitle)) { @@ -82,12 +83,12 @@ fun EditDefaultNotificationSettingView( } } if (state.roomsWithUserDefinedMode.isNotEmpty()) { - PreferenceCategory(title = stringResource(id = CommonStrings.screen_notification_settings_edit_custom_settings_section_title)) { + PreferenceCategory(title = stringResource(id = R.string.screen_notification_settings_edit_custom_settings_section_title)) { state.roomsWithUserDefinedMode.forEach { summary -> val subtitle = when (summary.details.notificationMode) { - RoomNotificationMode.ALL_MESSAGES -> stringResource(id = CommonStrings.screen_notification_settings_edit_mode_all_messages) + RoomNotificationMode.ALL_MESSAGES -> stringResource(id = R.string.screen_notification_settings_edit_mode_all_messages) RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> { - stringResource(id = CommonStrings.screen_notification_settings_edit_mode_mentions_and_keywords) + stringResource(id = R.string.screen_notification_settings_edit_mode_mentions_and_keywords) } RoomNotificationMode.MUTE -> stringResource(id = CommonStrings.common_mute) null -> "" @@ -117,7 +118,7 @@ fun EditDefaultNotificationSettingView( } AsyncView( async = state.changeNotificationSettingAction, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(EditDefaultNotificationSettingStateEvents.ClearError) }, onSuccess = {}, ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 801a7f6895..1fc47fdece 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.user.UserPreferences import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferencePage @@ -116,7 +117,7 @@ fun PreferencesRootView( } if (state.showNotificationSettings) { ListItem( - headlineContent = { Text(stringResource(id = CommonStrings.screen_notification_settings_title)) }, + headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) }, leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_notifications)), onClick = onOpenNotificationSettings, ) diff --git a/features/preferences/impl/src/main/res/values-cs/translations.xml b/features/preferences/impl/src/main/res/values-cs/translations.xml index d19ba2ac08..49ffdd14a1 100644 --- a/features/preferences/impl/src/main/res/values-cs/translations.xml +++ b/features/preferences/impl/src/main/res/values-cs/translations.xml @@ -12,4 +12,31 @@ "Nelze aktualizovat profil" "Upravit profil" "Aktualizace profilu…" + "Další nastavení" + "Halsové a video hovory" + "Neshoda konfigurace" + "Zjednodušili jsme nastavení oznámení, abychom usnadnili hledání možností. + +Některá vlastní nastavení, která jste si vybrali v minulosti, se zde nezobrazují, ale jsou stále aktivní. + +Pokud budete pokračovat, některá nastavení se mohou změnit." + "Přímé zprávy" + "Vlastní nastavení pro chat" + "Při aktualizaci nastavení oznámení došlo k chybě." + "Všechny zprávy" + "Pouze zmínky a klíčová slova" + "V přímých zprávách mě upozornit na" + "Ve skupinových chatech mě upozornit na" + "Povolit oznámení na tomto zařízení" + "Konfigurace nebyla opravena, zkuste to prosím znovu." + "Skupinové chaty" + "Zmínky" + "Vše" + "Zmínky" + "Upozornit mě na" + "Upozornit mě na @room" + "Chcete-li dostávat oznámení, změňte prosím svůj %1$s." + "systémová nastavení" + "Systémová oznámení byla vypnuta" + "Oznámení" diff --git a/features/preferences/impl/src/main/res/values-de/translations.xml b/features/preferences/impl/src/main/res/values-de/translations.xml index 3992b93ab7..4287d4d7c8 100644 --- a/features/preferences/impl/src/main/res/values-de/translations.xml +++ b/features/preferences/impl/src/main/res/values-de/translations.xml @@ -8,4 +8,27 @@ "Profil kann nicht aktualisiert werden" "Profil bearbeiten" "Profil wird aktualisiert…" + "Zusätzliche Einstellungen" + "Audio- und Videoanrufe" + "Konfiguration stimmt nicht überein" + "Wir haben die Einstellungen für Benachrichtigungen vereinfacht, damit die Optionen leichter zu finden sind. Einige benutzerdefinierte Einstellungen, die du in der Vergangenheit gewählt hast, werden hier nicht angezeigt, sind aber immer noch aktiv. Wenn du fortfährst, können sich einige deiner Einstellungen ändern." + "Direkte Chats" + "Benutzerdefinierte Einstellung pro Chat" + "Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." + "Alle Nachrichten" + "Nur Erwähnungen und Schlüsselwörter" + "Bei direkten Chats, benachrichtige mich bei" + "Bei Gruppenchats benachrichtige mich bei" + "Benachrichtigungen auf diesem Gerät aktivieren" + "Die Konfiguration wurde nicht korrigiert, bitte versuche es erneut." + "Gruppenchats" + "Erwähnungen" + "Alle" + "Erwähnungen" + "Benachrichtige mich bei" + "Benachrichtige mich bei @room" + "Um Benachrichtigungen zu erhalten, ändere bitte deine %1$s." + "Systemeinstellungen" + "Systembenachrichtigungen deaktiviert" + "Benachrichtigungen" diff --git a/features/preferences/impl/src/main/res/values-fr/translations.xml b/features/preferences/impl/src/main/res/values-fr/translations.xml index 85ac80b5d9..a831656698 100644 --- a/features/preferences/impl/src/main/res/values-fr/translations.xml +++ b/features/preferences/impl/src/main/res/values-fr/translations.xml @@ -12,4 +12,31 @@ "Impossible de mettre à jour le profil" "Modifier le profil" "Mise à jour du profil…" + "Réglages supplémentaires" + "Appels audio et vidéo" + "Incompatibilité de configuration" + "Nous avons simplifié les paramètres des notifications pour que les options soient plus faciles à trouver. + +Certains paramètres personnalisés que vous avez choisis par le passé ne sont pas affichés ici, mais ils sont toujours actifs. + +Si vous continuez, il est possible que certains de vos paramètres soient modifiés." + "Discussions directes" + "Paramétrage personnalisé par salon" + "Une erreur s’est produite lors de la mise à jour du paramètre de notification." + "Tous les messages" + "Mentions et mots clés uniquement" + "Sur les discussions directes, prévenez-moi pour" + "Lors de discussions de groupe, prévenez-moi pour" + "Activer les notifications sur cet appareil" + "La configuration n’a pas été corrigée, veuillez réessayer." + "Discussions de groupe" + "Mentions" + "Tous" + "Mentions" + "Prévenez-moi pour" + "Prévenez-moi si un message contient \"@room\"" + "Pour recevoir des notifications, veuillez modifier votre %1$s." + "paramètres du système" + "Les notifications du système sont désactivées" + "Notifications" diff --git a/features/preferences/impl/src/main/res/values-ro/translations.xml b/features/preferences/impl/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..1b652c3a30 --- /dev/null +++ b/features/preferences/impl/src/main/res/values-ro/translations.xml @@ -0,0 +1,30 @@ + + + "Setări adiționale" + "Apeluri audio și video" + "Nepotrivire de configurație" + "Am simplificat Setările pentru notificări pentru a face opțiunile mai ușor de găsit. + +Unele setări personalizate pe care le-ați ales în trecut nu sunt afișate aici, dar sunt încă active. + +Dacă continuați, unele dintre setările dumneavoastră pot fi modificate." + "Discuții directe" + "Setare personalizată per chat" + "A apărut o eroare în timpul actualizării setărilor pentru notificari." + "Toate mesajele" + "Numai mențiuni și cuvinte cheie" + "În conversațiile directe, anunță-mă pentru" + "În conversațiile de grup, anunțați-mă pentru" + "Activați notificările pe acest dispozitiv" + "Configurația nu a fost corectată, vă rugăm să încercați din nou." + "Discuții de grup" + "Mențiuni" + "Toate" + "Mențiuni" + "Anunță-mă pentru" + "Anunțați-mă pentru @room" + "Pentru a primi notificări, vă rugăm să vă schimbați %1$s." + "Setări de sistem" + "Notificările de sistem sunt dezactivate" + "Notificări" + diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index a1fd3f3bc6..9afb388a9a 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -12,4 +12,29 @@ "Невозможно обновить профиль" "Редактировать профиль" "Обновление профиля…" + "Дополнительные параметры" + "Аудио и видео звонки" + "Несоответствие конфигурации" + "Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. + +Если вы продолжите, некоторые настройки могут быть изменены." + "Прямые чаты" + "Индивидуальные настройки для каждого чата" + "При обновлении настроек уведомления произошла ошибка." + "Все сообщения" + "Только упоминания и ключевые слова" + "Уведомлять меня в личных чатах" + "Уведомлять меня в групповых чатах" + "Включить уведомления на данном устройстве" + "Конфигурация не была исправлена, попробуйте еще раз." + "Групповые чаты" + "Упоминания" + "Все" + "Упоминания" + "Уведомить меня" + "Уведомить меня в @room" + "Чтобы получать уведомления, измените свой %1$s." + "настройки системы" + "Системные уведомления выключены" + "Уведомления" diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index 2e1dc22b93..b1de800205 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -12,4 +12,31 @@ "Nepodarilo sa aktualizovať profil" "Upraviť profil" "Aktualizácia profilu…" + "Ďalšie nastavenia" + "Audio a video hovory" + "Nezhoda konfigurácie" + "Zjednodušili sme Nastavenia oznámení, aby ste ľahšie našli možnosti. + +Niektoré vlastné nastavenia, ktoré ste si nastavili v minulosti, sa tu nezobrazujú, ale sú stále aktívne. + +Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť." + "Priame konverzácie" + "Vlastné nastavenie pre konverzácie" + "Pri aktualizácii nastavenia oznámenia došlo k chybe." + "Všetky správy" + "Iba zmienky a kľúčové slová" + "Pri priamych rozhovoroch ma upozorniť na" + "Pri skupinových rozhovoroch ma upozorniť na" + "Povoliť oznámenia na tomto zariadení" + "Konfigurácia nebola opravená, skúste to prosím znova." + "Skupinové rozhovory" + "Zmienky" + "Všetky" + "Zmienky" + "Upozorniť ma na" + "Upozorniť ma na @miestnosť" + "Ak chcete dostávať oznámenia, zmeňte prosím svoje %1$s." + "nastavenia systému" + "Systémové oznámenia sú vypnuté" + "Oznámenia" diff --git a/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml index 046e820a51..1282f45e4a 100644 --- a/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh-rTW/translations.xml @@ -6,4 +6,16 @@ "無法更新個人檔案" "編輯個人檔案" "正在更新個人檔案…" + "其他設定" + "私訊" + "更新通知設定時發生錯誤。" + "所有訊息" + "僅限提及與關鍵字" + "在這個裝置上開啟通知" + "群組聊天" + "提及" + "提及" + "系統設定" + "已關閉系統通知" + "通知" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 1ca7071436..153c64f661 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -12,4 +12,29 @@ "Unable to update profile" "Edit profile" "Updating profile…" + "Additional settings" + "Audio and video calls" + "Configuration mismatch" + "We’ve simplified Notifications Settings to make options easier to find. Some custom settings you’ve chosen in the past are not shown here, but they’re still active. + +If you proceed, some of your settings may change." + "Direct chats" + "Custom setting per chat" + "An error occurred while updating the notification setting." + "All messages" + "Mentions and Keywords only" + "On direct chats, notify me for" + "On group chats, notify me for" + "Enable notifications on this device" + "The configuration has not been corrected, please try again." + "Group chats" + "Mentions" + "All" + "Mentions" + "Notify me for" + "Notify me on @room" + "To receive notifications, please change your %1$s." + "system settings" + "System notifications turned off" + "Notifications" diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt index 96372c0e65..cdb46a2fcc 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/RoomNotificationSettingsView.kt @@ -156,14 +156,14 @@ private fun RoomSpecificNotificationSettingsView( AsyncView( async = state.setNotificationSettingAction, onSuccess = {}, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(RoomNotificationSettingsEvents.ClearSetNotificationError) }, ) AsyncView( async = state.restoreDefaultAction, onSuccess = {}, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(RoomNotificationSettingsEvents.ClearRestoreDefaultError) }, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index 88a4b8a0a1..937422fd5d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -88,14 +88,14 @@ fun UserDefinedRoomNotificationSettingsView( AsyncView( async = state.setNotificationSettingAction, onSuccess = {}, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(RoomNotificationSettingsEvents.ClearSetNotificationError) }, ) AsyncView( async = state.restoreDefaultAction, onSuccess = { onBackPressed() }, - errorMessage = { stringResource(CommonStrings.screen_notification_settings_edit_failed_updating_default_mode) }, + errorMessage = { stringResource(R.string.screen_notification_settings_edit_failed_updating_default_mode) }, onErrorDismiss = { state.eventSink(RoomNotificationSettingsEvents.ClearRestoreDefaultError) }, ) } diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml index b02fcbc8e8..110257d6ae 100644 --- a/features/roomdetails/impl/src/main/res/values-cs/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -5,6 +5,7 @@ "%1$d osoby" "%1$d osob" + "Při aktualizaci nastavení oznámení došlo k chybě." "Přidat téma" "Již členem" "Již pozván(a)" diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index 5f0d558820..9589c06a1e 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -4,6 +4,7 @@ "%1$d Person" "%1$d Personen" + "Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." "Thema hinzufügen" "Bereits Mitglied" "Bereits eingeladen" diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index f899a58873..d2b7f22292 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -4,6 +4,7 @@ "%1$d personne" "%1$d personnes" + "Une erreur s’est produite lors de la mise à jour du paramètre de notification." "Ajouter un sujet" "Déjà membre" "Déjà invité(e)" diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index 958058432a..1a78709241 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -4,6 +4,7 @@ "o persoană" "%1$d persoane" + "A apărut o eroare în timpul actualizării setărilor pentru notificari." "Adăugare subiect" "Deja membru" "Deja invitat" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index d8e15924cc..38a8835251 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -5,6 +5,7 @@ "%1$d пользователя" "%1$d пользователей" + "При обновлении настроек уведомления произошла ошибка." "Добавить тему" "Уже зарегистрирован" "Уже приглашены" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index 0cdd7b3b0c..edaf50d915 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -5,6 +5,7 @@ "%1$d osoby" "%1$d osôb" + "Pri aktualizácii nastavenia oznámenia došlo k chybe." "Pridať tému" "Už ste členom" "Už ste pozvaní" diff --git a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml index 71ed1df861..50202c0e81 100644 --- a/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh-rTW/translations.xml @@ -3,6 +3,7 @@ "%1$d 位夥伴" + "更新通知設定時發生錯誤。" "新增主題" "已是成員" "已邀請" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 833b06ca40..70c31ac65e 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -4,6 +4,7 @@ "%1$d person" "%1$d people" + "An error occurred while updating the notification setting." "Add topic" "Already a member" "Already invited" diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index a437dd339c..44587b0f95 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -200,14 +200,14 @@ private fun NotificationIcon(room: RoomListRoomSummary) { RoomNotificationMode.MENTIONS_AND_KEYWORDS_ONLY -> Icon( modifier = Modifier.size(16.dp), - contentDescription = stringResource(CommonStrings.screen_notification_settings_mode_mentions), + contentDescription = null, imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_mention), tint = tint, ) RoomNotificationMode.MUTE -> Icon( modifier = Modifier.size(16.dp), - contentDescription = stringResource(CommonStrings.common_mute), + contentDescription = null, imageVector = ImageVector.vectorResource(CommonDrawables.ic_compound_notifications_solid_off), tint = tint, ) diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index ee44b50929..a1fc6da91e 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -218,33 +218,6 @@ "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." - "Další nastavení" - "Halsové a video hovory" - "Neshoda konfigurace" - "Zjednodušili jsme nastavení oznámení, abychom usnadnili hledání možností. - -Některá vlastní nastavení, která jste si vybrali v minulosti, se zde nezobrazují, ale jsou stále aktivní. - -Pokud budete pokračovat, některá nastavení se mohou změnit." - "Přímé zprávy" - "Vlastní nastavení pro chat" - "Při aktualizaci nastavení oznámení došlo k chybě." - "Všechny zprávy" - "Pouze zmínky a klíčová slova" - "V přímých zprávách mě upozornit na" - "Ve skupinových chatech mě upozornit na" - "Povolit oznámení na tomto zařízení" - "Konfigurace nebyla opravena, zkuste to prosím znovu." - "Skupinové chaty" - "Zmínky" - "Vše" - "Zmínky" - "Upozornit mě na" - "Upozornit mě na @room" - "Chcete-li dostávat oznámení, změňte prosím svůj %1$s." - "systémová nastavení" - "Systémová oznámení byla vypnuta" - "Oznámení" "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Sdílet polohu" "Sdílet moji polohu" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 9cee0be1a4..c652288a3b 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -186,29 +186,6 @@ "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." - "Zusätzliche Einstellungen" - "Audio- und Videoanrufe" - "Konfiguration stimmt nicht überein" - "Wir haben die Einstellungen für Benachrichtigungen vereinfacht, damit die Optionen leichter zu finden sind. Einige benutzerdefinierte Einstellungen, die du in der Vergangenheit gewählt hast, werden hier nicht angezeigt, sind aber immer noch aktiv. Wenn du fortfährst, können sich einige deiner Einstellungen ändern." - "Direkte Chats" - "Benutzerdefinierte Einstellung pro Chat" - "Beim Aktualisieren der Benachrichtigungseinstellungen ist ein Fehler aufgetreten." - "Alle Nachrichten" - "Nur Erwähnungen und Schlüsselwörter" - "Bei direkten Chats, benachrichtige mich bei" - "Bei Gruppenchats benachrichtige mich bei" - "Benachrichtigungen auf diesem Gerät aktivieren" - "Die Konfiguration wurde nicht korrigiert, bitte versuche es erneut." - "Gruppenchats" - "Erwähnungen" - "Alle" - "Erwähnungen" - "Benachrichtige mich bei" - "Benachrichtige mich bei @room" - "Um Benachrichtigungen zu erhalten, ändere bitte deine %1$s." - "Systemeinstellungen" - "Systembenachrichtigungen deaktiviert" - "Benachrichtigungen" "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Standort teilen" "Meinen Standort teilen" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index 44b165a44e..8974366164 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -213,33 +213,6 @@ "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." - "Réglages supplémentaires" - "Appels audio et vidéo" - "Incompatibilité de configuration" - "Nous avons simplifié les paramètres des notifications pour que les options soient plus faciles à trouver. - -Certains paramètres personnalisés que vous avez choisis par le passé ne sont pas affichés ici, mais ils sont toujours actifs. - -Si vous continuez, il est possible que certains de vos paramètres soient modifiés." - "Discussions directes" - "Paramétrage personnalisé par salon" - "Une erreur s’est produite lors de la mise à jour du paramètre de notification." - "Tous les messages" - "Mentions et mots clés uniquement" - "Sur les discussions directes, prévenez-moi pour" - "Lors de discussions de groupe, prévenez-moi pour" - "Activer les notifications sur cet appareil" - "La configuration n’a pas été corrigée, veuillez réessayer." - "Discussions de groupe" - "Mentions" - "Tous" - "Mentions" - "Prévenez-moi pour" - "Prévenez-moi si un message contient \"@room\"" - "Pour recevoir des notifications, veuillez modifier votre %1$s." - "paramètres du système" - "Les notifications du système sont désactivées" - "Notifications" "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Partage de position" "Partager ma position" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 8b0bb4f61a..4cd74977b1 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -170,33 +170,6 @@ "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Încărcarea fișierelor media a eșuat, încercați din nou." - "Setări adiționale" - "Apeluri audio și video" - "Nepotrivire de configurație" - "Am simplificat Setările pentru notificări pentru a face opțiunile mai ușor de găsit. - -Unele setări personalizate pe care le-ați ales în trecut nu sunt afișate aici, dar sunt încă active. - -Dacă continuați, unele dintre setările dumneavoastră pot fi modificate." - "Discuții directe" - "Setare personalizată per chat" - "A apărut o eroare în timpul actualizării setărilor pentru notificari." - "Toate mesajele" - "Numai mențiuni și cuvinte cheie" - "În conversațiile directe, anunță-mă pentru" - "În conversațiile de grup, anunțați-mă pentru" - "Activați notificările pe acest dispozitiv" - "Configurația nu a fost corectată, vă rugăm să încercați din nou." - "Discuții de grup" - "Mențiuni" - "Toate" - "Mențiuni" - "Anunță-mă pentru" - "Anunțați-mă pentru @room" - "Pentru a primi notificări, vă rugăm să vă schimbați %1$s." - "Setări de sistem" - "Notificările de sistem sunt dezactivate" - "Notificări" "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Partajați locația" "Distribuiți locația mea" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 4caabe371e..1fa960f397 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -220,31 +220,6 @@ "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." - "Дополнительные параметры" - "Аудио и видео звонки" - "Несоответствие конфигурации" - "Мы упростили настройки уведомлений, чтобы упростить поиск опций. Некоторые пользовательские настройки, выбранные вами ранее, не отображаются в данном меню, но они все еще активны. - -Если вы продолжите, некоторые настройки могут быть изменены." - "Прямые чаты" - "Индивидуальные настройки для каждого чата" - "При обновлении настроек уведомления произошла ошибка." - "Все сообщения" - "Только упоминания и ключевые слова" - "Уведомлять меня в личных чатах" - "Уведомлять меня в групповых чатах" - "Включить уведомления на данном устройстве" - "Конфигурация не была исправлена, попробуйте еще раз." - "Групповые чаты" - "Упоминания" - "Все" - "Упоминания" - "Уведомить меня" - "Уведомить меня в @room" - "Чтобы получать уведомления, измените свой %1$s." - "настройки системы" - "Системные уведомления выключены" - "Уведомления" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Поделиться местоположением" "Поделиться моим местоположением" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 398bdce0c4..56f59f21c4 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -220,33 +220,6 @@ "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." - "Ďalšie nastavenia" - "Audio a video hovory" - "Nezhoda konfigurácie" - "Zjednodušili sme Nastavenia oznámení, aby ste ľahšie našli možnosti. - -Niektoré vlastné nastavenia, ktoré ste si nastavili v minulosti, sa tu nezobrazujú, ale sú stále aktívne. - -Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť." - "Priame konverzácie" - "Vlastné nastavenie pre konverzácie" - "Pri aktualizácii nastavenia oznámenia došlo k chybe." - "Všetky správy" - "Iba zmienky a kľúčové slová" - "Pri priamych rozhovoroch ma upozorniť na" - "Pri skupinových rozhovoroch ma upozorniť na" - "Povoliť oznámenia na tomto zariadení" - "Konfigurácia nebola opravená, skúste to prosím znova." - "Skupinové rozhovory" - "Zmienky" - "Všetky" - "Zmienky" - "Upozorniť ma na" - "Upozorniť ma na @miestnosť" - "Ak chcete dostávať oznámenia, zmeňte prosím svoje %1$s." - "nastavenia systému" - "Systémové oznámenia sú vypnuté" - "Oznámenia" "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Zdieľať polohu" "Zdieľať moju polohu" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 0fd4347298..4399a4e305 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -173,18 +173,6 @@ "檢舉這個內容的原因" "新訊息" "無法上傳媒體檔案,請稍後再試。" - "其他設定" - "私訊" - "更新通知設定時發生錯誤。" - "所有訊息" - "僅限提及與關鍵字" - "在這個裝置上開啟通知" - "群組聊天" - "提及" - "提及" - "系統設定" - "已關閉系統通知" - "通知" "分享位置" "分享我的位置" "在 Apple Maps 中開啟" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 88524e9375..41b765f118 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -217,31 +217,6 @@ "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." - "Additional settings" - "Audio and video calls" - "Configuration mismatch" - "We’ve simplified Notifications Settings to make options easier to find. Some custom settings you’ve chosen in the past are not shown here, but they’re still active. - -If you proceed, some of your settings may change." - "Direct chats" - "Custom setting per chat" - "An error occurred while updating the notification setting." - "All messages" - "Mentions and Keywords only" - "On direct chats, notify me for" - "On group chats, notify me for" - "Enable notifications on this device" - "The configuration has not been corrected, please try again." - "Group chats" - "Mentions" - "All" - "Mentions" - "Notify me for" - "Notify me on @room" - "To receive notifications, please change your %1$s." - "system settings" - "System notifications turned off" - "Notifications" "Check if you want to hide all current and future messages from this user" "Share location" "Share my location" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 4032f55536..621274c6ff 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -107,7 +107,8 @@ "screen_room_details_.*", "screen_room_member_list_.*", "screen_dm_details_.*", - "screen_room_notification_settings_.*" + "screen_room_notification_settings_.*", + "screen_notification_settings_edit_failed_updating_default_mode" ] }, { @@ -164,7 +165,8 @@ "includeRegex": [ "screen_advanced_settings_.*", "screen\\.advanced_settings\\..*", - "screen_edit_profile_.*" + "screen_edit_profile_.*", + "screen_notification_settings_.*" ] }, { From c6d34e859914e7bcbdc067558ebd3213ec9e55dc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 11:16:57 +0100 Subject: [PATCH 023/102] Localazy: move `settings_rageshake` strings to the `:features:rageshake:api` module. --- .../rageshake/api/preferences/RageshakePreferencesView.kt | 5 +++-- .../rageshake/api/src/main/res/values-cs/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-de/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-es/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-fr/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-it/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-ro/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-ru/translations.xml | 2 ++ .../rageshake/api/src/main/res/values-sk/translations.xml | 2 ++ features/rageshake/api/src/main/res/values/localazy.xml | 2 ++ libraries/ui-strings/src/main/res/values-cs/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-de/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-es/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-fr/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-it/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-ro/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-ru/translations.xml | 2 -- libraries/ui-strings/src/main/res/values-sk/translations.xml | 2 -- libraries/ui-strings/src/main/res/values/localazy.xml | 2 -- tools/localazy/config.json | 3 ++- 20 files changed, 23 insertions(+), 21 deletions(-) diff --git a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt index b9b8566d4a..d3c830a821 100644 --- a/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt +++ b/features/rageshake/api/src/main/kotlin/io/element/android/features/rageshake/api/preferences/RageshakePreferencesView.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter +import io.element.android.features.rageshake.api.R import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceSlide import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch @@ -43,7 +44,7 @@ fun RageshakePreferencesView( } Column(modifier = modifier) { - PreferenceCategory(title = stringResource(id = CommonStrings.settings_rageshake)) { + PreferenceCategory(title = stringResource(id = R.string.settings_rageshake)) { if (state.isSupported) { PreferenceSwitch( title = stringResource(id = CommonStrings.preference_rageshake), @@ -51,7 +52,7 @@ fun RageshakePreferencesView( onCheckedChange = ::onEnabledChanged ) PreferenceSlide( - title = stringResource(id = CommonStrings.settings_rageshake_detection_threshold), + title = stringResource(id = R.string.settings_rageshake_detection_threshold), // summary = stringResource(id = CommonStrings.settings_rageshake_detection_threshold_summary), value = state.sensitivity, enabled = state.isEnabled, diff --git a/features/rageshake/api/src/main/res/values-cs/translations.xml b/features/rageshake/api/src/main/res/values-cs/translations.xml index 59e4448ba6..7a48cf84db 100644 --- a/features/rageshake/api/src/main/res/values-cs/translations.xml +++ b/features/rageshake/api/src/main/res/values-cs/translations.xml @@ -2,4 +2,6 @@ "%1$s havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?" "Zdá se, že frustrovaně třesete telefonem. Chcete nahlásit chybu?" + "Rageshake" + "Práh detekce" diff --git a/features/rageshake/api/src/main/res/values-de/translations.xml b/features/rageshake/api/src/main/res/values-de/translations.xml index 6cfb4608dc..6a22b4a72c 100644 --- a/features/rageshake/api/src/main/res/values-de/translations.xml +++ b/features/rageshake/api/src/main/res/values-de/translations.xml @@ -1,4 +1,6 @@ "%1$s ist bei der letzten Nutzung abgestürzt. Möchtest du einen Absturzbericht mit uns teilen?" + "Rageshake" + "Erkennungsschwelle" diff --git a/features/rageshake/api/src/main/res/values-es/translations.xml b/features/rageshake/api/src/main/res/values-es/translations.xml index 597ec74260..cc9b1f810d 100644 --- a/features/rageshake/api/src/main/res/values-es/translations.xml +++ b/features/rageshake/api/src/main/res/values-es/translations.xml @@ -2,4 +2,6 @@ "%1$s se cerró inesperadamente la última vez que se lo usaste. ¿Quieres compartir un informe de error con nosotros?" "Parece que sacudes el teléfono con frustración. ¿Quieres abrir la pantalla de informe de errores?" + "Agitar con fuerza" + "Umbral de detección" diff --git a/features/rageshake/api/src/main/res/values-fr/translations.xml b/features/rageshake/api/src/main/res/values-fr/translations.xml index 4b41fdc0cb..e21171120e 100644 --- a/features/rageshake/api/src/main/res/values-fr/translations.xml +++ b/features/rageshake/api/src/main/res/values-fr/translations.xml @@ -2,4 +2,6 @@ "%1$s s’est arrêté la dernière fois qu’il a été utilisé. Souhaitez-vous partager un rapport d’incident avec nous ?" "Vous semblez secouez votre téléphone avec frustration. Souhaitez-vous ouvrir le formulaire pour reporter un problème?" + "Rageshake" + "Seuil de détection" diff --git a/features/rageshake/api/src/main/res/values-it/translations.xml b/features/rageshake/api/src/main/res/values-it/translations.xml index 6d5e7a74c0..d9580064c7 100644 --- a/features/rageshake/api/src/main/res/values-it/translations.xml +++ b/features/rageshake/api/src/main/res/values-it/translations.xml @@ -2,4 +2,6 @@ "%1$s si è chiuso inaspettatamente l\'ultima volta che è stato usato. Vuoi condividere con noi un rapporto sull\'arresto anomalo?" "Sembra che tu stia scuotendo il telefono per la frustrazione. Vuoi aprire la schermata di segnalazione dei problemi?" + "Rageshake" + "Soglia di rilevamento" diff --git a/features/rageshake/api/src/main/res/values-ro/translations.xml b/features/rageshake/api/src/main/res/values-ro/translations.xml index 2c89703deb..67a72e3d55 100644 --- a/features/rageshake/api/src/main/res/values-ro/translations.xml +++ b/features/rageshake/api/src/main/res/values-ro/translations.xml @@ -2,4 +2,6 @@ "%1$s s-a blocat ultima dată când a fost folosit. Doriți să ne trimiteți un raport?" "Se pare că scuturați telefonul de frustrare. Doriți să deschdeți ecranul de raportare a unei erori?" + "Rageshake" + "Prag de detecție" diff --git a/features/rageshake/api/src/main/res/values-ru/translations.xml b/features/rageshake/api/src/main/res/values-ru/translations.xml index d45ecb192d..24ba792de6 100644 --- a/features/rageshake/api/src/main/res/values-ru/translations.xml +++ b/features/rageshake/api/src/main/res/values-ru/translations.xml @@ -2,4 +2,6 @@ "При последнем использовании %1$s произошел сбой. Хотите поделиться отчетом о сбое?" "Похоже, что вы трясете телефон. Хотите открыть экран сообщения об ошибке?" + "Rageshake" + "Порог обнаружения" diff --git a/features/rageshake/api/src/main/res/values-sk/translations.xml b/features/rageshake/api/src/main/res/values-sk/translations.xml index c1d55eab34..69eca0738c 100644 --- a/features/rageshake/api/src/main/res/values-sk/translations.xml +++ b/features/rageshake/api/src/main/res/values-sk/translations.xml @@ -2,4 +2,6 @@ "%1$s zlyhal pri poslednom použití. Chcete zdieľať správu o páde s našim tímom?" "Zdá sa, že zúrivo trasiete telefónom. Chcete otvoriť obrazovku s hlásením chýb?" + "Zúrivé potrasenie" + "Prahová hodnota detekcie" diff --git a/features/rageshake/api/src/main/res/values/localazy.xml b/features/rageshake/api/src/main/res/values/localazy.xml index bb694f2d00..f2d86ab45b 100644 --- a/features/rageshake/api/src/main/res/values/localazy.xml +++ b/features/rageshake/api/src/main/res/values/localazy.xml @@ -2,4 +2,6 @@ "%1$s crashed the last time it was used. Would you like to share a crash report with us?" "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?" + "Rageshake" + "Detection threshold" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index a1fc6da91e..5d2b9be02c 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -226,8 +226,6 @@ "Otevřít v OpenStreetMap" "Sdílet tuto polohu" "Poloha" - "Rageshake" - "Práh detekce" "Verze: %1$s (%2$s)" "en" "Chyba" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index c652288a3b..aff9196601 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -194,8 +194,6 @@ "In OpenStreetMap öffnen" "Diesen Standort teilen" "Standort" - "Rageshake" - "Erkennungsschwelle" "Version: %1$s (%2$s)" "en" "Fehler" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 75948bd37f..dd5eecafc6 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -114,8 +114,6 @@ "Este es el principio de esta conversación." "Nuevos" "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" - "Agitar con fuerza" - "Umbral de detección" "Versión: %1$s (%2$s)" "es" "Error" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index 8974366164..aad840e226 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -221,8 +221,6 @@ "Ouvrir dans OpenStreetMap" "Partager cet position" "Position" - "Rageshake" - "Seuil de détection" "Version : %1$s ( %2$s )" "Ang." "Erreur" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 4d80e95faf..e73f8d4fc3 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -114,8 +114,6 @@ "Questo è l\'inizio della conversazione." "Nuovo" "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" - "Rageshake" - "Soglia di rilevamento" "Versione: %1$s (%2$s)" "it" "Errore" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 4cd74977b1..71c5099397 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -178,8 +178,6 @@ "Deschideți în OpenStreetMap" "Distribuiți această locație" "Locație" - "Rageshake" - "Prag de detecție" "Versiunea: %1$s (%2$s)" "ro" "Eroare" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 1fa960f397..74223dc736 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -228,8 +228,6 @@ "Открыть в OpenStreetMap" "Поделиться этим местоположением" "Местоположение" - "Rageshake" - "Порог обнаружения" "Версия: %1$s (%2$s)" "en" "Ошибка" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 56f59f21c4..b1da2e54c4 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -228,8 +228,6 @@ "Otvoriť v OpenStreetMap" "Zdieľajte túto polohu" "Poloha" - "Zúrivé potrasenie" - "Prahová hodnota detekcie" "Verzia: %1$s (%2$s)" "sk" "Chyba" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 41b765f118..bdf88c6f2d 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -225,8 +225,6 @@ "Open in OpenStreetMap" "Share this location" "Location" - "Rageshake" - "Detection threshold" "Version: %1$s (%2$s)" "en" "en" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 621274c6ff..63959482a6 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -10,7 +10,8 @@ "name": ":features:rageshake:api", "includeRegex": [ "crash_detection_.*", - "rageshake_detection_.*" + "rageshake_detection_.*", + "settings_rageshake.*" ] }, { From 22576524508edf35bb2fcb1262f759b04bff561d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 11:26:40 +0100 Subject: [PATCH 024/102] Localazy: move `leave_room_alert_` strings to the `:features:leaveroom:api` module. --- .../element/android/features/leaveroom/api/LeaveRoomView.kt | 6 +++--- .../leaveroom/api/src/main/res/values-cs/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-de/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-es/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-fr/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-it/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-ro/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-ru/translations.xml | 6 ++++++ .../leaveroom/api/src/main/res/values-sk/translations.xml | 6 ++++++ .../api/src/main/res/values-zh-rTW/translations.xml | 6 ++++++ features/leaveroom/api/src/main/res/values/localazy.xml | 6 ++++++ .../ui-strings/src/main/res/values-cs/translations.xml | 3 --- .../ui-strings/src/main/res/values-de/translations.xml | 3 --- .../ui-strings/src/main/res/values-es/translations.xml | 3 --- .../ui-strings/src/main/res/values-fr/translations.xml | 3 --- .../ui-strings/src/main/res/values-it/translations.xml | 3 --- .../ui-strings/src/main/res/values-ro/translations.xml | 3 --- .../ui-strings/src/main/res/values-ru/translations.xml | 3 --- .../ui-strings/src/main/res/values-sk/translations.xml | 3 --- .../ui-strings/src/main/res/values-zh-rTW/translations.xml | 3 --- libraries/ui-strings/src/main/res/values/localazy.xml | 3 --- tools/localazy/config.json | 6 ++++++ 22 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 features/leaveroom/api/src/main/res/values-cs/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-de/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-es/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-fr/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-it/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-ro/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-ru/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-sk/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values-zh-rTW/translations.xml create mode 100644 features/leaveroom/api/src/main/res/values/localazy.xml diff --git a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt index 82bae809e8..22b5fcfd03 100644 --- a/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt +++ b/features/leaveroom/api/src/main/kotlin/io/element/android/features/leaveroom/api/LeaveRoomView.kt @@ -48,19 +48,19 @@ private fun LeaveRoomConfirmationDialog( when (state.confirmation) { is LeaveRoomState.Confirmation.Hidden -> {} is LeaveRoomState.Confirmation.PrivateRoom -> LeaveRoomConfirmationDialog( - text = CommonStrings.leave_room_alert_private_subtitle, + text = R.string.leave_room_alert_private_subtitle, roomId = state.confirmation.roomId, eventSink = state.eventSink, ) is LeaveRoomState.Confirmation.LastUserInRoom -> LeaveRoomConfirmationDialog( - text = CommonStrings.leave_room_alert_empty_subtitle, + text = R.string.leave_room_alert_empty_subtitle, roomId = state.confirmation.roomId, eventSink = state.eventSink, ) is LeaveRoomState.Confirmation.Generic -> LeaveRoomConfirmationDialog( - text = CommonStrings.leave_room_alert_subtitle, + text = R.string.leave_room_alert_subtitle, roomId = state.confirmation.roomId, eventSink = state.eventSink, ) diff --git a/features/leaveroom/api/src/main/res/values-cs/translations.xml b/features/leaveroom/api/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..64195325c4 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-cs/translations.xml @@ -0,0 +1,6 @@ + + + "Opravdu chcete opustit tuto místnost? Jste tu jediná osoba. Pokud odejdete, nikdo se v budoucnu nebude moci připojit, včetně vás." + "Opravdu chcete opustit tuto místnost? Tato místnost není veřejná a bez pozvánky se nebudete moci znovu připojit." + "Opravdu chcete opustit místnost?" + diff --git a/features/leaveroom/api/src/main/res/values-de/translations.xml b/features/leaveroom/api/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..c691f57471 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-de/translations.xml @@ -0,0 +1,6 @@ + + + "Bist du sicher, dass du diesen Raum verlassen möchtest? Du bist die einzige Person hier. Wenn du austritst, kann in Zukunft niemand mehr eintreten, auch du nicht." + "Bist du sicher, dass du diesen Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne Einladung nicht erneut beitreten." + "Bist du sicher, dass du den Raum verlassen willst?" + diff --git a/features/leaveroom/api/src/main/res/values-es/translations.xml b/features/leaveroom/api/src/main/res/values-es/translations.xml new file mode 100644 index 0000000000..b996ec51f2 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-es/translations.xml @@ -0,0 +1,6 @@ + + + "¿Estás seguro de que quieres salir de esta sala? Eres la única persona aquí. Si te vas, nadie podrá unirse en el futuro, ni siquiera tú." + "¿Estás seguro de que quieres abandonar esta sala? Esta sala no es pública y no podrás volver a entrar sin una invitación." + "¿Seguro que quieres salir de la habitación?" + diff --git a/features/leaveroom/api/src/main/res/values-fr/translations.xml b/features/leaveroom/api/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..43ca62a678 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-fr/translations.xml @@ -0,0 +1,6 @@ + + + "Êtes-vous sûr de vouloir quitter ce salon ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra rejoindre le salon à l’avenir, y compris vous." + "Êtes-vous sûr de vouloir quitter ce salon ? Ce salon n’est pas public et vous ne pourrez pas le rejoindre sans invitation." + "Êtes-vous sûr de vouloir quitter le salon ?" + diff --git a/features/leaveroom/api/src/main/res/values-it/translations.xml b/features/leaveroom/api/src/main/res/values-it/translations.xml new file mode 100644 index 0000000000..60d481824a --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-it/translations.xml @@ -0,0 +1,6 @@ + + + "Sei sicuro di voler lasciare questa stanza? Sei l\'unica persona presente. Se esci, nessuno potrà unirsi in futuro, te compreso." + "Sei sicuro di voler lasciare questa stanza? Questa stanza non è pubblica e non potrai rientrare senza un invito." + "Sei sicuro di voler lasciare la stanza?" + diff --git a/features/leaveroom/api/src/main/res/values-ro/translations.xml b/features/leaveroom/api/src/main/res/values-ro/translations.xml new file mode 100644 index 0000000000..d736c701a6 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-ro/translations.xml @@ -0,0 +1,6 @@ + + + "Sunteți sigur că vreți să părăsiți această cameră? Sunteți singura persoană de aici. Dacă o părasiți, nimeni nu se va mai putea alătura în viitor, inclusiv dumneavoastra." + "Sunteți sigur că vrei să părăsiți această cameră? Această cameră nu este publică și nu va veti putea alătura din nou fără o invitație." + "Sunteți sigur că vreți să părăsiți camera?" + diff --git a/features/leaveroom/api/src/main/res/values-ru/translations.xml b/features/leaveroom/api/src/main/res/values-ru/translations.xml new file mode 100644 index 0000000000..5915518c80 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-ru/translations.xml @@ -0,0 +1,6 @@ + + + "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас." + "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения." + "Вы уверены, что хотите покинуть комнату?" + diff --git a/features/leaveroom/api/src/main/res/values-sk/translations.xml b/features/leaveroom/api/src/main/res/values-sk/translations.xml new file mode 100644 index 0000000000..c67cb131e7 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-sk/translations.xml @@ -0,0 +1,6 @@ + + + "Ste si istí, že chcete opustiť túto miestnosť? Ste tu jediná osoba. Ak odídete, nikto sa do nej nebude môcť v budúcnosti pripojiť, vrátane vás." + "Ste si istí, že chcete opustiť túto miestnosť? Táto miestnosť nie je verejná a bez pozvania sa do nej nebudete môcť vrátiť." + "Ste si istí, že chcete opustiť miestnosť?" + diff --git a/features/leaveroom/api/src/main/res/values-zh-rTW/translations.xml b/features/leaveroom/api/src/main/res/values-zh-rTW/translations.xml new file mode 100644 index 0000000000..7b1b55acf6 --- /dev/null +++ b/features/leaveroom/api/src/main/res/values-zh-rTW/translations.xml @@ -0,0 +1,6 @@ + + + "您確定要離開聊天室嗎?這裡只有您一個人。如果您離開了,包含您在內的所有人都無法再進入此聊天室。" + "您確定要離開聊天室嗎?此聊天室不是公開的,如果沒有收到邀請,您無法重新加入。" + "您確定要離開聊天室嗎?" + diff --git a/features/leaveroom/api/src/main/res/values/localazy.xml b/features/leaveroom/api/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..262369c9be --- /dev/null +++ b/features/leaveroom/api/src/main/res/values/localazy.xml @@ -0,0 +1,6 @@ + + + "Are you sure that you want to leave this room? You\'re the only person here. If you leave, no one will be able to join in the future, including you." + "Are you sure that you want to leave this room? This room is not public and you won\'t be able to rejoin without an invite." + "Are you sure that you want to leave the room?" + diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 5d2b9be02c..89141c9d4d 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -190,9 +190,6 @@ "Omlouváme se, došlo k chybě" "🔐️ Připojte se ke mně na %1$s" "Ahoj, ozvi se mi na %1$s: %2$s" - "Opravdu chcete opustit tuto místnost? Jste tu jediná osoba. Pokud odejdete, nikdo se v budoucnu nebude moci připojit, včetně vás." - "Opravdu chcete opustit tuto místnost? Tato místnost není veřejná a bez pozvánky se nebudete moci znovu připojit." - "Opravdu chcete opustit místnost?" "%1$s Android" "zadána %1$d číslice" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index aff9196601..3a4f32c3cd 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -165,9 +165,6 @@ "Entschuldigung, es ist ein Fehler aufgetreten" "🔐️ Begleite mich auf %1$s" "Hey, sprich mit mir auf %1$s: %2$s" - "Bist du sicher, dass du diesen Raum verlassen möchtest? Du bist die einzige Person hier. Wenn du austritst, kann in Zukunft niemand mehr eintreten, auch du nicht." - "Bist du sicher, dass du diesen Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne Einladung nicht erneut beitreten." - "Bist du sicher, dass du den Raum verlassen willst?" "%1$s Android" "%1$d Mitglied" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index dd5eecafc6..595d59f295 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -99,9 +99,6 @@ "Algunos mensajes no se han enviado" "Lo siento, se ha producido un error" "Hola, puedes hablar conmigo en %1$s: %2$s" - "¿Estás seguro de que quieres salir de esta sala? Eres la única persona aquí. Si te vas, nadie podrá unirse en el futuro, ni siquiera tú." - "¿Estás seguro de que quieres abandonar esta sala? Esta sala no es pública y no podrás volver a entrar sin una invitación." - "¿Seguro que quieres salir de la habitación?" "%1$s Android" "%1$d miembro" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index aad840e226..9cad4513d8 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -188,9 +188,6 @@ "Désolé, une erreur s’est produite" "🔐️ Rejoignez-moi sur %1$s" "Salut, parle-moi sur %1$s : %2$s" - "Êtes-vous sûr de vouloir quitter ce salon ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra rejoindre le salon à l’avenir, y compris vous." - "Êtes-vous sûr de vouloir quitter ce salon ? Ce salon n’est pas public et vous ne pourrez pas le rejoindre sans invitation." - "Êtes-vous sûr de vouloir quitter le salon ?" "%1$s Android" "%1$d chiffre saisi" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index e73f8d4fc3..9fe0d0b900 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -99,9 +99,6 @@ "Alcuni messaggi non sono stati inviati" "Siamo spiacenti, si è verificato un errore" "Ehi, parlami su %1$s: %2$s" - "Sei sicuro di voler lasciare questa stanza? Sei l\'unica persona presente. Se esci, nessuno potrà unirsi in futuro, te compreso." - "Sei sicuro di voler lasciare questa stanza? Questa stanza non è pubblica e non potrai rientrare senza un invito." - "Sei sicuro di voler lasciare la stanza?" "%1$s Android" "%1$d membro" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 71c5099397..09a6392bc6 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -147,9 +147,6 @@ "Ne pare rău, a apărut o eroare" "🔐️ Alăturați-vă mie pe %1$s" "Hei, vorbește cu mine pe %1$s: %2$s" - "Sunteți sigur că vreți să părăsiți această cameră? Sunteți singura persoană de aici. Dacă o părasiți, nimeni nu se va mai putea alătura în viitor, inclusiv dumneavoastra." - "Sunteți sigur că vrei să părăsiți această cameră? Această cameră nu este publică și nu va veti putea alătura din nou fără o invitație." - "Sunteți sigur că vreți să părăsiți camera?" "%1$s Android" "%1$d membru" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 74223dc736..e8c2780e26 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -192,9 +192,6 @@ "Извините, произошла ошибка" "🔐️ Присоединяйтесь ко мне в %1$s" "Привет, поговори со мной по %1$s: %2$s" - "Вы уверены, что хотите покинуть эту комнату? Вы здесь единственный человек. Если вы уйдете, никто не сможет присоединиться в будущем, включая вас." - "Вы уверены, что хотите покинуть эту комнату? Эта комната не является публичной, и Вы не сможете присоединиться к ней без приглашения." - "Вы уверены, что хотите покинуть комнату?" "%1$s Android" "Введена цифра %1$d" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index b1da2e54c4..abfc25093b 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -192,9 +192,6 @@ "Prepáčte, vyskytla sa chyba" "🔐️ Pripojte sa ku mne na %1$s" "Ahoj, porozprávajte sa so mnou na %1$s: %2$s" - "Ste si istí, že chcete opustiť túto miestnosť? Ste tu jediná osoba. Ak odídete, nikto sa do nej nebude môcť v budúcnosti pripojiť, vrátane vás." - "Ste si istí, že chcete opustiť túto miestnosť? Táto miestnosť nie je verejná a bez pozvania sa do nej nebudete môcť vrátiť." - "Ste si istí, že chcete opustiť miestnosť?" "%1$s Android" "%1$d zadaná číslica" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 4399a4e305..6854367532 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -160,9 +160,6 @@ "%1$s 沒有權限存取您的位置。請在下方開啟權限。" "有些訊息尚未傳送" "嘿,來 %1$s 和我聊天:%2$s" - "您確定要離開聊天室嗎?這裡只有您一個人。如果您離開了,包含您在內的所有人都無法再進入此聊天室。" - "您確定要離開聊天室嗎?此聊天室不是公開的,如果沒有收到邀請,您無法重新加入。" - "您確定要離開聊天室嗎?" "%1$s Android" "%1$d 位成員" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index bdf88c6f2d..17bbe93bca 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -192,9 +192,6 @@ "Sorry, an error occurred" "🔐️ Join me on %1$s" "Hey, talk to me on %1$s: %2$s" - "Are you sure that you want to leave this room? You\'re the only person here. If you leave, no one will be able to join in the future, including you." - "Are you sure that you want to leave this room? This room is not public and you won\'t be able to rejoin without an invite." - "Are you sure that you want to leave the room?" "%1$s Android" "%1$d digit entered" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 63959482a6..23fc18a5ef 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -94,6 +94,12 @@ "screen_waitlist_.*" ] }, + { + "name": ":features:leaveroom:api", + "includeRegex": [ + "leave_room_alert_.*" + ] + }, { "name": ":features:roomlist:impl", "includeRegex": [ From 4f146d7a1cbd9c040535be9136511ae00a9ded5c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 11:37:21 +0100 Subject: [PATCH 025/102] Localazy: move `report_content` strings to the `:features:messages:impl` module. --- .../features/messages/impl/report/ReportMessageView.kt | 9 +++++---- .../impl/src/main/res/values-cs/translations.xml | 4 ++++ .../impl/src/main/res/values-de/translations.xml | 4 ++++ .../impl/src/main/res/values-es/translations.xml | 4 ++++ .../impl/src/main/res/values-fr/translations.xml | 4 ++++ .../impl/src/main/res/values-it/translations.xml | 4 ++++ .../impl/src/main/res/values-ro/translations.xml | 4 ++++ .../impl/src/main/res/values-ru/translations.xml | 4 ++++ .../impl/src/main/res/values-sk/translations.xml | 4 ++++ .../impl/src/main/res/values-zh-rTW/translations.xml | 2 ++ features/messages/impl/src/main/res/values/localazy.xml | 4 ++++ .../ui-strings/src/main/res/values-cs/translations.xml | 4 ---- .../ui-strings/src/main/res/values-de/translations.xml | 4 ---- .../ui-strings/src/main/res/values-es/translations.xml | 4 ---- .../ui-strings/src/main/res/values-fr/translations.xml | 4 ---- .../ui-strings/src/main/res/values-it/translations.xml | 4 ---- .../ui-strings/src/main/res/values-ro/translations.xml | 4 ---- .../ui-strings/src/main/res/values-ru/translations.xml | 4 ---- .../ui-strings/src/main/res/values-sk/translations.xml | 4 ---- .../src/main/res/values-zh-rTW/translations.xml | 2 -- libraries/ui-strings/src/main/res/values/localazy.xml | 4 ---- tools/localazy/config.json | 3 ++- 22 files changed, 45 insertions(+), 43 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt index d75c8f45c6..f4b152ecab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/report/ReportMessageView.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.features.messages.impl.R import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.async.AsyncView import io.element.android.libraries.designsystem.components.button.BackButton @@ -101,14 +102,14 @@ fun ReportMessageView( OutlinedTextField( value = state.reason, onValueChange = { state.eventSink(ReportMessageEvents.UpdateReason(it)) }, - placeholder = { Text(stringResource(CommonStrings.report_content_hint)) }, + placeholder = { Text(stringResource(R.string.report_content_hint)) }, enabled = !isSending, modifier = Modifier .fillMaxWidth() .heightIn(min = 90.dp) ) Text( - text = stringResource(CommonStrings.report_content_explanation), + text = stringResource(R.string.report_content_explanation), style = ElementTheme.typography.fontBodySmRegular, color = MaterialTheme.colorScheme.secondary, textAlign = TextAlign.Start, @@ -122,11 +123,11 @@ fun ReportMessageView( ) { Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(4.dp)) { Text( - text = stringResource(CommonStrings.screen_report_content_block_user), + text = stringResource(R.string.screen_report_content_block_user), style = ElementTheme.typography.fontBodyLgRegular, ) Text( - text = stringResource(CommonStrings.screen_report_content_block_user_hint), + text = stringResource(R.string.screen_report_content_block_user_hint), style = ElementTheme.typography.fontBodyMdRegular, color = MaterialTheme.colorScheme.secondary, ) diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 09c0aa4929..a59dcdca59 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -13,7 +13,10 @@ "%1$d změny místnosti" "%1$d změn místnosti" + "Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy." + "Důvod nahlášení tohoto obsahu" "Informujte celou místnost" + "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Fotoaparát" "Vyfotit" "Natočit video" @@ -49,6 +52,7 @@ "Zobrazit méně" "Držte pro nahrávání" "Všichni" + "Zablokovat uživatele" "Nahrání média se nezdařilo, zkuste to prosím znovu." "Pouze zmínky a klíčová slova" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index c561551060..cb446ff6b4 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -12,6 +12,9 @@ "%1$d Raumänderung" "%1$d Raumänderungen" + "Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen." + "Grund für die Meldung dieses Inhalts" + "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Kamera" "Foto machen" "Video aufnehmen" @@ -44,6 +47,7 @@ "Deine Nachricht konnte nicht gesendet werden" "Emoji hinzufügen" "Weniger anzeigen" + "Benutzer sperren" "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Nur Erwähnungen und Schlüsselwörter" diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index f411e6152f..6a0b285638 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -12,4 +12,8 @@ "%1$d cambio en la sala" "%1$d cambios en la sala" + "Este mensaje se notificará al administrador de su homeserver. No podrán leer ningún mensaje cifrado." + "Motivo para denunciar este contenido" + "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" + "Bloquear usuario" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 13376219da..8c3babbcf8 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -12,7 +12,10 @@ "%1$d changement dans le salon" "%1$d changements dans le salon" + "Ce message sera signalé à l’administrateur de votre serveur d’accueil. Il ne pourra lire aucun message chiffré." + "Raison du signalement de ce contenu" "Notifier tout le salon" + "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Appareil photo" "Prendre une photo" "Enregistrer une vidéo" @@ -48,6 +51,7 @@ "Afficher moins" "Maintenir pour enregistrer" "Tout le monde" + "Bloquer l’utilisateur" "Échec du traitement des médias à télécharger, veuillez réessayer." "Mentions et mots clés uniquement" diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml index 295be40922..735ecbe913 100644 --- a/features/messages/impl/src/main/res/values-it/translations.xml +++ b/features/messages/impl/src/main/res/values-it/translations.xml @@ -12,4 +12,8 @@ "%1$d modifica alla stanza" "%1$d modifiche alla stanza" + "Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati." + "Motivo della segnalazione di questo contenuto" + "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" + "Blocca utente" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index d096387b16..b23e524d77 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -13,6 +13,9 @@ "%1$d schimbări ale camerei" "%1$d schimbări ale camerei" + "Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat." + "Motivul raportării acestui conținut" + "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Cameră foto" "Faceți o fotografie" "Înregistrați un videoclip" @@ -45,6 +48,7 @@ "Mesajul dvs. nu a putut fi trimis" "Adăugați emoji" "Afișați mai puțin" + "Blocați utilizatorul" "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Numai mențiuni și cuvinte cheie" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 881c8c929f..7fc6a14494 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -13,7 +13,10 @@ "%1$d изменения в комнате" "%1$d изменений в комнате" + "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." + "Причина, по которой вы пожаловались на этот контент" "Уведомить всю комнату" + "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Камера" "Сделать фото" "Записать видео" @@ -49,6 +52,7 @@ "Показать меньше" "Удерживайте для записи" "Для всех" + "Заблокировать пользователя" "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Только упоминания и ключевые слова" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index b0f20c938d..8c20bcf252 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -13,7 +13,10 @@ "%1$d zmeny miestnosti" "%1$d zmien miestnosti" + "Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy." + "Dôvod nahlásenia tohto obsahu" "Informovať celú miestnosť" + "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Kamera" "Odfotiť" "Nahrať video" @@ -49,6 +52,7 @@ "Zobraziť menej" "Podržaním nahrajte" "Všetci" + "Zablokovať používateľa" "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Iba zmienky a kľúčové slová" diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml index 945d815ce6..9eb7664c47 100644 --- a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml @@ -11,6 +11,7 @@ "%1$d 個聊天室變更" + "檢舉這個內容的原因" "照相機" "拍照" "錄影" @@ -33,5 +34,6 @@ "無法傳送您的訊息" "新增表情符號" "較少" + "封鎖使用者" "僅限提及與關鍵字" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index cbf6c3ec16..cbc5f127e9 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -12,7 +12,10 @@ "%1$d room change" "%1$d room changes" + "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." + "Reason for reporting this content" "Notify the whole room" + "Check if you want to hide all current and future messages from this user" "Camera" "Take photo" "Record video" @@ -48,6 +51,7 @@ "Show less" "Hold to record" "Everyone" + "Block user" "Failed processing media to upload, please try again." "Mentions and Keywords only" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 89141c9d4d..ddb7b09671 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -207,15 +207,12 @@ "%d hlasů" "Zatřeste zařízením pro nahlášení chyby" - "Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy." - "Důvod nahlášení tohoto obsahu" "Toto je začátek %1$s." "Toto je začátek této konverzace." "Nové" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." - "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Sdílet polohu" "Sdílet moji polohu" "Otevřít v Mapách Apple" @@ -227,5 +224,4 @@ "en" "Chyba" "Úspěch" - "Zablokovat uživatele" diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 3a4f32c3cd..2fa0c20ff5 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -175,15 +175,12 @@ "%d Stimmen" "Schüttel heftig zum Melden von Fehlern" - "Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen." - "Grund für die Meldung dieses Inhalts" "Dies ist der Anfang von %1$s." "Dies ist der Anfang dieses Gesprächs." "Neu" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." - "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Standort teilen" "Meinen Standort teilen" "In Apple Maps öffnen" @@ -195,5 +192,4 @@ "en" "Fehler" "Erfolg" - "Benutzer sperren" diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 595d59f295..f901b48cb3 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -105,15 +105,11 @@ "%1$d miembros" "Agitar con fuerza para informar de un error" - "Este mensaje se notificará al administrador de su homeserver. No podrán leer ningún mensaje cifrado." - "Motivo para denunciar este contenido" "Este es el principio de %1$s." "Este es el principio de esta conversación." "Nuevos" - "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" "Versión: %1$s (%2$s)" "es" "Error" "Terminado" - "Bloquear usuario" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index 9cad4513d8..cc0adae38d 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -202,15 +202,12 @@ "%d votes" "Rageshake pour signaler un problème" - "Ce message sera signalé à l’administrateur de votre serveur d’accueil. Il ne pourra lire aucun message chiffré." - "Raison du signalement de ce contenu" "Ceci est le début de %1$s." "Ceci est le début de cette conversation." "Nouveau" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." - "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Partage de position" "Partager ma position" "Ouvrir dans Apple Maps" @@ -222,5 +219,4 @@ "Ang." "Erreur" "Succès" - "Bloquer l’utilisateur" diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 9fe0d0b900..e8e35c284e 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -105,15 +105,11 @@ "%1$d membri" "Scuoti per segnalare un problema" - "Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati." - "Motivo della segnalazione di questo contenuto" "Questo è l\'inizio di %1$s." "Questo è l\'inizio della conversazione." "Nuovo" - "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" "Versione: %1$s (%2$s)" "it" "Errore" "Operazione riuscita" - "Blocca utente" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 09a6392bc6..863d4cd097 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -159,15 +159,12 @@ "%d voturi" "Rageshake pentru a raporta erori" - "Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat." - "Motivul raportării acestui conținut" "Acesta este începutul conversației %1$s." "Acesta este începutul acestei conversații." "Nou" "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Încărcarea fișierelor media a eșuat, încercați din nou." - "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Partajați locația" "Distribuiți locația mea" "Deschideți în Apple Maps" @@ -179,5 +176,4 @@ "ro" "Eroare" "Succes" - "Blocați utilizatorul" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index e8c2780e26..87f4d74201 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -209,15 +209,12 @@ "%d голосов" "Rageshake сообщит об ошибке" - "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." - "Причина, по которой вы пожаловались на этот контент" "Это начало %1$s." "Это начало разговора." "Новый" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." - "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Поделиться местоположением" "Поделиться моим местоположением" "Открыть в Apple Maps" @@ -229,5 +226,4 @@ "en" "Ошибка" "Успешно" - "Заблокировать пользователя" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index abfc25093b..4f38c2fb8b 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -209,15 +209,12 @@ "%d hlasov" "Zúrivo potriasť pre nahlásenie chyby" - "Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy." - "Dôvod nahlásenia tohto obsahu" "Toto je začiatok %1$s." "Toto je začiatok tejto konverzácie." "Nové" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." - "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Zdieľať polohu" "Zdieľať moju polohu" "Otvoriť v Apple Maps" @@ -229,5 +226,4 @@ "sk" "Chyba" "Úspech" - "Zablokovať používateľa" diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index 6854367532..d86a2b0265 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -167,7 +167,6 @@ "%d 票" - "檢舉這個內容的原因" "新訊息" "無法上傳媒體檔案,請稍後再試。" "分享位置" @@ -181,5 +180,4 @@ "zh-tw" "錯誤" "成功" - "封鎖使用者" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 17bbe93bca..156b5e5b17 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -206,15 +206,12 @@ "%d votes" "Rageshake to report bug" - "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." - "Reason for reporting this content" "This is the beginning of %1$s." "This is the beginning of this conversation." "New" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." - "Check if you want to hide all current and future messages from this user" "Share location" "Share my location" "Open in Apple Maps" @@ -227,5 +224,4 @@ "en" "Error" "Success" - "Block user" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 23fc18a5ef..e95892ac52 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -125,7 +125,8 @@ "screen\\.room\\..*", "screen_dm_details_.*", "room_timeline_state_changes", - "emoji_picker_category_.*" + "emoji_picker_category_.*", + ".*report_content_.*" ], "excludeRegex": [ "screen_room_details_.*", From 74c13a7f791202d7b55859217b19cbba51a62c78 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 14:11:08 +0100 Subject: [PATCH 026/102] Cleanup --- .../UserDefinedRoomNotificationSettingsView.kt | 1 - .../features/roomlist/impl/components/RoomSummaryRow.kt | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index 937422fd5d..f66c5d8b9b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -41,7 +41,6 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.ui.strings.CommonStrings @Composable fun UserDefinedRoomNotificationSettingsView( diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt index 44587b0f95..a1fa410c7f 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt @@ -36,7 +36,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow @@ -47,8 +46,8 @@ import io.element.android.features.roomlist.impl.model.RoomListRoomSummaryProvid import io.element.android.libraries.core.extensions.orEmpty import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom import io.element.android.libraries.designsystem.components.avatar.Avatar -import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.roomListRoomMessage @@ -58,7 +57,6 @@ import io.element.android.libraries.designsystem.theme.unreadIndicator import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.room.RoomNotificationMode import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.ui.strings.CommonStrings internal val minHeight = 84.dp From 6cb4a9af6d4c36287c3b62ee30a023bace1b6380 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 14:28:15 +0100 Subject: [PATCH 027/102] Fix rendering of emote when the message contains some formatting. --- .../factories/event/TimelineItemContentMessageFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index d15a07526c..bf4ba27bd2 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -61,7 +61,7 @@ class TimelineItemContentMessageFactory @Inject constructor( return when (val messageType = content.type) { is EmoteMessageType -> TimelineItemEmoteContent( body = "* $senderDisplayName ${messageType.body}", - htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* senderDisplayName"), + htmlDocument = messageType.formatted?.toHtmlDocument(prefix = "* $senderDisplayName"), isEdited = content.isEdited, ) is ImageMessageType -> { From 0b1d41e861f872ff3928d6e91e0dbec9046386ab Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Tue, 14 Nov 2023 14:05:59 +0000 Subject: [PATCH 028/102] Update voice message recording button behaviour (#1784) Changes recording button behaviour so that - tapping the record button starts a recording and displays the stop button - tapping the stop button stops the recording - tapping the delete button cancels the recording - 'hold to record' tooltip is removed --------- Co-authored-by: ElementBot --- changelog.d/1784.feature | 1 + .../messagecomposer/MessageComposerView.kt | 8 +- .../composer/VoiceMessageComposerEvents.kt | 6 +- .../composer/VoiceMessageComposerPresenter.kt | 18 +- .../VoiceMessageComposerPresenterTest.kt | 84 ++++---- .../src/main/res/drawable/ic_pause.xml | 0 .../src/main/res/drawable/ic_play.xml | 0 .../src/main/res/drawable/ic_stop.xml | 9 + .../libraries/textcomposer/TextComposer.kt | 25 ++- .../textcomposer/components/RecordButton.kt | 189 ------------------ .../components/VoiceMessagePreview.kt | 6 +- .../components/VoiceMessageRecorderButton.kt | 119 +++++++++++ ...sEvent.kt => VoiceMessageRecorderEvent.kt} | 8 +- .../textcomposer/utils/PressState.kt | 31 --- .../textcomposer/utils/PressStateEffects.kt | 47 ----- .../textcomposer/utils/PressStateHolder.kt | 101 ---------- .../utils/PressStateHolderTest.kt | 111 ---------- ...ewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png | 4 +- ...Voice-Night-6_7_null_0,NEXUS_5,1.0,en].png | 4 +- ...Tooltip-Day-13_13_null,NEXUS_5,1.0,en].png | 3 - ...oltip-Night-13_14_null,NEXUS_5,1.0,en].png | 3 - ...dButton-Day-12_12_null,NEXUS_5,1.0,en].png | 3 - ...utton-Night-12_13_null,NEXUS_5,1.0,en].png | 3 - ...Button-Day-12_12_null,NEXUS_5,1.0,en].png} | 0 ...tton-Night-12_13_null,NEXUS_5,1.0,en].png} | 0 ...atting-Day-13_13_null,NEXUS_5,1.0,en].png} | 0 ...ting-Night-13_14_null,NEXUS_5,1.0,en].png} | 0 ...Button-Day-14_14_null,NEXUS_5,1.0,en].png} | 0 ...tton-Night-14_15_null,NEXUS_5,1.0,en].png} | 0 ...review-Day-15_15_null,NEXUS_5,1.0,en].png} | 0 ...view-Night-15_16_null,NEXUS_5,1.0,en].png} | 0 ...rButton-Day-16_16_null,NEXUS_5,1.0,en].png | 3 + ...utton-Night-16_17_null,NEXUS_5,1.0,en].png | 3 + ...ording-Day-17_17_null,NEXUS_5,1.0,en].png} | 0 ...ding-Night-17_18_null,NEXUS_5,1.0,en].png} | 0 ...oserVoice-Day-4_4_null,NEXUS_5,1.0,en].png | 4 +- ...erVoice-Night-4_5_null,NEXUS_5,1.0,en].png | 4 +- 37 files changed, 221 insertions(+), 576 deletions(-) create mode 100644 changelog.d/1784.feature rename libraries/{textcomposer/impl => designsystem}/src/main/res/drawable/ic_pause.xml (100%) rename libraries/{textcomposer/impl => designsystem}/src/main/res/drawable/ic_play.xml (100%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_stop.xml delete mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt create mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt rename libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/{PressEvent.kt => VoiceMessageRecorderEvent.kt} (77%) delete mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt delete mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt delete mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt delete mode 100644 libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Day-13_13_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Night-13_14_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Day-12_12_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Night-12_13_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-14_14_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-14_15_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-15_15_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_13_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-15_16_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_14_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-16_16_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-16_17_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-17_17_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_15_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-17_18_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_16_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-18_18_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_17_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-18_19_null,NEXUS_5,1.0,en].png => ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_18_null,NEXUS_5,1.0,en].png} (100%) diff --git a/changelog.d/1784.feature b/changelog.d/1784.feature new file mode 100644 index 0000000000..8613d7c798 --- /dev/null +++ b/changelog.d/1784.feature @@ -0,0 +1 @@ +Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt index e34a22eedc..b988d545a6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt @@ -32,7 +32,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.textcomposer.TextComposer import io.element.android.libraries.textcomposer.model.Message -import io.element.android.libraries.textcomposer.model.PressEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import kotlinx.coroutines.launch @@ -77,8 +77,8 @@ internal fun MessageComposerView( } } - val onVoiceRecordButtonEvent = { press: PressEvent -> - voiceMessageState.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(press)) + val onVoiceRecorderEvent = { press: VoiceMessageRecorderEvent -> + voiceMessageState.eventSink(VoiceMessageComposerEvents.RecorderEvent(press)) } val onSendVoiceMessage = { @@ -107,7 +107,7 @@ internal fun MessageComposerView( onDismissTextFormatting = ::onDismissTextFormatting, enableTextFormatting = enableTextFormatting, enableVoiceMessages = enableVoiceMessages, - onVoiceRecordButtonEvent = onVoiceRecordButtonEvent, + onVoiceRecorderEvent = onVoiceRecorderEvent, onVoicePlayerEvent = onVoicePlayerEvent, onSendVoiceMessage = onSendVoiceMessage, onDeleteVoiceMessage = onDeleteVoiceMessage, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt index f80ee15d95..0d384185df 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt @@ -17,12 +17,12 @@ package io.element.android.features.messages.impl.voicemessages.composer import androidx.lifecycle.Lifecycle -import io.element.android.libraries.textcomposer.model.PressEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent sealed interface VoiceMessageComposerEvents { - data class RecordButtonEvent( - val pressEvent: PressEvent + data class RecorderEvent( + val recorderEvent: VoiceMessageRecorderEvent ): VoiceMessageComposerEvents data class PlayerEvent( val playerEvent: VoiceMessagePlayerEvent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt index d39b065e5a..cd54dc5588 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -37,7 +37,7 @@ import io.element.android.libraries.di.SingleIn import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsPresenter -import io.element.android.libraries.textcomposer.model.PressEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.voicerecorder.api.VoiceRecorder @@ -95,10 +95,10 @@ class VoiceMessageComposerPresenter @Inject constructor( } } - val onRecordButtonPress = { event: VoiceMessageComposerEvents.RecordButtonEvent -> + val onVoiceMessageRecorderEvent = { event: VoiceMessageComposerEvents.RecorderEvent -> val permissionGranted = permissionState.permissionGranted - when (event.pressEvent) { - PressEvent.PressStart -> { + when (event.recorderEvent) { + VoiceMessageRecorderEvent.Start -> { Timber.v("Voice message record button pressed") when { permissionGranted -> { @@ -110,12 +110,12 @@ class VoiceMessageComposerPresenter @Inject constructor( } } } - PressEvent.LongPressEnd -> { - Timber.v("Voice message record button released") + VoiceMessageRecorderEvent.Stop -> { + Timber.v("Voice message stop button pressed") localCoroutineScope.finishRecording() } - PressEvent.Tapped -> { - Timber.v("Voice message record button tapped") + VoiceMessageRecorderEvent.Cancel -> { + Timber.v("Voice message cancel button tapped") localCoroutineScope.cancelRecording() } } @@ -163,7 +163,7 @@ class VoiceMessageComposerPresenter @Inject constructor( val handleEvents: (VoiceMessageComposerEvents) -> Unit = { event -> when (event) { - is VoiceMessageComposerEvents.RecordButtonEvent -> onRecordButtonPress(event) + is VoiceMessageComposerEvents.RecorderEvent -> onVoiceMessageRecorderEvent(event) is VoiceMessageComposerEvents.PlayerEvent -> onPlayerEvent(event.playerEvent) is VoiceMessageComposerEvents.SendVoiceMessage -> localCoroutineScope.launch { onSendButtonPress() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 1a6ff988db..8fe1048c3c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -44,7 +44,7 @@ import io.element.android.libraries.permissions.api.aPermissionsState import io.element.android.libraries.permissions.test.FakePermissionsPresenter import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.textcomposer.model.PressEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.voicerecorder.test.FakeVoiceRecorder @@ -99,7 +99,7 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE) @@ -116,13 +116,13 @@ class VoiceMessageComposerPresenterTest { presenter.present() }.test { awaitItem().apply { - eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) assertThat(keepScreenOn).isFalse() } awaitItem().apply { assertThat(keepScreenOn).isTrue() - eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) } val finalState = awaitItem().apply { @@ -139,13 +139,11 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.Tapped)) - + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Cancel)) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 1) - testPauseAndDestroy(finalState) } } @@ -156,8 +154,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState()) @@ -173,7 +171,7 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) val finalState = awaitItem().apply { this.eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) } @@ -192,8 +190,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) val finalState = awaitItem().also { assertThat(it.voiceMessageState).isEqualTo(aPlayingState()) @@ -210,8 +208,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Pause)) val finalState = awaitItem().also { @@ -229,8 +227,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Seek(0.5f))) awaitItem().apply { assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.5f, time = 0.seconds, showCursor = true)) @@ -256,8 +254,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.DeleteVoiceMessage) val finalState = awaitItem() @@ -274,8 +272,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.DeleteVoiceMessage) awaitItem().apply { @@ -296,8 +294,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) @@ -318,15 +316,15 @@ class VoiceMessageComposerPresenterTest { }.test { // Send a normal voice message messageComposerContext.composerMode = MessageComposerMode.Normal - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) skipItems(1) // Sending state // Now reply with a voice message messageComposerContext.composerMode = aReplyMode() - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) val finalState = awaitItem() // Sending state @@ -345,8 +343,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) assertThat(awaitItem().voiceMessageState).isEqualTo(aPlayingState().toSendingState()) @@ -367,8 +365,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().run { eventSink(VoiceMessageComposerEvents.SendVoiceMessage) eventSink(VoiceMessageComposerEvents.SendVoiceMessage) @@ -392,8 +390,8 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().apply { assertThat(voiceMessageState).isEqualTo(aPreviewState()) eventSink(VoiceMessageComposerEvents.SendVoiceMessage) @@ -417,8 +415,8 @@ class VoiceMessageComposerPresenterTest { presenter.present() }.test { mediaPreProcessor.givenResult(Result.failure(Exception())) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) val previewState = awaitItem() previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) @@ -467,7 +465,7 @@ class VoiceMessageComposerPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) assertThat(matrixRoom.sendMediaCount).isEqualTo(0) assertThat(analyticsService.trackedErrors).containsExactly( @@ -491,15 +489,15 @@ class VoiceMessageComposerPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) assertThat(awaitItem().voiceMessageState).isEqualTo(VoiceMessageState.Idle) - initialState.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + initialState.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) voiceRecorder.assertCalls(stopped = 1) permissionsPresenter.setPermissionGranted() - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE) voiceRecorder.assertCalls(stopped = 1, started = 1) @@ -519,7 +517,7 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) // See the dialog and accept it awaitItem().also { @@ -533,7 +531,7 @@ class VoiceMessageComposerPresenterTest { permissionsPresenter.setPermissionGranted() - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE) voiceRecorder.assertCalls(started = 1) @@ -553,7 +551,7 @@ class VoiceMessageComposerPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) // See the dialog and accept it awaitItem().also { @@ -565,7 +563,7 @@ class VoiceMessageComposerPresenterTest { // Dialog is hidden, user tries to record again awaitItem().also { assertThat(it.showPermissionRationaleDialog).isFalse() - it.eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + it.eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) } // Dialog is shown once again diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_pause.xml b/libraries/designsystem/src/main/res/drawable/ic_pause.xml similarity index 100% rename from libraries/textcomposer/impl/src/main/res/drawable/ic_pause.xml rename to libraries/designsystem/src/main/res/drawable/ic_pause.xml diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_play.xml b/libraries/designsystem/src/main/res/drawable/ic_play.xml similarity index 100% rename from libraries/textcomposer/impl/src/main/res/drawable/ic_play.xml rename to libraries/designsystem/src/main/res/drawable/ic_play.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_stop.xml b/libraries/designsystem/src/main/res/drawable/ic_stop.xml new file mode 100644 index 0000000000..e4cd1507bb --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,9 @@ + + + diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index fef9d40a5b..9f02254b4d 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -66,7 +66,7 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.textcomposer.components.ComposerOptionsButton import io.element.android.libraries.textcomposer.components.DismissTextFormattingButton -import io.element.android.libraries.textcomposer.components.RecordButton +import io.element.android.libraries.textcomposer.components.VoiceMessageRecorderButton import io.element.android.libraries.textcomposer.components.SendButton import io.element.android.libraries.textcomposer.components.TextFormatting import io.element.android.libraries.textcomposer.components.VoiceMessageDeleteButton @@ -75,7 +75,7 @@ import io.element.android.libraries.textcomposer.components.VoiceMessageRecordin import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape import io.element.android.libraries.textcomposer.model.Message import io.element.android.libraries.textcomposer.model.MessageComposerMode -import io.element.android.libraries.textcomposer.model.PressEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState @@ -103,7 +103,7 @@ fun TextComposer( onResetComposerMode: () -> Unit = {}, onAddAttachment: () -> Unit = {}, onDismissTextFormatting: () -> Unit = {}, - onVoiceRecordButtonEvent: (PressEvent) -> Unit = {}, + onVoiceRecorderEvent: (VoiceMessageRecorderEvent) -> Unit = {}, onVoicePlayerEvent: (VoiceMessagePlayerEvent) -> Unit = {}, onSendVoiceMessage: () -> Unit = {}, onDeleteVoiceMessage: () -> Unit = {}, @@ -167,16 +167,15 @@ fun TextComposer( ) } val recordVoiceButton = @Composable { - RecordButton( - onPressStart = { onVoiceRecordButtonEvent(PressEvent.PressStart) }, - onLongPressEnd = { onVoiceRecordButtonEvent(PressEvent.LongPressEnd) }, - onTap = { onVoiceRecordButtonEvent(PressEvent.Tapped) }, + VoiceMessageRecorderButton( + isRecording = voiceMessageState is VoiceMessageState.Recording, + onEvent = onVoiceRecorderEvent, ) } val sendVoiceButton = @Composable { SendButton( canSendMessage = voiceMessageState is VoiceMessageState.Preview, - onClick = { onSendVoiceMessage() }, + onClick = onSendVoiceMessage, composerMode = composerMode, ) } @@ -223,8 +222,12 @@ fun TextComposer( } val voiceDeleteButton = @Composable { - if (voiceMessageState is VoiceMessageState.Preview) { - VoiceMessageDeleteButton(enabled = !voiceMessageState.isSending, onClick = onDeleteVoiceMessage) + when (voiceMessageState) { + is VoiceMessageState.Preview -> + VoiceMessageDeleteButton(enabled = !voiceMessageState.isSending, onClick = onDeleteVoiceMessage) + is VoiceMessageState.Recording -> + VoiceMessageDeleteButton(enabled = true, onClick = { onVoiceRecorderEvent(VoiceMessageRecorderEvent.Cancel) }) + else -> {} } } @@ -286,7 +289,7 @@ private fun StandardLayout( verticalAlignment = Alignment.Bottom, ) { if (enableVoiceMessages && voiceMessageState !is VoiceMessageState.Idle) { - if (voiceMessageState is VoiceMessageState.Preview) { + if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) { Box( modifier = Modifier .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt deleted file mode 100644 index 0a0710095e..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/RecordButton.kt +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - -@file:OptIn(ExperimentalMaterial3Api::class) - -package io.element.android.libraries.textcomposer.components - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.TooltipState -import androidx.compose.material3.rememberTooltipState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.input.pointer.PointerEventType -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.components.tooltip.ElementTooltipDefaults -import io.element.android.libraries.designsystem.components.tooltip.PlainTooltip -import io.element.android.libraries.designsystem.components.tooltip.TooltipBox -import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.designsystem.theme.components.Icon -import io.element.android.libraries.designsystem.theme.components.IconButton -import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.designsystem.utils.CommonDrawables -import io.element.android.libraries.textcomposer.R -import io.element.android.libraries.textcomposer.utils.PressState -import io.element.android.libraries.textcomposer.utils.PressStateEffects -import io.element.android.libraries.textcomposer.utils.rememberPressState -import io.element.android.libraries.theme.ElementTheme -import io.element.android.libraries.ui.strings.CommonStrings -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun RecordButton( - modifier: Modifier = Modifier, - initialTooltipIsVisible: Boolean = false, - onPressStart: () -> Unit = {}, - onLongPressEnd: () -> Unit = {}, - onTap: () -> Unit = {}, -) { - val coroutineScope = rememberCoroutineScope() - val pressState = rememberPressState() - val hapticFeedback = LocalHapticFeedback.current - - val performHapticFeedback = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - } - - val tooltipState = rememberTooltipState( - initialIsVisible = initialTooltipIsVisible - ) - - PressStateEffects( - pressState = pressState.value, - onPressStart = { - onPressStart() - performHapticFeedback() - }, - onLongPressEnd = { - onLongPressEnd() - performHapticFeedback() - }, - onTap = { - onTap() - performHapticFeedback() - coroutineScope.launch { tooltipState.show() } - }, - ) - Box(modifier = modifier) { - HoldToRecordTooltip( - tooltipState = tooltipState, - spacingBetweenTooltipAndAnchor = 0.dp, // Accounts for the 48.dp size of the record button - anchor = { - RecordButtonView( - isPressed = pressState.value is PressState.Pressing, - modifier = Modifier - .pointerInput(Unit) { - awaitPointerEventScope { - while (true) { - val event = awaitPointerEvent() - coroutineScope.launch { - when (event.type) { - PointerEventType.Press -> pressState.press() - PointerEventType.Release -> pressState.release() - } - } - } - } - } - ) - } - ) - } -} - -@Composable -private fun RecordButtonView( - isPressed: Boolean, - modifier: Modifier = Modifier, -) { - IconButton( - modifier = modifier - .size(48.dp), - onClick = {}, - ) { - Icon( - modifier = Modifier.size(24.dp), - resourceId = if (isPressed) { - CommonDrawables.ic_compound_mic_on_solid - } else { - CommonDrawables.ic_compound_mic_on_outline - }, - contentDescription = stringResource(CommonStrings.a11y_voice_message_record), - tint = ElementTheme.colors.iconSecondary, - ) - } -} - -@Composable -private fun HoldToRecordTooltip( - tooltipState: TooltipState, - spacingBetweenTooltipAndAnchor: Dp, - modifier: Modifier = Modifier, - anchor: @Composable () -> Unit, -) { - TooltipBox( - positionProvider = ElementTooltipDefaults.rememberPlainTooltipPositionProvider( - spacingBetweenTooltipAndAnchor = spacingBetweenTooltipAndAnchor, - ), - tooltip = { - PlainTooltip { - Text( - text = stringResource(R.string.screen_room_voice_message_tooltip), - color = ElementTheme.colors.textOnSolidPrimary, - style = ElementTheme.typography.fontBodySmMedium, - ) - } - }, - state = tooltipState, - modifier = modifier, - focusable = false, - enableUserInput = false, - content = anchor, - ) -} - -@PreviewsDayNight -@Composable -internal fun RecordButtonPreview() = ElementPreview { - Row { - RecordButtonView(isPressed = false) - RecordButtonView(isPressed = true) - } -} - -@PreviewsDayNight -@Composable -internal fun HoldToRecordTooltipPreview() = ElementPreview { - Box(modifier = Modifier.fillMaxSize()) { - RecordButton( - modifier = Modifier.align(Alignment.BottomEnd), - initialTooltipIsVisible = true, - ) - } -} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt index 222ee2aeaf..bcf307ec2e 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt @@ -43,7 +43,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text -import io.element.android.libraries.textcomposer.R +import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.time.formatShort @@ -145,14 +145,14 @@ private fun PlayerButton( @Composable private fun PauseIcon() = Icon( - resourceId = R.drawable.ic_pause, + resourceId = CommonDrawables.ic_pause, contentDescription = stringResource(id = CommonStrings.a11y_pause), modifier = Modifier.size(20.dp), ) @Composable private fun PlayIcon() = Icon( - resourceId = R.drawable.ic_play, + resourceId = CommonDrawables.ic_play, contentDescription = stringResource(id = CommonStrings.a11y_play), modifier = Modifier.size(20.dp), ) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt new file mode 100644 index 0000000000..d2a8c2cae1 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecorderButton.kt @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent +import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +internal fun VoiceMessageRecorderButton( + isRecording: Boolean, + modifier: Modifier = Modifier, + onEvent: (VoiceMessageRecorderEvent) -> Unit = {}, +) { + val hapticFeedback = LocalHapticFeedback.current + + val performHapticFeedback = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + } + + if (isRecording) { + StopButton( + modifier = modifier, + onClick = { + performHapticFeedback() + onEvent(VoiceMessageRecorderEvent.Stop) + } + ) + } else { + StartButton( + modifier = modifier, + onClick = { + performHapticFeedback() + onEvent(VoiceMessageRecorderEvent.Start) + } + ) + } +} + +@Composable +private fun StartButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) = IconButton( + modifier = modifier.size(48.dp), + onClick = onClick, +) { + Icon( + modifier = Modifier.size(24.dp), + resourceId = CommonDrawables.ic_compound_mic_on_outline, + contentDescription = stringResource(CommonStrings.a11y_voice_message_record), + tint = ElementTheme.colors.iconSecondary, + ) +} + +@Composable +private fun StopButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) = IconButton( + modifier = modifier + .size(48.dp), + onClick = onClick, +) { + Box( + Modifier + .size(36.dp) + .background( + color = ElementTheme.colors.bgActionPrimaryRest, + shape = CircleShape, + ) + ) + Icon( + modifier = Modifier.size(24.dp), + resourceId = CommonDrawables.ic_stop, + contentDescription = stringResource(CommonStrings.a11y_voice_message_stop_recording), + tint = ElementTheme.colors.iconOnSolidPrimary, + ) +} + +@PreviewsDayNight +@Composable +internal fun VoiceMessageRecorderButtonPreview() = ElementPreview { + Row { + VoiceMessageRecorderButton(isRecording = false) + VoiceMessageRecorderButton(isRecording = true) + } +} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageRecorderEvent.kt similarity index 77% rename from libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt rename to libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageRecorderEvent.kt index 340540886d..17091930c5 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/PressEvent.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageRecorderEvent.kt @@ -16,8 +16,8 @@ package io.element.android.libraries.textcomposer.model -sealed interface PressEvent { - data object PressStart: PressEvent - data object Tapped: PressEvent - data object LongPressEnd: PressEvent +sealed interface VoiceMessageRecorderEvent { + data object Start: VoiceMessageRecorderEvent + data object Stop: VoiceMessageRecorderEvent + data object Cancel: VoiceMessageRecorderEvent } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt deleted file mode 100644 index 50df6d591c..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressState.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils - -/** - * State of a press gesture. - */ -internal sealed interface PressState { - data class Idle( - val lastPress: Pressing? - ) : PressState - - sealed interface Pressing : PressState - data object Tapping : Pressing - data object LongPressing : Pressing -} - diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt deleted file mode 100644 index aaee6bae0f..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateEffects.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect - -/** - * React to [PressState] changes. - */ -@Composable -internal fun PressStateEffects( - pressState: PressState, - onPressStart: () -> Unit = {}, - onLongPressStart: () -> Unit = {}, - onTap: () -> Unit = {}, - onLongPressEnd: () -> Unit = {}, -) { - LaunchedEffect(pressState) { - when (pressState) { - is PressState.Idle -> - when (pressState.lastPress) { - PressState.Tapping -> onTap() - PressState.LongPressing -> onLongPressEnd() - null -> {} // Do nothing - } - is PressState.LongPressing -> onLongPressStart() - PressState.Tapping -> onPressStart() - } - } -} - - diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt deleted file mode 100644 index 7021b8ac46..0000000000 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolder.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.platform.LocalViewConfiguration -import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.yield -import timber.log.Timber - -@Composable -internal fun rememberPressState( - longPressTimeoutMillis: Long = LocalViewConfiguration.current.longPressTimeoutMillis, -): PressStateHolder { - return remember(longPressTimeoutMillis) { - PressStateHolder(longPressTimeoutMillis = longPressTimeoutMillis) - } -} - -/** - * State machine that keeps track of the pressed state. - * - * When a press is started, the state will transition through: - * [PressState.Idle] -> [PressState.Tapping] -> ... - * - * If a press is held for a longer time, the state will continue through: - * ... -> [PressState.LongPressing] -> ... - * - * When the press is released the states will then transition back to idle. - * ... -> [PressState.Idle] - * - * Whether a press should be considered a tap or a long press can be determined by - * looking at the last press when in the idle state. - * - * @see [PressStateEffects] - * @see [rememberPressState] - */ -internal class PressStateHolder( - private val longPressTimeoutMillis: Long, -) : State { - private var state: PressState by mutableStateOf(PressState.Idle(lastPress = null)) - - override val value: PressState - get() = state - - private var longPressTimer: Job? = null - - suspend fun press() = coroutineScope { - when (state) { - is PressState.Idle -> { - state = PressState.Tapping - } - is PressState.Pressing -> - Timber.e("Pointer pressed but it has not been released") - } - - longPressTimer = launch { - delay(longPressTimeoutMillis) - yield() - - if (isActive && state == PressState.Tapping) { - state = PressState.LongPressing - } - } - } - - fun release() { - longPressTimer?.cancel() - longPressTimer = null - when (val lastState = state) { - is PressState.Pressing -> - state = PressState.Idle(lastPress = lastState) - is PressState.Idle -> - Timber.e("Pointer pressed but it has not been released") - } - } -} - diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt deleted file mode 100644 index 615692911e..0000000000 --- a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/utils/PressStateHolderTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.utils - -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.textcomposer.utils.PressState.Idle -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.async -import kotlinx.coroutines.test.advanceTimeBy -import kotlinx.coroutines.test.runTest -import org.junit.Test -import kotlin.time.Duration.Companion.milliseconds - -@OptIn(ExperimentalCoroutinesApi::class) class PressStateHolderTest { - companion object { - const val LONG_PRESS_TIMEOUT_MILLIS = 1L - } - @Test - fun `it starts in idle state`() = runTest { - val stateHolder = createStateHolder() - assertThat(stateHolder.value).isEqualTo(Idle(lastPress = null)) - } - - @Test - fun `when press, it moves to tapping state`() = runTest { - val stateHolder = createStateHolder() - val press = async { stateHolder.press() } - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.Tapping) - press.await() - } - - @Test - fun `when release after short delay, it moves through tap states`() = runTest { - val stateHolder = createStateHolder() - val press = async { stateHolder.press() } - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.Tapping) - stateHolder.release() - advanceTimeBy(1.milliseconds) // wait for the long press timeout which should not be triggered - assertThat(stateHolder.value).isEqualTo(Idle(lastPress = PressState.Tapping)) - press.await() - } - - @Test - fun `when hold, it moves through long press states`() = runTest { - val stateHolder = createStateHolder() - val press = async { stateHolder.press() } - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.Tapping) - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.LongPressing) - stateHolder.release() - assertThat(stateHolder.value).isEqualTo(Idle(lastPress = PressState.LongPressing)) - press.await() - } - - @Test - fun `when release and repress, it doesn't enter long press states`() = runTest { - val stateHolder = createStateHolder() - val press1 = async { stateHolder.press() } - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.Tapping) - stateHolder.release() - val press2 = async { stateHolder.press() } - advanceTimeBy(1.milliseconds) - assertThat(stateHolder.value).isEqualTo(PressState.Tapping) - press1.await() - press2.await() - } - - @Test - fun `when press twice without releasing, it doesn't throw an error`() = runTest { - val stateHolder = createStateHolder() - stateHolder.press() - stateHolder.press() - } - - @Test - fun `when release without first pressing, it doesn't throw an error`() = runTest { - val stateHolder = createStateHolder() - stateHolder.release() - } - - @Test - fun `when release twice without pressing, it doesn't throw an error `() = runTest { - val stateHolder = createStateHolder() - stateHolder.press() - stateHolder.release() - stateHolder.release() - } - - private fun createStateHolder() = - PressStateHolder( - LONG_PRESS_TIMEOUT_MILLIS, - ) -} diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png index 3ec6004b6d..521717b05b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Day-6_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d5209087d7841a80f7213f26aaf8322cc5fee03466b9059e06b0daf5f489c1b -size 10012 +oid sha256:5cd4e624c0ed8abe5d22d60992f64f81e91fa41ea7d8772acc5e3832eadbcbb7 +size 10581 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png index dcddb1ae01..3dd0888ff4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_MessageComposerViewVoice_null_MessageComposerViewVoice-Night-6_7_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b8fca1de81e424f01a518e3a2547b58c0e9eb7249327f3fcdb1cf62ab655972 -size 9429 +oid sha256:01eda47acbf273bf61e60dbbc466aac5e9a4eac7690af4cde2e06e7e1f190bcf +size 10152 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Day-13_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Day-13_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 2f3dc97e8c..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Day-13_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e6d75686b1463d11d89e5130b160c3c446f26612e8da44981cc6994687001d80 -size 7093 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Night-13_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Night-13_14_null,NEXUS_5,1.0,en].png deleted file mode 100644 index ff6633e96a..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_HoldToRecordTooltip_null_HoldToRecordTooltip-Night-13_14_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f8c96b9ce6a8d9136c56789e9e6af25b4259143f6416305c523f4e5224bc8fa8 -size 5898 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Day-12_12_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Day-12_12_null,NEXUS_5,1.0,en].png deleted file mode 100644 index f0c42b1810..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Day-12_12_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0e8a1efaf22dd86e6e27904d72a820cfb0b5d1d38ffeb5745bfa6ca3b0a1c85 -size 6003 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Night-12_13_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Night-12_13_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 27368109ad..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_RecordButton_null_RecordButton-Night-12_13_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7f2db2983459eae8b7538c51d125836807b29334b01fbca495c3c9630fb510c2 -size 5969 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-14_14_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-14_14_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Day-12_12_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-14_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-14_15_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_SendButton_null_SendButton-Night-12_13_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-15_15_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_13_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-15_15_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Day-13_13_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-15_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_14_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-15_16_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_TextFormatting_null_TextFormatting-Night-13_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-16_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-16_16_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Day-14_14_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-16_17_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageDeleteButton_null_VoiceMessageDeleteButton-Night-14_15_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-17_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_15_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-17_17_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Day-15_15_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-17_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_16_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-17_18_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessagePreview_null_VoiceMessagePreview-Night-15_16_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3059b1e5ad --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Day-16_16_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b6f6caef8d902d4e0b93721f212dcd3f20cde267e1782813f0c55498bca5c8e +size 6805 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..279ced000a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecorderButton_null_VoiceMessageRecorderButton-Night-16_17_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6def3059e37f5c7d6d8930df229a895f5d37b95f46a138fa4ef790bbf76b80b6 +size 6744 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-18_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_17_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-18_18_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Day-17_17_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_18_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-18_19_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.components_VoiceMessageRecording_null_VoiceMessageRecording-Night-17_18_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png index f5ba129fd3..e9254642a2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:991416dcd21ebf8e2d57baac7cb42506596a1a346dd7d643b09c84c473e053b7 -size 28016 +oid sha256:9a76e1dcbc4461b6e0a1cca836a097846262a3d302befbec4486ed7ee837a8da +size 28214 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png index 8bd3ded996..304d6dc23e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_TextComposerVoice_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8b8c60473ce1b9e7451e763c960a9242bb83ca1d3769ee921db830ff6e57e7c -size 27094 +oid sha256:25be7fa172232c508c58369f60e82fc7ec1d7362d9f701c1e698ec8d8498b482 +size 27449 From 470613e45178f0b83f51357f21179e92174a5054 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 15:07:20 +0100 Subject: [PATCH 029/102] Cleanup --- .../android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt index bc4e19d13e..5a1cfe537b 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt @@ -47,7 +47,6 @@ import java.io.File import java.util.UUID import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.Duration.Companion.minutes import kotlin.time.TimeSource @SingleIn(RoomScope::class) From 2c25e69df8695a9914b6e1f8f2f0ebb71a1188c8 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Tue, 14 Nov 2023 15:24:24 +0100 Subject: [PATCH 030/102] Persist state of VoiceMessagePresenter in memory (#1795) Allows [VoiceMessagePresenter] instances to keep their progress and download states while going in and out of the timeline viewport. This is implemented by caching each instance of a TimelineItem presenter inside the RoomScope. TimelineItem presenters can move some of their state outside of the `present()` function so that such state will survive scrollings of the timeline. --- .../di/LocalTimelineItemPresenterFactories.kt | 26 ++++++ .../di/TimelineItemPresenterFactories.kt | 84 ++++++++++++------- .../timeline/VoiceMessagePresenter.kt | 16 ++-- .../timeline/VoiceMessagePresenterTest.kt | 4 +- 4 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/LocalTimelineItemPresenterFactories.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/LocalTimelineItemPresenterFactories.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/LocalTimelineItemPresenterFactories.kt new file mode 100644 index 0000000000..d93bee7817 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/LocalTimelineItemPresenterFactories.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.di + +import androidx.compose.runtime.staticCompositionLocalOf + +/** + * Provides a [TimelineItemPresenterFactories] to the composition. + */ +val LocalTimelineItemPresenterFactories = staticCompositionLocalOf { + TimelineItemPresenterFactories(emptyMap()) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt index 0574f7e903..e8997f5a1c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/di/TimelineItemPresenterFactories.kt @@ -18,13 +18,13 @@ package io.element.android.features.messages.impl.timeline.di import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import androidx.compose.runtime.staticCompositionLocalOf import com.squareup.anvil.annotations.ContributesTo import dagger.Module import dagger.multibindings.Multibinds import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.di.SingleIn import javax.inject.Inject /** @@ -40,38 +40,60 @@ interface TimelineItemPresenterFactoriesModule { } /** - * Wrapper around the [TimelineItemPresenterFactory] map multi binding. + * Room level caching layer for the [TimelineItemPresenterFactory] instances. * - * Its only purpose is to provide a nicer type name than: - * `@JvmSuppressWildcards Map, TimelineItemPresenterFactory<*, *>>`. - * - * A typealias would have been better but typealiases on Dagger types which use @JvmSuppressWildcards - * currently make Dagger crash. - * - * Request this type from Dagger to access the [TimelineItemPresenterFactory] map multibinding. + * It will cache the presenter instances in the room scope, so that they can be + * reused across recompositions of the timeline items that happen whenever an item + * goes out of the [LazyColumn] viewport. */ -data class TimelineItemPresenterFactories @Inject constructor( - val factories: @JvmSuppressWildcards Map, TimelineItemPresenterFactory<*, *>>, -) +@SingleIn(RoomScope::class) +class TimelineItemPresenterFactories @Inject constructor( + private val factories: @JvmSuppressWildcards Map, TimelineItemPresenterFactory<*, *>>, +) { + private val presenters: MutableMap> = mutableMapOf() -/** - * Provides a [TimelineItemPresenterFactories] to the composition. - */ -val LocalTimelineItemPresenterFactories = staticCompositionLocalOf { - TimelineItemPresenterFactories(emptyMap()) -} - -/** - * Creates and remembers a presenter for the given content. - * - * Will throw if the presenter is not found in the [TimelineItemPresenterFactory] map multi binding. - */ -@Composable -inline fun TimelineItemPresenterFactories.rememberPresenter( - content: C -): Presenter = remember(content) { - factories.getValue(C::class.java).let { - @Suppress("UNCHECKED_CAST") - (it as TimelineItemPresenterFactory).create(content) + /** + * Creates and caches a presenter for the given content. + * + * Will throw if the presenter is not found in the [TimelineItemPresenterFactory] map multi binding. + * + * @param C The [TimelineItemEventContent] subtype handled by this TimelineItem presenter. + * @param S The state type produced by this timeline item presenter. + * @param content The [TimelineItemEventContent] instance to create a presenter for. + * @param contentClass The class of [content]. + * @return An instance of a TimelineItem presenter that will be cached in the room scope. + */ + @Composable + fun rememberPresenter( + content: C, + contentClass: Class, + ): Presenter = remember(content) { + presenters[content]?.let { + @Suppress("UNCHECKED_CAST") + it as Presenter + } ?: factories.getValue(contentClass).let { + @Suppress("UNCHECKED_CAST") + (it as TimelineItemPresenterFactory).create(content).apply { + presenters[content] = this + } + } } } + +/** + * Creates and caches a presenter for the given content. + * + * Will throw if the presenter is not found in the [TimelineItemPresenterFactory] map multi binding. + * + * @param C The [TimelineItemEventContent] subtype handled by this TimelineItem presenter. + * @param S The state type produced by this timeline item presenter. + * @param content The [TimelineItemEventContent] instance to create a presenter for. + * @return An instance of a TimelineItem presenter that will be cached in the room scope. + */ +@Composable +inline fun TimelineItemPresenterFactories.rememberPresenter( + content: C +): Presenter = rememberPresenter( + content = content, + contentClass = C::class.java +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 1bbdedc22d..5df804c8ac 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -22,7 +22,6 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import com.squareup.anvil.annotations.ContributesTo import dagger.Binds import dagger.Module @@ -40,6 +39,7 @@ import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.di.RoomScope import io.element.android.libraries.ui.utils.time.formatShort import io.element.android.services.analytics.api.AnalyticsService +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.milliseconds @@ -55,6 +55,7 @@ interface VoiceMessagePresenterModule { class VoiceMessagePresenter @AssistedInject constructor( voiceMessagePlayerFactory: VoiceMessagePlayer.Factory, private val analyticsService: AnalyticsService, + private val scope: CoroutineScope, @Assisted private val content: TimelineItemVoiceContent, ) : Presenter { @@ -70,13 +71,13 @@ class VoiceMessagePresenter @AssistedInject constructor( body = content.body, ) + private val play = mutableStateOf>(Async.Uninitialized) + private var progressCache: Float = 0f + @Composable override fun present(): VoiceMessageState { - val scope = rememberCoroutineScope() - val playerState by player.state.collectAsState(VoiceMessagePlayer.State(isPlaying = false, isMyMedia = false, currentPosition = 0L)) - val play = remember { mutableStateOf>(Async.Uninitialized) } val button by remember { derivedStateOf { @@ -90,7 +91,12 @@ class VoiceMessagePresenter @AssistedInject constructor( } } val progress by remember { - derivedStateOf { if (playerState.isMyMedia) playerState.currentPosition / content.duration.toMillis().toFloat() else 0f } + derivedStateOf { + if (playerState.isMyMedia) { + progressCache = playerState.currentPosition / content.duration.toMillis().toFloat() + } + progressCache + } } val time by remember { derivedStateOf { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt index 0097245d27..26f4232531 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -31,6 +31,7 @@ import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMes import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test @@ -201,7 +202,7 @@ class VoiceMessagePresenterTest { } } -fun createVoiceMessagePresenter( +fun TestScope.createVoiceMessagePresenter( voiceMessageMediaRepo: VoiceMessageMediaRepo = FakeVoiceMessageMediaRepo(), analyticsService: AnalyticsService = FakeAnalyticsService(), content: TimelineItemVoiceContent = aTimelineItemVoiceContent(), @@ -217,5 +218,6 @@ fun createVoiceMessagePresenter( ) }, analyticsService = analyticsService, + scope = this, content = content, ) From a5f76da2042f1a3fee6e02691672c1ae4f99767e Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Tue, 14 Nov 2023 15:32:18 +0100 Subject: [PATCH 031/102] Remove lateinit from Application class Just stumbled in this lateinit and thought we could just get rid of it. --- .../main/kotlin/io/element/android/x/ElementXApplication.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt index 542ae7090d..c1bed67d80 100644 --- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt @@ -27,14 +27,10 @@ import io.element.android.x.initializer.TracingInitializer class ElementXApplication : Application(), DaggerComponentOwner { - private lateinit var appComponent: AppComponent - - override val daggerComponent: Any - get() = appComponent + override val daggerComponent: AppComponent = DaggerAppComponent.factory().create(this) override fun onCreate() { super.onCreate() - appComponent = DaggerAppComponent.factory().create(applicationContext) AppInitializer.getInstance(this).apply { initializeComponent(CrashInitializer::class.java) initializeComponent(TracingInitializer::class.java) From 6eb012a7d5be2d8a9984b5031f20ec20ba96968b Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Tue, 14 Nov 2023 15:04:43 +0000 Subject: [PATCH 032/102] Delay displaying the voice message download indicator (#1793) --------- Co-authored-by: ElementBot --- .../components/event/TimelineItemVoiceView.kt | 52 ++++++++++++++++--- ...sButton-Day-43_43_null,NEXUS_5,1.0,en].png | 3 ++ ...utton-Night-43_44_null,NEXUS_5,1.0,en].png | 3 ++ ...Unified-Day-42_42_null,NEXUS_5,1.0,en].png | 4 +- ...ified-Night-42_43_null,NEXUS_5,1.0,en].png | 4 +- ...eView-Day-41_41_null_0,NEXUS_5,1.0,en].png | 4 +- ...View-Day-41_41_null_10,NEXUS_5,1.0,en].png | 4 +- ...eView-Day-41_41_null_5,NEXUS_5,1.0,en].png | 4 +- ...iew-Night-41_42_null_0,NEXUS_5,1.0,en].png | 4 +- ...ew-Night-41_42_null_10,NEXUS_5,1.0,en].png | 4 +- ...iew-Night-41_42_null_5,NEXUS_5,1.0,en].png | 4 +- ...erView-Day-44_44_null,NEXUS_5,1.0,en].png} | 0 ...View-Night-44_45_null,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_0,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_1,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_10,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_11,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_12,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_13,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_14,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_15,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_16,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_17,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_18,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_19,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_2,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_20,NEXUS_5,1.0,en].png} | 0 ...ent-Day-45_45_null_21,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_3,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_4,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_5,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_6,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_7,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_8,NEXUS_5,1.0,en].png} | 0 ...ment-Day-45_45_null_9,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_0,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_1,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_10,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_11,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_12,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_13,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_14,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_15,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_16,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_17,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_18,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_19,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_2,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_20,NEXUS_5,1.0,en].png} | 0 ...t-Night-45_46_null_21,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_3,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_4,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_5,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_6,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_7,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_8,NEXUS_5,1.0,en].png} | 0 ...nt-Night-45_46_null_9,NEXUS_5,1.0,en].png} | 0 ...tent-Day-46_46_null_0,NEXUS_5,1.0,en].png} | 0 ...nt-Night-46_47_null_0,NEXUS_5,1.0,en].png} | 0 ...Menu-Day-47_47_null_0,NEXUS_5,1.0,en].png} | 0 ...Menu-Day-47_47_null_1,NEXUS_5,1.0,en].png} | 0 ...nu-Night-47_48_null_0,NEXUS_5,1.0,en].png} | 0 ...nu-Night-47_48_null_1,NEXUS_5,1.0,en].png} | 0 ...View-Day-48_48_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Day-48_48_null_1,NEXUS_5,1.0,en].png} | 0 ...View-Day-48_48_null_2,NEXUS_5,1.0,en].png} | 0 ...ew-Night-48_49_null_0,NEXUS_5,1.0,en].png} | 0 ...ew-Night-48_49_null_1,NEXUS_5,1.0,en].png} | 0 ...ew-Night-48_49_null_2,NEXUS_5,1.0,en].png} | 0 ...View-Day-49_49_null_0,NEXUS_5,1.0,en].png} | 0 ...View-Day-49_49_null_1,NEXUS_5,1.0,en].png} | 0 ...ew-Night-49_50_null_0,NEXUS_5,1.0,en].png} | 0 ...ew-Night-49_50_null_1,NEXUS_5,1.0,en].png} | 0 ...icator-Day-50_50_null,NEXUS_5,1.0,en].png} | 0 ...ator-Night-50_51_null,NEXUS_5,1.0,en].png} | 0 ...foView-Day-51_51_null,NEXUS_5,1.0,en].png} | 0 ...View-Night-51_52_null,NEXUS_5,1.0,en].png} | 0 77 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-43_43_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-43_44_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_10,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_11,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_12,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_13,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_14,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_15,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_16,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_17,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_18,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_19,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_20,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_21,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_5,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_6,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_7,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_8,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_9,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_10,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_11,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_12,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_13,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_14,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_15,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_16,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_17,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_18,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_19,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_20,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_21,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_3,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_4,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_5,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_6,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_7,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_8,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_9,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-45_45_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-46_46_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-45_46_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-46_47_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-49_49_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-49_50_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-50_50_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-50_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png} (100%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index e7762fe278..e1494b9da0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -27,6 +27,11 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.IconButtonDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -53,6 +58,7 @@ import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.delay @Composable fun TimelineItemVoiceView( @@ -147,19 +153,40 @@ private fun RetryButton( } } +/** + * Progress button is shown when the voice message is being downloaded. + * + * The progress indicator is optimistic and displays a pause button (which + * indicates the audio is playing) for 2 seconds before revealing the + * actual progress indicator. + */ @Composable -private fun ProgressButton() { +private fun ProgressButton( + displayImmediately: Boolean = false, +) { + var canDisplay by remember { mutableStateOf(displayImmediately) } + LaunchedEffect(Unit) { + delay(2000L) + canDisplay = true + } CustomIconButton( onClick = {}, enabled = false, ) { - CircularProgressIndicator( - modifier = Modifier - .padding(2.dp) - .size(16.dp), - color = ElementTheme.colors.iconSecondary, - strokeWidth = 2.dp, - ) + if (canDisplay) { + CircularProgressIndicator( + modifier = Modifier + .padding(2.dp) + .size(16.dp), + color = ElementTheme.colors.iconSecondary, + strokeWidth = 2.dp, + ) + } else { + Icon( + resourceId = R.drawable.pause, + contentDescription = stringResource(id = CommonStrings.a11y_pause), + ) + } } } @@ -228,3 +255,12 @@ internal fun TimelineItemVoiceViewUnifiedPreview() = ElementPreview { } } } + +@PreviewsDayNight +@Composable +internal fun ProgressButtonPreview() = ElementPreview { + Row { + ProgressButton(displayImmediately = true) + ProgressButton(displayImmediately = false) + } +} diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5b8f0832b2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Day-43_43_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:726cb736f33634d3b7efd1ae01b5f7808ffba8933ceda115b3b368070f26ede7 +size 5474 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1fffb2a53c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_ProgressButton_null_ProgressButton-Night-43_44_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99d85977724a11c52831391fb2d1bcb5bcd2fd697a9e1073b067cfc25a9cbcc7 +size 5437 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png index edf4815074..7bda39becb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Day-42_42_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:327a1fd51b242e1547878e58f32e6511e013cb440f4cd457a20e179ca3a1ed39 -size 46377 +oid sha256:cb6bf3cc0868def05297678d308ecfda99f36239bda2ad67a144c5054cd09f19 +size 45680 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png index a954c65665..b916f8b832 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceViewUnified_null_TimelineItemVoiceViewUnified-Night-42_43_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c50b273463475a1ecdeb1393f688da883448be9fefa28b8ea15232a5ef7c601 -size 45417 +oid sha256:5f9f5fbbaa510904e7f21fee456789ba9154dc7fd927caf5719daf4bce49519d +size 44517 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png index 1e6264eeac..82c25db6e1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f19f38428d2e17f2cb7a8b3de1af39cf01e48e5c854d3a64208a78c4687b867 -size 6101 +oid sha256:9b7baba714a54c9be4611677b938705277e2f967c7361af4fd8e16651b732cc0 +size 5729 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png index 2e123fb511..0ca95a9c4a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef25bb048d7feb642509f1d5bbc646b867b89264efa32e77730650f31f0233ad -size 9895 +oid sha256:111c3aafacfbb3fd16421c74eae2b3c6ba7c2f6f3b2203c0ab91a226e5015e2a +size 9529 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png index 2ec7b91240..733df194eb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Day-41_41_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9ea30725d03749028f907e341ee09bb98a67cc6bda76757f7b3d266fed3722f -size 7177 +oid sha256:bcb3a451a7ae4271fc2acbda8ba5f40a21167bb36a81d14b9006c68baddff3c0 +size 6790 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png index 223f192095..2f0181bf55 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfffebea253932b08a308ac098a392910233e4a8524a898b459842849854c91 -size 6036 +oid sha256:6780dcb4537aed0168fecc8a7df77d17b38d57f5e2fa697a353728e9bd28aa59 +size 5697 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png index 1da72edd97..a71c8488c8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca13f10ca5fd6194829c3cef02fa5cd0c00790f23c82d2cf70ac5d1d58babb54 -size 9656 +oid sha256:a6d969780e06a96c68cc547f7731d839d4eb3855bc39bacd7e7fc95692bc8544 +size 9324 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png index aa9de6ea89..a57555c928 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemVoiceView_null_TimelineItemVoiceView-Night-41_42_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4a2824fdc49adb0e3fc155e7da319414544a87dc083bdece78e85ba72669d78 -size 7155 +oid sha256:12ad14137042e62096d78434481b73a9e8668a617f5b465ee97695fc0a008382 +size 6795 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-43_43_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-43_43_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Day-44_44_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-43_44_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-43_44_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.group_GroupHeaderView_null_GroupHeaderView-Night-44_45_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_12,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_12,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_13,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_13,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_14,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_14,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_15,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_15,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_16,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_16,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_17,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_17,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_18,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_18,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_19,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_19,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_20,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_20,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_21,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_21,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-44_44_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Day-45_45_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_10,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_10,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_11,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_11,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_12,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_12,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_13,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_13,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_14,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_14,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_15,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_15,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_16,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_16,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_16,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_17,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_17,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_17,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_18,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_18,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_18,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_19,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_19,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_19,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_20,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_20,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_20,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_21,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_21,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_21,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_3,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_4,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_4,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_5,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_5,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_6,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_6,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_7,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_7,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_8,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-44_45_null_9,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.html_HtmlDocument_null_HtmlDocument-Night-45_46_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-45_45_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-46_46_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-45_45_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Day-46_46_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-45_46_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-46_47_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-45_46_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.reactionsummary_SheetContent_null_SheetContent-Night-46_47_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-46_46_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-46_47_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-47_47_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-47_48_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-48_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-48_49_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-49_49_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-49_49_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-49_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-49_50_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-50_50_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-50_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png From c3471a1d5d7e9e87336ad09993794a7239685bb1 Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Tue, 14 Nov 2023 15:05:07 +0000 Subject: [PATCH 033/102] Show error dialog when voice message fails to send (#1796) --------- Co-authored-by: ElementBot --- .../messages/impl/MessagesStateProvider.kt | 10 +++++- .../features/messages/impl/MessagesView.kt | 6 ++++ .../composer/VoiceMessageComposerEvents.kt | 1 + .../composer/VoiceMessageComposerPresenter.kt | 32 ++++++++++++----- .../composer/VoiceMessageComposerState.kt | 1 + .../VoiceMessageComposerStateProvider.kt | 12 +++++++ .../VoiceMessageSendingFailedDialog.kt | 34 ++++++++++++++++++ .../VoiceMessageComposerPresenterTest.kt | 36 +++++++++++++++++++ ...esView-Day-0_0_null_11,NEXUS_5,1.0,en].png | 3 ++ ...View-Night-0_1_null_11,NEXUS_5,1.0,en].png | 3 ++ 10 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 275b324862..38d6289a88 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState +import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessagePreviewState import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -65,7 +66,14 @@ open class MessagesStateProvider : PreviewParameterProvider { ), aMessagesState().copy( isCallOngoing = true, - ) + ), + aMessagesState().copy( + enableVoiceMessages = true, + voiceMessageComposerState = aVoiceMessageComposerState( + voiceMessageState = aVoiceMessagePreviewState(), + showSendFailureDialog = true + ), + ), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 4d7ef3ef3e..9c8a9dab8a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -75,6 +75,7 @@ import io.element.android.features.messages.impl.timeline.components.retrysendme import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog +import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule @@ -340,6 +341,11 @@ private fun MessagesViewContent( appName = state.appName ) } + if (state.enableVoiceMessages && state.voiceMessageComposerState.showSendFailureDialog) { + VoiceMessageSendingFailedDialog( + onDismiss = { state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.DismissSendFailureDialog) }, + ) + } // This key is used to force the sheet to be remeasured when the content changes. // Any state change that should trigger a height size should be added to the list of remembered values here. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt index 0d384185df..0c83e834b0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerEvents.kt @@ -32,4 +32,5 @@ sealed interface VoiceMessageComposerEvents { data object AcceptPermissionRationale: VoiceMessageComposerEvents data object DismissPermissionsRationale: VoiceMessageComposerEvents data class LifecycleEvent(val event: Lifecycle.Event): VoiceMessageComposerEvents + data object DismissSendFailureDialog: VoiceMessageComposerEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt index cd54dc5588..528711b71e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -75,6 +75,7 @@ class VoiceMessageComposerPresenter @Inject constructor( val permissionState = permissionsPresenter.present() var isSending by remember { mutableStateOf(false) } + var showSendFailureDialog by remember { mutableStateOf(false) } LaunchedEffect(recorderState) { val recording = recorderState as? VoiceRecorderState.Finished @@ -138,6 +139,10 @@ class VoiceMessageComposerPresenter @Inject constructor( permissionState.eventSink(PermissionsEvents.CloseDialog) } + val onDismissSendFailureDialog = { + showSendFailureDialog = false + } + val onSendButtonPress = lambda@{ val finishedState = recorderState as? VoiceRecorderState.Finished if (finishedState == null) { @@ -152,11 +157,16 @@ class VoiceMessageComposerPresenter @Inject constructor( isSending = true player.pause() analyticsService.captureComposerEvent() - appCoroutineScope.sendMessage( - file = finishedState.file, - mimeType = finishedState.mimeType, - waveform = finishedState.waveform, - ).invokeOnCompletion { + appCoroutineScope.launch { + val result = sendMessage( + file = finishedState.file, + mimeType = finishedState.mimeType, + waveform = finishedState.waveform, + ) + if (result.isFailure) { + showSendFailureDialog = true + } + }.invokeOnCompletion { isSending = false } } @@ -175,6 +185,7 @@ class VoiceMessageComposerPresenter @Inject constructor( VoiceMessageComposerEvents.DismissPermissionsRationale -> onDismissPermissionsRationale() VoiceMessageComposerEvents.AcceptPermissionRationale -> onAcceptPermissionsRationale() is VoiceMessageComposerEvents.LifecycleEvent -> onLifecycleEvent(event.event) + VoiceMessageComposerEvents.DismissSendFailureDialog -> onDismissSendFailureDialog() } } @@ -193,6 +204,7 @@ class VoiceMessageComposerPresenter @Inject constructor( else -> VoiceMessageState.Idle }, showPermissionRationaleDialog = permissionState.showDialog, + showSendFailureDialog = showSendFailureDialog, keepScreenOn = keepScreenOn, eventSink = handleEvents, ) @@ -239,11 +251,11 @@ class VoiceMessageComposerPresenter @Inject constructor( voiceRecorder.deleteRecording() } - private fun CoroutineScope.sendMessage( + private suspend fun sendMessage( file: File, mimeType: String, - waveform: List - ) = launch { + waveform: List, + ): Result { val result = mediaSender.sendVoiceMessage( uri = file.toUri(), mimeType = mimeType, @@ -252,10 +264,12 @@ class VoiceMessageComposerPresenter @Inject constructor( if (result.isFailure) { Timber.e(result.exceptionOrNull(), "Voice message error") - return@launch + return result } voiceRecorder.deleteRecording() + + return result } private fun AnalyticsService.captureComposerEvent() = diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerState.kt index 055fa28177..aaab388ec1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerState.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.textcomposer.model.VoiceMessageState data class VoiceMessageComposerState( val voiceMessageState: VoiceMessageState, val showPermissionRationaleDialog: Boolean, + val showSendFailureDialog: Boolean, val keepScreenOn: Boolean, val eventSink: (VoiceMessageComposerEvents) -> Unit, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerStateProvider.kt index f9856005bb..0e884f5491 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.voicemessages.composer import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.designsystem.components.media.createFakeWaveform import io.element.android.libraries.textcomposer.model.VoiceMessageState import kotlinx.collections.immutable.toPersistentList import kotlin.time.Duration.Companion.seconds @@ -32,13 +33,24 @@ internal fun aVoiceMessageComposerState( voiceMessageState: VoiceMessageState = VoiceMessageState.Idle, keepScreenOn: Boolean = false, showPermissionRationaleDialog: Boolean = false, + showSendFailureDialog: Boolean = false, ) = VoiceMessageComposerState( voiceMessageState = voiceMessageState, showPermissionRationaleDialog = showPermissionRationaleDialog, + showSendFailureDialog = showSendFailureDialog, keepScreenOn = keepScreenOn, eventSink = {}, ) +internal fun aVoiceMessagePreviewState() = VoiceMessageState.Preview( + isSending = false, + isPlaying = false, + showCursor = false, + playbackProgress = 0f, + time = 10.seconds, + waveform = createFakeWaveform(), +) + internal var aWaveformLevels = List(100) { it.toFloat() / 100 }.toPersistentList() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt new file mode 100644 index 0000000000..a828e19f30 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageSendingFailedDialog.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages.composer + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog +import io.element.android.libraries.ui.strings.CommonStrings + +@Composable +internal fun VoiceMessageSendingFailedDialog( + onDismiss: () -> Unit, +) { + ErrorDialog( + title = stringResource(CommonStrings.common_error), + content = stringResource(CommonStrings.error_failed_uploading_voice_message), + onDismiss = onDismiss, + submitText = stringResource(CommonStrings.action_ok), + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 8fe1048c3c..8fb72d0e19 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -437,6 +437,42 @@ class VoiceMessageComposerPresenterTest { } } + @Test + fun `present - send failures are displayed as an error dialog`() = runTest { + val presenter = createVoiceMessageComposerPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + // Let sending fail due to media preprocessing error + mediaPreProcessor.givenResult(Result.failure(Exception())) + awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) + + assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) + + awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState().toSendingState()) + assertThat(showSendFailureDialog).isTrue() + } + + awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState()) + assertThat(showSendFailureDialog).isTrue() + eventSink(VoiceMessageComposerEvents.DismissSendFailureDialog) + } + + val finalState = awaitItem().apply { + assertThat(voiceMessageState).isEqualTo(aPreviewState()) + assertThat(showSendFailureDialog).isFalse() + } + + + assertThat(matrixRoom.sendMediaCount).isEqualTo(0) + testPauseAndDestroy(finalState) + } + } + @Test fun `present - send error - missing recording is tracked`() = runTest { val presenter = createVoiceMessageComposerPresenter() diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2251ff798a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Day-0_0_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:083476cb480c6641924331689121581300247fd9d1f61c155a35389a07601c69 +size 51237 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a4bc4d4f6b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl_MessagesView_null_MessagesView-Night-0_1_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33b0fb2e0990a008ea6699a0d0fc59478bc3ce755637584af4e2577fc007e2c3 +size 45809 From 96e4106410a92bcb5af55ff12508c0ab6f19db7a Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Tue, 14 Nov 2023 16:23:51 +0100 Subject: [PATCH 034/102] Allow to seek a voice message before playing it (#1780) --- .../timeline/VoiceMessagePlayer.kt | 48 ++++++----- .../timeline/VoiceMessagePresenter.kt | 13 ++- .../timeline/DefaultVoiceMessagePlayerTest.kt | 33 ++++++++ .../timeline/VoiceMessagePresenterTest.kt | 82 ++++++++++++++++++- 4 files changed, 152 insertions(+), 24 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt index f96342a308..57607507c9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt @@ -17,10 +17,10 @@ package io.element.android.features.messages.impl.voicemessages.timeline import com.squareup.anvil.annotations.ContributesBinding -import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.mediaplayer.api.MediaPlayer import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -75,11 +75,14 @@ interface VoiceMessagePlayer { fun pause() /** - * Seek to a specific position. + * Seek to a specific position acquiring control of the + * underlying [MediaPlayer] if needed. + * + * Will suspend whilst the media file is being downloaded. * * @param positionMs The position in milliseconds. */ - fun seekTo(positionMs: Long) + suspend fun seekTo(positionMs: Long): Result data class State( /** @@ -145,22 +148,8 @@ class DefaultVoiceMessagePlayer( ) }.distinctUntilChanged() - override suspend fun play(): Result = if (inControl()) { + override suspend fun play(): Result = acquireControl { mediaPlayer.play() - Result.success(Unit) - } else { - if (eventId != null) { - repo.getMediaFile().mapCatching { mediaFile -> - mediaPlayer.setMedia( - uri = mediaFile.path, - mediaId = eventId.value, - mimeType = "audio/ogg", // Files in the voice cache have no extension so we need to set the mime type manually. - ) - mediaPlayer.play() - } - } else { - Result.failure(IllegalStateException("Cannot play a voice message with no eventId")) - } } override fun pause() { @@ -169,10 +158,8 @@ class DefaultVoiceMessagePlayer( } } - override fun seekTo(positionMs: Long) { - ifInControl { - mediaPlayer.seekTo(positionMs) - } + override suspend fun seekTo(positionMs: Long): Result = acquireControl { + mediaPlayer.seekTo(positionMs) } private fun String?.isMyTrack(): Boolean = if (eventId == null) false else this == eventId.value @@ -182,4 +169,21 @@ class DefaultVoiceMessagePlayer( } private fun inControl(): Boolean = mediaPlayer.state.value.mediaId.isMyTrack() + + private suspend inline fun acquireControl(onReady: (state: MediaPlayer.State) -> Unit): Result = if (inControl()) { + onReady(mediaPlayer.state.value) + Result.success(Unit) + } else { + if (eventId != null) { + repo.getMediaFile().mapCatching { mediaFile -> + mediaPlayer.setMedia( + uri = mediaFile.path, + mediaId = eventId.value, + mimeType = "audio/ogg" // Files in the voice cache have no extension so we need to set the mime type manually. + ).let(onReady) + } + } else { + Result.failure(IllegalStateException("Cannot acquireControl on a voice message with no eventId")) + } + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 5df804c8ac..d0f7f3b2c1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -126,7 +126,18 @@ class VoiceMessagePresenter @AssistedInject constructor( } } is VoiceMessageEvents.Seek -> { - player.seekTo((event.percentage * content.duration.toMillis()).toLong()) + scope.launch { + play.runUpdatingState( + errorTransform = { + analyticsService.trackError( + VoiceMessageException.PlayMessageError("Error while trying to seek voice message", it) + ) + it + }, + ) { + player.seekTo((event.percentage * content.duration.toMillis()).toLong()) + } + } } } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt index 78c34cd60b..62f117fcc9 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/DefaultVoiceMessagePlayerTest.kt @@ -118,6 +118,39 @@ class DefaultVoiceMessagePlayerTest { } } + @Test + fun `download and seek to works`() = runTest { + val player = createDefaultVoiceMessagePlayer() + player.state.test { + skipItems(1) // skip initial state. + Truth.assertThat(player.seekTo(2000).isSuccess).isTrue() + player.seekTo(2000) + awaitItem().let { + Truth.assertThat(it.isPlaying).isEqualTo(false) + Truth.assertThat(it.isMyMedia).isEqualTo(true) + Truth.assertThat(it.currentPosition).isEqualTo(0) + } + awaitItem().let { + Truth.assertThat(it.isPlaying).isEqualTo(false) + Truth.assertThat(it.isMyMedia).isEqualTo(true) + Truth.assertThat(it.currentPosition).isEqualTo(2000) + } + } + } + + @Test + fun `download and seek to fails`() = runTest { + val player = createDefaultVoiceMessagePlayer( + voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { + shouldFail = true + }, + ) + player.state.test { + skipItems(1) // skip initial state. + Truth.assertThat(player.seekTo(2000).isFailure).isTrue() + } + } + @Test fun `seek to works`() = runTest { val player = createDefaultVoiceMessagePlayer() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt index 26f4232531..af8dbfc1d4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -114,7 +114,10 @@ class VoiceMessagePresenterTest { Truth.assertThat(it.time).isEqualTo("0:02") } analyticsService.trackedErrors.first().also { - Truth.assertThat(it).isInstanceOf(VoiceMessageException.PlayMessageError::class.java) + Truth.assertThat(it).apply { + isInstanceOf(VoiceMessageException.PlayMessageError::class.java) + hasMessageThat().isEqualTo("Error while trying to play voice message") + } } } } @@ -167,6 +170,83 @@ class VoiceMessagePresenterTest { } } + @Test + fun `seeking downloads and seeks`() = runTest { + val presenter = createVoiceMessagePresenter( + content = aTimelineItemVoiceContent(durationMs = 10_000), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:10") + } + + initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) + + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:10") + } + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:00") + } + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + Truth.assertThat(it.progress).isEqualTo(0.5f) + Truth.assertThat(it.time).isEqualTo("0:05") + } + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + Truth.assertThat(it.progress).isEqualTo(0.5f) + Truth.assertThat(it.time).isEqualTo("0:05") + } + } + } + + @Test + fun `seeking downloads and fails`() = runTest { + val analyticsService = FakeAnalyticsService() + val presenter = createVoiceMessagePresenter( + voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true }, + analyticsService = analyticsService, + content = aTimelineItemVoiceContent(durationMs = 10_000), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:10") + } + + initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) + + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:10") + } + awaitItem().also { + Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry) + Truth.assertThat(it.progress).isEqualTo(0f) + Truth.assertThat(it.time).isEqualTo("0:10") + } + analyticsService.trackedErrors.first().also { + Truth.assertThat(it).apply { + isInstanceOf(VoiceMessageException.PlayMessageError::class.java) + hasMessageThat().isEqualTo("Error while trying to seek voice message") + } + } + } + } + @Test fun `seeking seeks`() = runTest { val presenter = createVoiceMessagePresenter( From 5209627f67ca0422148a88e9dca4db99d17d0f21 Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Tue, 14 Nov 2023 16:21:58 +0000 Subject: [PATCH 035/102] Fix merge conflict (#1804) --- .../composer/VoiceMessageComposerPresenterTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 8fb72d0e19..4ec1432377 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -445,8 +445,8 @@ class VoiceMessageComposerPresenterTest { }.test { // Let sending fail due to media preprocessing error mediaPreProcessor.givenResult(Result.failure(Exception())) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) - awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem().eventSink(VoiceMessageComposerEvents.RecorderEvent(VoiceMessageRecorderEvent.Stop)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) From 2cb0060f96a61178eab5faf66469a62956935faa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 17:06:19 +0100 Subject: [PATCH 036/102] Add a View to show the beginning of the timeline (parity with iOS) --- changelog.d/1801.misc | 1 + .../features/messages/impl/MessagesView.kt | 1 + .../impl/timeline/TimelineStateProvider.kt | 6 +- .../messages/impl/timeline/TimelineView.kt | 8 +++ .../virtual/TimelineItemRoomBeginningView.kt | 72 +++++++++++++++++++ .../src/main/res/values-cs/translations.xml | 2 + .../src/main/res/values-de/translations.xml | 2 + .../src/main/res/values-es/translations.xml | 2 + .../src/main/res/values-fr/translations.xml | 2 + .../src/main/res/values-it/translations.xml | 2 + .../src/main/res/values-ro/translations.xml | 2 + .../src/main/res/values-ru/translations.xml | 2 + .../src/main/res/values-sk/translations.xml | 2 + .../impl/src/main/res/values/localazy.xml | 3 + .../impl/src/main/res/values/localazy.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-cs/translations.xml | 2 + .../matrix/api/timeline/MatrixTimeline.kt | 3 +- .../impl/timeline/RustMatrixTimeline.kt | 10 ++- .../TimelineEncryptedHistoryPostProcessor.kt | 3 +- ...melineEncryptedHistoryPostProcessorTest.kt | 24 ++++++- .../test/timeline/FakeMatrixTimeline.kt | 6 +- .../src/main/res/values-cs/translations.xml | 4 ++ .../src/main/res/values-cs/translations.xml | 6 +- .../src/main/res/values-de/translations.xml | 2 - .../src/main/res/values-es/translations.xml | 2 - .../src/main/res/values-fr/translations.xml | 2 - .../src/main/res/values-it/translations.xml | 2 - .../src/main/res/values-ro/translations.xml | 2 - .../src/main/res/values-ru/translations.xml | 2 - .../src/main/res/values-sk/translations.xml | 2 - .../src/main/res/values/localazy.xml | 2 - tools/localazy/config.json | 1 + 33 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 changelog.d/1801.misc create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt diff --git a/changelog.d/1801.misc b/changelog.d/1801.misc new file mode 100644 index 0000000000..798f2ae60f --- /dev/null +++ b/changelog.d/1801.misc @@ -0,0 +1 @@ +Add item "This is the beginning of..." at the beginning of the timeline. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 9c8a9dab8a..c9063f2a79 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -373,6 +373,7 @@ private fun MessagesViewContent( TimelineView( modifier = Modifier.padding(paddingValues), state = state.timelineState, + roomName = state.roomName.dataOrNull(), onMessageClicked = onMessageClicked, onMessageLongClicked = onMessageLongClicked, onUserDataClicked = onUserDataClicked, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 0e1795117f..affb77e88a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -43,7 +43,11 @@ import kotlin.random.Random fun aTimelineState(timelineItems: ImmutableList = persistentListOf()) = TimelineState( timelineItems = timelineItems, - paginationState = MatrixTimeline.PaginationState(isBackPaginating = false, hasMoreToLoadBackwards = true), + paginationState = MatrixTimeline.PaginationState( + isBackPaginating = false, + hasMoreToLoadBackwards = true, + beginningOfRoomReached = false, + ), highlightedEventId = null, userHasPermissionToSendMessage = true, hasNewItems = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 6d1929f825..5b6b8c6d1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -59,6 +59,7 @@ import io.element.android.features.messages.impl.timeline.components.TimelineIte import io.element.android.features.messages.impl.timeline.components.TimelineItemStateEventRow import io.element.android.features.messages.impl.timeline.components.TimelineItemVirtualRow import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView +import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemRoomBeginningView import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories @@ -82,6 +83,7 @@ import kotlinx.coroutines.launch @Composable fun TimelineView( state: TimelineState, + roomName: String?, onUserDataClicked: (UserId) -> Unit, onMessageClicked: (TimelineItem.Event) -> Unit, onMessageLongClicked: (TimelineItem.Event) -> Unit, @@ -148,6 +150,11 @@ fun TimelineView( } } } + if (state.paginationState.beginningOfRoomReached) { + item(contentType = "BeginningOfRoomReached") { + TimelineItemRoomBeginningView(roomName = roomName) + } + } } TimelineScrollHelper( @@ -346,6 +353,7 @@ internal fun TimelineViewPreview( ) { TimelineView( state = aTimelineState(timelineItems), + roomName = null, onMessageClicked = {}, onTimestampClicked = {}, onUserDataClicked = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt new file mode 100644 index 0000000000..f400c214a3 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.virtual + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.ElementTheme + +@Composable +fun TimelineItemRoomBeginningView( + roomName: String?, + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + contentAlignment = Alignment.Center, + ) { + val text = if (roomName == null) { + stringResource(id = R.string.room_timeline_beginning_of_room_no_name) + } else { + stringResource(id = R.string.room_timeline_beginning_of_room, roomName) + } + Text( + color = MaterialTheme.colorScheme.secondary, + style = ElementTheme.typography.fontBodyMdRegular, + text = text, + textAlign = TextAlign.Center, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview { + Column { + TimelineItemRoomBeginningView( + roomName = null, + ) + TimelineItemRoomBeginningView( + roomName = "Room Name", + ) + } +} diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index a59dcdca59..4d5ce6f4b6 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -15,6 +15,8 @@ "Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy." "Důvod nahlášení tohoto obsahu" + "Toto je začátek %1$s." + "Toto je začátek této konverzace." "Informujte celou místnost" "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Fotoaparát" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index cb446ff6b4..0766f80ee0 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -14,6 +14,8 @@ "Diese Meldung wird an den Administrator deines Homeservers weitergeleitet. Dieser kann keine verschlüsselten Nachrichten lesen." "Grund für die Meldung dieses Inhalts" + "Dies ist der Anfang von %1$s." + "Dies ist der Anfang dieses Gesprächs." "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Kamera" "Foto machen" diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index 6a0b285638..fec3f48ccf 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -14,6 +14,8 @@ "Este mensaje se notificará al administrador de su homeserver. No podrán leer ningún mensaje cifrado." "Motivo para denunciar este contenido" + "Este es el principio de %1$s." + "Este es el principio de esta conversación." "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" "Bloquear usuario" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 8c3babbcf8..0cbd8b7b66 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -14,6 +14,8 @@ "Ce message sera signalé à l’administrateur de votre serveur d’accueil. Il ne pourra lire aucun message chiffré." "Raison du signalement de ce contenu" + "Ceci est le début de %1$s." + "Ceci est le début de cette conversation." "Notifier tout le salon" "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Appareil photo" diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml index 735ecbe913..acafb0e8d6 100644 --- a/features/messages/impl/src/main/res/values-it/translations.xml +++ b/features/messages/impl/src/main/res/values-it/translations.xml @@ -14,6 +14,8 @@ "Questo messaggio verrà segnalato all\'amministratore dell\'homeserver. Questi non sarà in grado di leggere i messaggi criptati." "Motivo della segnalazione di questo contenuto" + "Questo è l\'inizio di %1$s." + "Questo è l\'inizio della conversazione." "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" "Blocca utente" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index b23e524d77..98eec0cc69 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -15,6 +15,8 @@ "Acest mesaj va fi raportat administratorilor homeserver-ului tau. Ei nu vor putea citi niciun mesaj criptat." "Motivul raportării acestui conținut" + "Acesta este începutul conversației %1$s." + "Acesta este începutul acestei conversații." "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Cameră foto" "Faceți o fotografie" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 7fc6a14494..76b0c9cfe1 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -15,6 +15,8 @@ "Это сообщение будет передано администратору вашего домашнего сервера. Они не смогут прочитать зашифрованные сообщения." "Причина, по которой вы пожаловались на этот контент" + "Это начало %1$s." + "Это начало разговора." "Уведомить всю комнату" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Камера" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 8c20bcf252..3d81d4eabc 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -15,6 +15,8 @@ "Táto správa bude nahlásená správcovi vášho domovského servera. Nebude môcť prečítať žiadne šifrované správy." "Dôvod nahlásenia tohto obsahu" + "Toto je začiatok %1$s." + "Toto je začiatok tejto konverzácie." "Informovať celú miestnosť" "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Kamera" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index cbc5f127e9..b95a341b36 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -14,6 +14,8 @@ "This message will be reported to your homeserver’s administrator. They will not be able to read any encrypted messages." "Reason for reporting this content" + "This is the beginning of %1$s." + "This is the beginning of this conversation." "Notify the whole room" "Check if you want to hide all current and future messages from this user" "Camera" @@ -41,6 +43,7 @@ "An error occurred while loading notification settings." "Failed restoring the default mode, please try again." "Failed setting the mode, please try again." + "Your homeserver does not support this option in encrypted rooms, you won\'t get notified in this room." "All messages" "In this room, notify me for" "Show less" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 153c64f661..9cb6aaf698 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -28,6 +28,7 @@ If you proceed, some of your settings may change." "Enable notifications on this device" "The configuration has not been corrected, please try again." "Group chats" + "Your homeserver does not support this option in encrypted rooms, you may not get notified in some rooms." "Mentions" "All" "Mentions" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 70c31ac65e..5363c6a78e 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -35,6 +35,7 @@ "An error occurred while loading notification settings." "Failed restoring the default mode, please try again." "Failed setting the mode, please try again." + "Your homeserver does not support this option in encrypted rooms, you won\'t get notified in this room." "All messages" "In this room, notify me for" "Block" diff --git a/features/securebackup/impl/src/main/res/values-cs/translations.xml b/features/securebackup/impl/src/main/res/values-cs/translations.xml index 5ae1d4b0ec..a34d77aeee 100644 --- a/features/securebackup/impl/src/main/res/values-cs/translations.xml +++ b/features/securebackup/impl/src/main/res/values-cs/translations.xml @@ -22,6 +22,8 @@ "Klíč pro obnovení byl změněn" "Změnit klíč pro obnovení?" "Zadejte klíč pro obnovení a potvrďte přístup k záloze chatu." + "Zkuste prosím znovu potvrdit přístup k záloze chatu." + "Nesprávný klíč pro obnovení" "Zadejte kód o délce 48 znaků." "Zadejte…" "Klíč pro obnovení potvrzen" diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt index af411216e5..5fa55c357f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/MatrixTimeline.kt @@ -24,7 +24,8 @@ interface MatrixTimeline { data class PaginationState( val isBackPaginating: Boolean, - val hasMoreToLoadBackwards: Boolean + val hasMoreToLoadBackwards: Boolean, + val beginningOfRoomReached: Boolean, ) { val canBackPaginate = !isBackPaginating && hasMoreToLoadBackwards } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 53ae41d887..85e6905f0a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -72,7 +72,11 @@ class RustMatrixTimeline( MutableStateFlow(emptyList()) private val _paginationState = MutableStateFlow( - MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false) + MatrixTimeline.PaginationState( + hasMoreToLoadBackwards = true, + isBackPaginating = false, + beginningOfRoomReached = false, + ) ) private val encryptedHistoryPostProcessor = TimelineEncryptedHistoryPostProcessor( @@ -155,6 +159,7 @@ class RustMatrixTimeline( return@getAndUpdate currentPaginationState.copy( isBackPaginating = false, hasMoreToLoadBackwards = false, + beginningOfRoomReached = false, ) } when (status) { @@ -173,7 +178,8 @@ class RustMatrixTimeline( BackPaginationStatus.TIMELINE_START_REACHED -> { currentPaginationState.copy( isBackPaginating = false, - hasMoreToLoadBackwards = false + hasMoreToLoadBackwards = false, + beginningOfRoomReached = true, ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt index 60de981780..25241ff3e1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessor.kt @@ -45,7 +45,8 @@ class TimelineEncryptedHistoryPostProcessor( paginationStateFlow.getAndUpdate { it.copy( isBackPaginating = false, - hasMoreToLoadBackwards = false + hasMoreToLoadBackwards = false, + beginningOfRoomReached = false, ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt index d3af9a5670..cf5c3682c2 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/timeline/postprocessor/TimelineEncryptedHistoryPostProcessorTest.kt @@ -98,7 +98,13 @@ class TimelineEncryptedHistoryPostProcessorTest { @Test fun `given a list with several with lower or equal timestamps than lastLoginTimestamp, they're replaced and the user can't back paginate`() = runTest { - val paginationStateFlow = MutableStateFlow(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false)) + val paginationStateFlow = MutableStateFlow( + MatrixTimeline.PaginationState( + hasMoreToLoadBackwards = true, + isBackPaginating = false, + beginningOfRoomReached = false, + ) + ) val processor = createPostProcessor(paginationStateFlow = paginationStateFlow) val items = listOf( MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time - 1)), @@ -111,7 +117,13 @@ class TimelineEncryptedHistoryPostProcessorTest { MatrixTimelineItem.Event(0L, anEventTimelineItem(timestamp = defaultLastLoginTimestamp.time + 1)) ) ) - assertThat(paginationStateFlow.value).isEqualTo(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = false, isBackPaginating = false)) + assertThat(paginationStateFlow.value).isEqualTo( + MatrixTimeline.PaginationState( + hasMoreToLoadBackwards = false, + isBackPaginating = false, + beginningOfRoomReached = false, + ) + ) } private fun TestScope.createPostProcessor( @@ -119,7 +131,13 @@ class TimelineEncryptedHistoryPostProcessorTest { isRoomEncrypted: Boolean = true, isKeyBackupEnabled: Boolean = false, paginationStateFlow: MutableStateFlow = - MutableStateFlow(MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false)) + MutableStateFlow( + MatrixTimeline.PaginationState( + hasMoreToLoadBackwards = true, + isBackPaginating = false, + beginningOfRoomReached = false, + ) + ) ) = TimelineEncryptedHistoryPostProcessor( lastLoginTimestamp = lastLoginTimestamp, isRoomEncrypted = isRoomEncrypted, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt index 73bc5fb597..6cf6f05cbe 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeMatrixTimeline.kt @@ -29,7 +29,11 @@ import kotlinx.coroutines.flow.getAndUpdate class FakeMatrixTimeline( initialTimelineItems: List = emptyList(), - initialPaginationState: MatrixTimeline.PaginationState = MatrixTimeline.PaginationState(hasMoreToLoadBackwards = true, isBackPaginating = false) + initialPaginationState: MatrixTimeline.PaginationState = MatrixTimeline.PaginationState( + hasMoreToLoadBackwards = true, + isBackPaginating = false, + beginningOfRoomReached = false, + ) ) : MatrixTimeline { private val _paginationState: MutableStateFlow = MutableStateFlow(initialPaginationState) diff --git a/libraries/push/impl/src/main/res/values-cs/translations.xml b/libraries/push/impl/src/main/res/values-cs/translations.xml index 84525a427e..b9488e008b 100644 --- a/libraries/push/impl/src/main/res/values-cs/translations.xml +++ b/libraries/push/impl/src/main/res/values-cs/translations.xml @@ -8,6 +8,10 @@ "Vstoupit" "Odmítnout" "Vás pozval(a) do chatu" + "%1$s vás zmínil(a). +%2$s" + "Byli jste zmíněni. +%1$s" "Nové zprávy" "Reagoval(a) s %1$s" "Označit jako přečtené" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index ddb7b09671..4bcdb258a6 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -13,7 +13,7 @@ "Zobrazit heslo" "Zahájit hovor" "Uživatelské menu" - "Nahrajte hlasovou zprávu. Dvojitě klepněte a podržte pro záznam. Uvolněním ukončíte nahrávání." + "Nahrajte hlasovou zprávu." "Zastavit nahrávání" "Přijmout" "Přidat na časovou osu" @@ -116,6 +116,7 @@ "Odkaz zkopírován do schránky" "Načítání…" "Zpráva" + "Akce zprávy" "Rozložení zprávy" "Zpráva byla odstraněna" "Moderní" @@ -176,6 +177,7 @@ "Čekání na dešifrovací klíč" "Opravdu chcete ukončit toto hlasování?" "Hlasování: %1$s" + "Ověřit zařízení" "Potvrzení" "Upozornění" "Vytvoření trvalého odkazu se nezdařilo" @@ -207,8 +209,6 @@ "%d hlasů" "Zatřeste zařízením pro nahlášení chyby" - "Toto je začátek %1$s." - "Toto je začátek této konverzace." "Nové" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 2fa0c20ff5..09fdba6432 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -175,8 +175,6 @@ "%d Stimmen" "Schüttel heftig zum Melden von Fehlern" - "Dies ist der Anfang von %1$s." - "Dies ist der Anfang dieses Gesprächs." "Neu" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index f901b48cb3..9b2c35a63b 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -105,8 +105,6 @@ "%1$d miembros" "Agitar con fuerza para informar de un error" - "Este es el principio de %1$s." - "Este es el principio de esta conversación." "Nuevos" "Versión: %1$s (%2$s)" "es" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index cc0adae38d..39f2c116d2 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -202,8 +202,6 @@ "%d votes" "Rageshake pour signaler un problème" - "Ceci est le début de %1$s." - "Ceci est le début de cette conversation." "Nouveau" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index e8e35c284e..a49e6c7d37 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -105,8 +105,6 @@ "%1$d membri" "Scuoti per segnalare un problema" - "Questo è l\'inizio di %1$s." - "Questo è l\'inizio della conversazione." "Nuovo" "Versione: %1$s (%2$s)" "it" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 863d4cd097..7fbb5a322a 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -159,8 +159,6 @@ "%d voturi" "Rageshake pentru a raporta erori" - "Acesta este începutul conversației %1$s." - "Acesta este începutul acestei conversații." "Nou" "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 87f4d74201..5b105c8372 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -209,8 +209,6 @@ "%d голосов" "Rageshake сообщит об ошибке" - "Это начало %1$s." - "Это начало разговора." "Новый" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 4f38c2fb8b..f7a28ddc50 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -209,8 +209,6 @@ "%d hlasov" "Zúrivo potriasť pre nahlásenie chyby" - "Toto je začiatok %1$s." - "Toto je začiatok tejto konverzácie." "Nové" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 156b5e5b17..63909fe9ca 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -206,8 +206,6 @@ "%d votes" "Rageshake to report bug" - "This is the beginning of %1$s." - "This is the beginning of this conversation." "New" "Failed selecting media, please try again." "Failed processing media to upload, please try again." diff --git a/tools/localazy/config.json b/tools/localazy/config.json index e95892ac52..95cf40d76f 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -121,6 +121,7 @@ { "name": ":features:messages:impl", "includeRegex": [ + "room_timeline_beginning_.*", "screen_room_.*", "screen\\.room\\..*", "screen_dm_details_.*", From be2bd999839de74017cf8c443c4e188fee4453db Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 14 Nov 2023 16:15:38 +0000 Subject: [PATCH 037/102] Update screenshots --- ...ineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png | 3 +++ ...eItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png | 3 +++ ...ineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png} | 0 ...eLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png} | 0 ...null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png} | 0 ...ll_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png} | 0 6 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0f29064f65 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a0fbf492a08f213d4603e9a7d4a0670cd4174441faa1a7b54e6793c234fde6a +size 15830 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0075b63b12 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14316aa45f398154680fde84673a4d1117c49317a0e81641d52184da7b5ff2e1 +size 15472 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-50_50_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-50_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-51_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-51_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png From eb6252bed39cbed88edf643544d0bfc28e5b1b15 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 18:10:26 +0100 Subject: [PATCH 038/102] Timeline: render TimelineItemReadMarkerModel --- .../components/TimelineItemVirtualRow.kt | 3 +- .../virtual/TimelineItemReadMarkerView.kt | 64 +++++++++++++++++++ .../src/main/res/values-cs/translations.xml | 1 + .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-es/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 1 + .../src/main/res/values-it/translations.xml | 1 + .../src/main/res/values-ro/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 1 + .../src/main/res/values-sk/translations.xml | 1 + .../main/res/values-zh-rTW/translations.xml | 1 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-cs/translations.xml | 1 - .../src/main/res/values-de/translations.xml | 1 - .../src/main/res/values-es/translations.xml | 1 - .../src/main/res/values-fr/translations.xml | 1 - .../src/main/res/values-it/translations.xml | 1 - .../src/main/res/values-ro/translations.xml | 1 - .../src/main/res/values-ru/translations.xml | 1 - .../src/main/res/values-sk/translations.xml | 1 - .../main/res/values-zh-rTW/translations.xml | 1 - .../src/main/res/values/localazy.xml | 1 - tools/localazy/config.json | 2 +- 23 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt index 7a476cb8a6..4bf8e5b847 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemVirtualRow.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.Modifier import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.features.messages.impl.timeline.components.virtual.TimelineEncryptedHistoryBannerView import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemDaySeparatorView +import io.element.android.features.messages.impl.timeline.components.virtual.TimelineItemReadMarkerView import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemDaySeparatorModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemEncryptedHistoryBannerVirtualModel @@ -34,7 +35,7 @@ fun TimelineItemVirtualRow( ) { when (virtual.model) { is TimelineItemDaySeparatorModel -> TimelineItemDaySeparatorView(virtual.model, modifier) - TimelineItemReadMarkerModel -> return + TimelineItemReadMarkerModel -> TimelineItemReadMarkerView() is TimelineItemEncryptedHistoryBannerVirtualModel -> TimelineEncryptedHistoryBannerView(sessionState, modifier) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt new file mode 100644 index 0000000000..ac58b14e60 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemReadMarkerView.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.virtual + +import androidx.compose.foundation.layout.Arrangement.spacedBy +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import io.element.android.features.messages.impl.R +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.HorizontalDivider +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.theme.ElementTheme + +@Composable +internal fun TimelineItemReadMarkerView( + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 12.dp, horizontal = 18.dp), + horizontalAlignment = Alignment.End, + verticalArrangement = spacedBy(4.dp), + ) { + Text( + text = stringResource(id = R.string.room_timeline_read_marker_title).uppercase(), + style = ElementTheme.typography.fontBodySmMedium, + color = ElementTheme.colors.textSecondary, + ) + HorizontalDivider( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 2.dp), + color = ElementTheme.colors.borderInteractivePrimary, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun TimelineItemReadMarkerViewPreview() = ElementPreview { + TimelineItemReadMarkerView() +} diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml index 4d5ce6f4b6..d8231ab1ee 100644 --- a/features/messages/impl/src/main/res/values-cs/translations.xml +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -17,6 +17,7 @@ "Důvod nahlášení tohoto obsahu" "Toto je začátek %1$s." "Toto je začátek této konverzace." + "Nové" "Informujte celou místnost" "Zaškrtněte, pokud chcete skrýt všechny aktuální a budoucí zprávy od tohoto uživatele" "Fotoaparát" diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 0766f80ee0..16580c21c5 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -16,6 +16,7 @@ "Grund für die Meldung dieses Inhalts" "Dies ist der Anfang von %1$s." "Dies ist der Anfang dieses Gesprächs." + "Neu" "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" "Kamera" "Foto machen" diff --git a/features/messages/impl/src/main/res/values-es/translations.xml b/features/messages/impl/src/main/res/values-es/translations.xml index fec3f48ccf..493fdb7ad7 100644 --- a/features/messages/impl/src/main/res/values-es/translations.xml +++ b/features/messages/impl/src/main/res/values-es/translations.xml @@ -16,6 +16,7 @@ "Motivo para denunciar este contenido" "Este es el principio de %1$s." "Este es el principio de esta conversación." + "Nuevos" "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" "Bloquear usuario" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 0cbd8b7b66..2c9ab7574c 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -16,6 +16,7 @@ "Raison du signalement de ce contenu" "Ceci est le début de %1$s." "Ceci est le début de cette conversation." + "Nouveau" "Notifier tout le salon" "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." "Appareil photo" diff --git a/features/messages/impl/src/main/res/values-it/translations.xml b/features/messages/impl/src/main/res/values-it/translations.xml index acafb0e8d6..c36e8c515c 100644 --- a/features/messages/impl/src/main/res/values-it/translations.xml +++ b/features/messages/impl/src/main/res/values-it/translations.xml @@ -16,6 +16,7 @@ "Motivo della segnalazione di questo contenuto" "Questo è l\'inizio di %1$s." "Questo è l\'inizio della conversazione." + "Nuovo" "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" "Blocca utente" diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index 98eec0cc69..c15a465976 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -17,6 +17,7 @@ "Motivul raportării acestui conținut" "Acesta este începutul conversației %1$s." "Acesta este începutul acestei conversații." + "Nou" "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Cameră foto" "Faceți o fotografie" diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index 76b0c9cfe1..a1ce610568 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -17,6 +17,7 @@ "Причина, по которой вы пожаловались на этот контент" "Это начало %1$s." "Это начало разговора." + "Новый" "Уведомить всю комнату" "Отметьте, хотите ли вы скрыть все текущие и будущие сообщения от этого пользователя" "Камера" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index 3d81d4eabc..fd7efc1200 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -17,6 +17,7 @@ "Dôvod nahlásenia tohto obsahu" "Toto je začiatok %1$s." "Toto je začiatok tejto konverzácie." + "Nové" "Informovať celú miestnosť" "Označte, či chcete skryť všetky aktuálne a budúce správy od tohto používateľa" "Kamera" diff --git a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml index 9eb7664c47..4f22c5bb7a 100644 --- a/features/messages/impl/src/main/res/values-zh-rTW/translations.xml +++ b/features/messages/impl/src/main/res/values-zh-rTW/translations.xml @@ -12,6 +12,7 @@ "%1$d 個聊天室變更" "檢舉這個內容的原因" + "新訊息" "照相機" "拍照" "錄影" diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml index b95a341b36..d9c5fce030 100644 --- a/features/messages/impl/src/main/res/values/localazy.xml +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -16,6 +16,7 @@ "Reason for reporting this content" "This is the beginning of %1$s." "This is the beginning of this conversation." + "New" "Notify the whole room" "Check if you want to hide all current and future messages from this user" "Camera" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index 4bcdb258a6..9889bf4697 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -209,7 +209,6 @@ "%d hlasů" "Zatřeste zařízením pro nahlášení chyby" - "Nové" "Výběr média se nezdařil, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." "Nahrání média se nezdařilo, zkuste to prosím znovu." diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 09fdba6432..e473e6efb9 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -175,7 +175,6 @@ "%d Stimmen" "Schüttel heftig zum Melden von Fehlern" - "Neu" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." "Fehler beim Verarbeiten des hochgeladenen Mediums. Bitte versuche es erneut." "Das Hochladen der Medien ist fehlgeschlagen. Bitte versuche es erneut." diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index 9b2c35a63b..eaab1988d5 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -105,7 +105,6 @@ "%1$d miembros" "Agitar con fuerza para informar de un error" - "Nuevos" "Versión: %1$s (%2$s)" "es" "Error" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index 39f2c116d2..ac67487989 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -202,7 +202,6 @@ "%d votes" "Rageshake pour signaler un problème" - "Nouveau" "Échec de la sélection du média, veuillez réessayer." "Échec du traitement des médias à télécharger, veuillez réessayer." "Échec du téléchargement du média, veuillez réessayer." diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index a49e6c7d37..cfd24d7c08 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -105,7 +105,6 @@ "%1$d membri" "Scuoti per segnalare un problema" - "Nuovo" "Versione: %1$s (%2$s)" "it" "Errore" diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index 7fbb5a322a..cddf19ae13 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -159,7 +159,6 @@ "%d voturi" "Rageshake pentru a raporta erori" - "Nou" "Selectarea fișierelor media a eșuat, încercați din nou." "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." "Încărcarea fișierelor media a eșuat, încercați din nou." diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 5b105c8372..309e463faf 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -209,7 +209,6 @@ "%d голосов" "Rageshake сообщит об ошибке" - "Новый" "Не удалось выбрать носитель, попробуйте еще раз." "Не удалось обработать медиафайл для загрузки, попробуйте еще раз." "Не удалось загрузить медиафайлы, попробуйте еще раз." diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index f7a28ddc50..0ccbab9cb8 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -209,7 +209,6 @@ "%d hlasov" "Zúrivo potriasť pre nahlásenie chyby" - "Nové" "Nepodarilo sa vybrať médium, skúste to prosím znova." "Nepodarilo sa spracovať médiá na odoslanie, skúste to prosím znova." "Nepodarilo sa nahrať médiá, skúste to prosím znova." diff --git a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml index d86a2b0265..6f6fa08892 100644 --- a/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh-rTW/translations.xml @@ -167,7 +167,6 @@ "%d 票" - "新訊息" "無法上傳媒體檔案,請稍後再試。" "分享位置" "分享我的位置" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 63909fe9ca..6eb51a87c8 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -206,7 +206,6 @@ "%d votes" "Rageshake to report bug" - "New" "Failed selecting media, please try again." "Failed processing media to upload, please try again." "Failed uploading media, please try again." diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 95cf40d76f..86ed61ec78 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -125,7 +125,7 @@ "screen_room_.*", "screen\\.room\\..*", "screen_dm_details_.*", - "room_timeline_state_changes", + "room_timeline_.*", "emoji_picker_category_.*", ".*report_content_.*" ], From d380cff645b960694dd4eb9e4f0e830df9e150d4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 18:25:05 +0100 Subject: [PATCH 039/102] Simplify config for Localazy --- tools/localazy/config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 86ed61ec78..e611db953c 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -121,11 +121,10 @@ { "name": ":features:messages:impl", "includeRegex": [ - "room_timeline_beginning_.*", + "room_timeline_.*", "screen_room_.*", "screen\\.room\\..*", "screen_dm_details_.*", - "room_timeline_.*", "emoji_picker_category_.*", ".*report_content_.*" ], From 97bbc37f6c36686b79d89c52db6647280400571b Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 14 Nov 2023 20:59:17 +0100 Subject: [PATCH 040/102] LockScreen : avoid removing from composition the LoggedInFlowNode.Children when LockScreen is displayed. --- .../io/element/android/x/MainActivity.kt | 15 ++++++- .../android/appnav/LoggedInFlowNode.kt | 29 ++++++-------- .../MoveActivityToBackgroundBackHandler.kt | 39 ------------------- .../lockscreen/api/LockScreenService.kt | 6 +++ 4 files changed, 32 insertions(+), 57 deletions(-) delete mode 100644 appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 7fcae7040e..2f020a89ac 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -33,6 +33,7 @@ import com.bumble.appyx.core.integration.NodeHost import com.bumble.appyx.core.integrationpoint.NodeActivity import com.bumble.appyx.core.plugin.NodeReadyObserver import io.element.android.features.lockscreen.api.handleSecureFlag +import io.element.android.features.lockscreen.api.isLocked import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher @@ -46,7 +47,6 @@ private val loggerTag = LoggerTag("MainActivity") class MainActivity : NodeActivity() { private lateinit var mainNode: MainNode - private lateinit var appBindings: AppBindings override fun onCreate(savedInstanceState: Bundle?) { @@ -61,6 +61,19 @@ class MainActivity : NodeActivity() { } } + @Deprecated("") + override fun onBackPressed() { + // If the app is locked, we need to intercept onBackPressed before it goes to OnBackPressedDispatcher. + // Indeed, otherwise we would need to trick Appyx backstack management everywhere. + // Without this trick, we would get pop operations on the hidden backstack. + if (appBindings.lockScreenService().isLocked) { + //Do not kill the app in this case, just go to background. + moveTaskToBack(false) + } else { + super.onBackPressed() + } + } + @Composable private fun MainContent(appBindings: AppBindings) { ElementTheme { diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index ed286c71b9..23c35b119a 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -362,23 +362,18 @@ class LoggedInFlowNode @AssistedInject constructor( override fun View(modifier: Modifier) { Box(modifier = modifier) { val lockScreenState by lockScreenStateService.lockState.collectAsState() - when (lockScreenState) { - LockScreenLockState.Unlocked -> { - Children( - navModel = backstack, - modifier = Modifier, - // Animate navigation to settings and to a room - transitionHandler = rememberDefaultTransitionHandler(), - ) - val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState() - if (!isFtueDisplayed) { - PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) - } - } - LockScreenLockState.Locked -> { - MoveActivityToBackgroundBackHandler() - PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LockPermanent) - } + Children( + navModel = backstack, + modifier = Modifier, + // Animate navigation to settings and to a room + transitionHandler = rememberDefaultTransitionHandler(), + ) + val isFtueDisplayed by ftueState.shouldDisplayFlow.collectAsState() + if (!isFtueDisplayed) { + PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent) + } + if (lockScreenState == LockScreenLockState.Locked) { + PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LockPermanent) } } } diff --git a/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt b/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt deleted file mode 100644 index 5d959f9464..0000000000 --- a/appnav/src/main/kotlin/io/element/android/appnav/MoveActivityToBackgroundBackHandler.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 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 io.element.android.appnav - -import android.content.Context -import android.content.ContextWrapper -import androidx.activity.ComponentActivity -import androidx.activity.compose.BackHandler -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -@Composable -fun MoveActivityToBackgroundBackHandler(enabled: Boolean = true) { - - fun Context.findActivity(): ComponentActivity? = when (this) { - is ComponentActivity -> this - is ContextWrapper -> baseContext.findActivity() - else -> null - } - - val context = LocalContext.current - BackHandler(enabled = enabled) { - context.findActivity()?.moveTaskToBack(false) - } -} diff --git a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt index 2a628585a1..4e7a03c476 100644 --- a/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt +++ b/features/lockscreen/api/src/main/kotlin/io/element/android/features/lockscreen/api/LockScreenService.kt @@ -44,6 +44,12 @@ interface LockScreenService { fun isPinSetup(): Flow } +/** + * Check if the app is currently locked. + */ +val LockScreenService.isLocked: Boolean + get() = lockState.value == LockScreenLockState.Locked + /** * Makes sure the secure flag is set on the activity if the pin is setup. * @param activity the activity to set the flag on. From 8b250fad33c95a564ad0f1ce123c0134ae3fad09 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 14 Nov 2023 21:07:58 +0100 Subject: [PATCH 041/102] #1806 add changelog --- changelog.d/1806.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1806.misc diff --git a/changelog.d/1806.misc b/changelog.d/1806.misc new file mode 100644 index 0000000000..fe3ed3f693 --- /dev/null +++ b/changelog.d/1806.misc @@ -0,0 +1 @@ +LockScreen : rework LoggedInFlowNode and back management when locked. From b4a19b91d9b1e91e5203f1a708e7d1cf8c3edd38 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 14 Nov 2023 21:38:15 +0100 Subject: [PATCH 042/102] #1806 fix compilation --- app/src/main/kotlin/io/element/android/x/MainActivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 2f020a89ac..30b20a1bd7 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -70,6 +70,7 @@ class MainActivity : NodeActivity() { //Do not kill the app in this case, just go to background. moveTaskToBack(false) } else { + @Suppress("DEPRECATION") super.onBackPressed() } } From 844768c9ec5dc2a30649ed2ecfccca13c8bb1d20 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 14 Nov 2023 21:49:58 +0000 Subject: [PATCH 043/102] Update screenshots --- ...melineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png | 3 +++ ...neItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png} | 0 ...ItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png} | 0 ...ineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png} | 0 ...eLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png} | 0 ...null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png} | 0 ...ll_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png} | 0 8 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png} (100%) diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e7d317471e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c9d54aa37eacbd7b563ce2ea460e6a548da98fb4cffe6dd765f5e7ce8037dfd +size 5364 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..80cdadfb7f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:676173205d2716d0f57354dc48e53e0e50e65e831a228845721a624b64d905ca +size 5365 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-50_50_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-50_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-51_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-51_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-52_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-52_53_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png From 2aa92b655c633c82b86481019db6997b9176e178 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Nov 2023 23:15:27 +0100 Subject: [PATCH 044/102] Format comment --- app/src/main/kotlin/io/element/android/x/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 30b20a1bd7..29f2d76527 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -67,7 +67,7 @@ class MainActivity : NodeActivity() { // Indeed, otherwise we would need to trick Appyx backstack management everywhere. // Without this trick, we would get pop operations on the hidden backstack. if (appBindings.lockScreenService().isLocked) { - //Do not kill the app in this case, just go to background. + // Do not kill the app in this case, just go to background. moveTaskToBack(false) } else { @Suppress("DEPRECATION") From a5bad53c6214e2f8bcc52ea934406fa1369cce80 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 15 Nov 2023 10:52:37 +0100 Subject: [PATCH 045/102] Iterate design on several screens: update icons, replace PreferenceTexts (#1771) - Batch import new icons from the design team. - Rename _september icons since they're just extra icons that need to be integrated in Compound in the future, and it should be ok if we don't distinguish between ic_september_*, ic_november_* etc., so all icons are now simply ic_* in the designsystem module. - Create a new CompoundIconListPreviewProvider to add chunked lists of icons for previews. Add an exception for it to Konsist. - Move some icons to use Compound icons. - Remove most PreferenceText usages, use ListItem instead. --------- Co-authored-by: ElementBot --- changelog.d/1718.misc | 1 + .../impl/root/CreateRoomRootView.kt | 2 +- .../location/impl/send/SendLocationView.kt | 10 ++-- .../impl/actionlist/ActionListView.kt | 40 +++++++------- .../actionlist/model/TimelineItemAction.kt | 12 ++--- .../impl/media/local/LocalMediaView.kt | 2 +- .../messagecomposer/AttachmentsBottomSheet.kt | 54 +++++++++++-------- .../components/MessagesReactionButton.kt | 2 +- .../components/ReplySwipeIndicator.kt | 2 +- .../components/TimelineItemReactionsLayout.kt | 2 +- .../components/TimelineItemReactionsView.kt | 2 +- .../event/TimelineItemEncryptedView.kt | 2 +- .../components/event/TimelineItemFileView.kt | 2 +- .../impl/root/PreferencesRootView.kt | 3 +- .../roomdetails/impl/RoomDetailsView.kt | 43 ++++++++------- .../impl/blockuser/BlockUserSection.kt | 40 +++++++++----- ...UserDefinedRoomNotificationSettingsView.kt | 14 ++--- .../roomlist/impl/RoomListContextMenu.kt | 47 +++++++--------- .../features/roomlist/impl/RoomListView.kt | 2 +- .../impl/components/RoomListTopBar.kt | 2 +- .../impl/setup/views/RecoveryKeyView.kt | 2 +- .../components/list/ListItemContent.kt | 9 +++- .../libraries/designsystem/icons/IconsList.kt | 42 +++++++-------- .../designsystem/icons/IconsPreview.kt | 33 ++++++------ ...r_add_reaction.xml => ic_add_reaction.xml} | 0 ...ember_attachment.xml => ic_attachment.xml} | 0 .../res/drawable/ic_compound_chat_problem.xml | 6 +-- ...video_library.xml => ic_compound_plus.xml} | 2 +- .../{ic_september_copy.xml => ic_copy.xml} | 0 .../src/main/res/drawable/ic_edit.xml | 25 +++++++++ ...r_edit_outline.xml => ic_edit_outline.xml} | 0 ...er_edit_solid_16.xml => ic_edit_solid.xml} | 0 .../res/drawable/ic_encryption_enabled.xml | 25 +++++++++ ...c_september_forward.xml => ic_forward.xml} | 0 ...tember_compose_button.xml => ic_image.xml} | 5 +- .../res/drawable/ic_location_navigator.xml | 25 +++++++++ .../ic_location_navigator_centered.xml | 25 +++++++++ ...ember_location.xml => ic_location_pin.xml} | 4 +- .../src/main/res/drawable/ic_lock_outline.xml | 18 +++---- .../src/main/res/drawable/ic_new_message.xml | 31 +++++++++++ .../{ic_september_reply.xml => ic_reply.xml} | 0 .../{ic_september_send.xml => ic_send.xml} | 0 .../ic_september_decryption_error.xml | 25 --------- .../drawable/ic_september_photo_camera.xml | 25 --------- ...to_camera.xml => ic_take_photo_camera.xml} | 0 ..._formatting.xml => ic_text_formatting.xml} | 0 .../src/main/res/drawable/ic_user.xml | 25 +++++++++ .../src/main/res/drawable/ic_user_add.xml | 28 ++++++++++ ...ember_video_call.xml => ic_video_call.xml} | 0 .../res/drawable/ic_waiting_to_decrypt.xml | 29 ++++++++++ .../ui/components/AttachmentThumbnail.kt | 2 +- .../ui/components/AvatarActionBottomSheet.kt | 18 +++---- .../ui/components/EditableAvatarView.kt | 5 +- .../libraries/matrix/ui/media/AvatarAction.kt | 4 +- .../libraries/textcomposer/TextComposer.kt | 2 +- .../textcomposer/components/SendButton.kt | 2 +- .../res/drawable/ic_developer_options.xml} | 2 +- .../impl/src/main/res/drawable/ic_devices.xml | 25 +++++++++ .../impl/src/main/res/drawable/ic_edit.xml | 25 +++++++++ .../res/drawable/ic_encryption_enabled.xml | 25 +++++++++ .../res/drawable/ic_location_navigator.xml | 25 +++++++++ .../src/main/res/drawable/ic_new_message.xml | 31 +++++++++++ .../impl/src/main/res/drawable/ic_reply.xml | 25 +++++++++ .../src/main/res/drawable/ic_sign_out.xml | 25 +++++++++ .../impl/src/main/res/drawable/ic_user.xml | 25 +++++++++ .../src/main/res/drawable/ic_user_add.xml | 28 ++++++++++ .../res/drawable/ic_waiting_to_decrypt.xml | 29 ++++++++++ .../tests/konsist/KonsistClassNameTest.kt | 2 +- ...ootView-Day-4_4_null_0,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-4_5_null_0,NEXUS_5,1.0,en].png | 4 +- ...lcomeView-Day-2_2_null,NEXUS_5,1.0,en].png | 4 +- ...omeView-Night-2_3_null,NEXUS_5,1.0,en].png | 4 +- ...ionView-Day-0_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ionView-Day-0_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ionView-Day-0_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ionView-Day-0_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ionView-Day-0_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...nView-Night-0_1_null_0,NEXUS_5,1.0,en].png | 4 +- ...nView-Night-0_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...nView-Night-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...nView-Night-0_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...nView-Night-0_1_null_4,NEXUS_5,1.0,en].png | 4 +- ...ontent-Day-1_1_null_10,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_4,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_5,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_6,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_7,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_8,NEXUS_5,1.0,en].png | 4 +- ...Content-Day-1_1_null_9,NEXUS_5,1.0,en].png | 4 +- ...tent-Night-1_2_null_10,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_3,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_4,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_5,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_6,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_7,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_8,NEXUS_5,1.0,en].png | 4 +- ...ntent-Night-1_2_null_9,NEXUS_5,1.0,en].png | 4 +- ...ickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png | 4 +- ...kerMenu-Night-4_5_null,NEXUS_5,1.0,en].png | 4 +- ...tedView-Day-29_29_null,NEXUS_5,1.0,en].png | 4 +- ...dView-Night-29_30_null,NEXUS_5,1.0,en].png | 4 +- ...ineView-Day-8_8_null_1,NEXUS_5,1.0,en].png | 4 +- ...eView-Night-8_9_null_1,NEXUS_5,1.0,en].png | 4 +- ...otViewDark--1_3_null_0,NEXUS_5,1.0,en].png | 4 +- ...otViewDark--1_3_null_1,NEXUS_5,1.0,en].png | 4 +- ...tViewLight--0_2_null_0,NEXUS_5,1.0,en].png | 4 +- ...tViewLight--0_2_null_1,NEXUS_5,1.0,en].png | 4 +- ...eView-Day-10_10_null_0,NEXUS_5,1.0,en].png | 4 +- ...iew-Night-10_11_null_0,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ditView-Day-0_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_0,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_1,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_4,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_5,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_6,NEXUS_5,1.0,en].png | 4 +- ...lsViewDark--3_5_null_0,NEXUS_5,1.0,en].png | 4 +- ...lsViewDark--3_5_null_1,NEXUS_5,1.0,en].png | 4 +- ...lsViewDark--3_5_null_2,NEXUS_5,1.0,en].png | 4 +- ...lsViewDark--3_5_null_3,NEXUS_5,1.0,en].png | 4 +- ...lsViewDark--3_5_null_4,NEXUS_5,1.0,en].png | 2 +- ...lsViewDark--3_5_null_5,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_0,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_1,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_2,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_3,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_4,NEXUS_5,1.0,en].png | 4 +- ...sViewLight--2_4_null_5,NEXUS_5,1.0,en].png | 4 +- ...ettings-Day-5_5_null_0,NEXUS_5,1.0,en].png | 4 +- ...tings-Night-5_6_null_0,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_0,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_1,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_2,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_3,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_4,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_5,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_6,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_7,NEXUS_5,1.0,en].png | 4 +- ...etailsDark--1_3_null_8,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_0,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_1,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_3,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_4,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_5,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_6,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_7,NEXUS_5,1.0,en].png | 4 +- ...oomDetails--0_2_null_8,NEXUS_5,1.0,en].png | 4 +- ...etContent-Day-1_1_null,NEXUS_5,1.0,en].png | 4 +- ...Content-Night-1_2_null,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_0,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_1,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_2,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_3,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_4,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_5,NEXUS_5,1.0,en].png | 4 +- ...istView-Day-2_2_null_9,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_0,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_1,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_2,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_3,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_4,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_5,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-2_3_null_9,NEXUS_5,1.0,en].png | 4 +- ...OutView-Day-0_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...tView-Night-0_1_null_0,NEXUS_5,1.0,en].png | 4 +- ...ferenceCategory_0_null,NEXUS_5,1.0,en].png | 4 +- ...ferenceTextDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...erenceTextLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...ithEndBadgeDark_0_null,NEXUS_5,1.0,en].png | 4 +- ...thEndBadgeLight_0_null,NEXUS_5,1.0,en].png | 4 +- ...ferenceView-Day_0_null,NEXUS_5,1.0,en].png | 4 +- ...renceView-Night_1_null,NEXUS_5,1.0,en].png | 4 +- ...sCompound-Day_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ompound-Night_1_null_0,NEXUS_5,1.0,en].png | 4 +- ..._IconsOther-Day_0_null,NEXUS_5,1.0,en].png | 3 -- ...consOther-Day_0_null_0,NEXUS_5,1.0,en].png | 3 ++ ...consOther-Night_1_null,NEXUS_5,1.0,en].png | 3 -- ...nsOther-Night_1_null_0,NEXUS_5,1.0,en].png | 3 ++ ...nsSeptember-Day_0_null,NEXUS_5,1.0,en].png | 3 -- ...September-Night_1_null,NEXUS_5,1.0,en].png | 3 -- ...ropdownMenuItem_0_null,NEXUS_5,1.0,en].png | 4 +- ...ttomSheet-Day-1_2_null,NEXUS_5,1.0,en].png | 4 +- ...omSheet-Night-1_3_null,NEXUS_5,1.0,en].png | 4 +- 194 files changed, 945 insertions(+), 519 deletions(-) create mode 100644 changelog.d/1718.misc rename libraries/designsystem/src/main/res/drawable/{ic_september_add_reaction.xml => ic_add_reaction.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_attachment.xml => ic_attachment.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_photo_video_library.xml => ic_compound_plus.xml} (58%) rename libraries/designsystem/src/main/res/drawable/{ic_september_copy.xml => ic_copy.xml} (100%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_edit.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_edit_outline.xml => ic_edit_outline.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_edit_solid_16.xml => ic_edit_solid.xml} (100%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_forward.xml => ic_forward.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_compose_button.xml => ic_image.xml} (51%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_location_navigator.xml create mode 100644 libraries/designsystem/src/main/res/drawable/ic_location_navigator_centered.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_location.xml => ic_location_pin.xml} (75%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_new_message.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_reply.xml => ic_reply.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_send.xml => ic_send.xml} (100%) delete mode 100644 libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml delete mode 100644 libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_take_photo_camera.xml => ic_take_photo_camera.xml} (100%) rename libraries/designsystem/src/main/res/drawable/{ic_september_text_formatting.xml => ic_text_formatting.xml} (100%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_user.xml create mode 100644 libraries/designsystem/src/main/res/drawable/ic_user_add.xml rename libraries/designsystem/src/main/res/drawable/{ic_september_video_call.xml => ic_video_call.xml} (100%) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_waiting_to_decrypt.xml rename libraries/{designsystem/src/main/res/drawable/ic_september_view_source.xml => textcomposer/impl/src/main/res/drawable/ic_developer_options.xml} (97%) create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_devices.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_edit.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_encryption_enabled.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_location_navigator.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_new_message.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_reply.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_sign_out.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_user.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_user_add.xml create mode 100644 libraries/textcomposer/impl/src/main/res/drawable/ic_waiting_to_decrypt.xml delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Day_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Night_1_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1718.misc b/changelog.d/1718.misc new file mode 100644 index 0000000000..1b021767c3 --- /dev/null +++ b/changelog.d/1718.misc @@ -0,0 +1 @@ +Update icons and move away from `PreferenceText` components. diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt index 6902a45378..6bc6e33339 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/root/CreateRoomRootView.kt @@ -140,7 +140,7 @@ private fun CreateRoomActionButtonsList( ) { Column(modifier = modifier) { CreateRoomActionButton( - iconRes = CommonDrawables.ic_groups, + iconRes = CommonDrawables.ic_compound_plus, text = stringResource(id = R.string.screen_create_room_action_create_room), onClick = onNewRoomClicked, ) diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt index 124f30e9f4..ccafa7e9e7 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationView.kt @@ -18,7 +18,6 @@ package io.element.android.features.location.impl.send import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues @@ -27,9 +26,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.LocationSearching -import androidx.compose.material.icons.filled.MyLocation import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ListItem import androidx.compose.material3.SheetValue @@ -68,7 +64,7 @@ import io.element.android.libraries.maplibre.compose.rememberCameraPositionState import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SendLocationView( state: SendLocationState, @@ -215,8 +211,8 @@ fun SendLocationView( .padding(end = 16.dp, bottom = 72.dp + navBarPadding), ) { when (state.mode) { - SendLocationState.Mode.PinLocation -> Icon(imageVector = Icons.Default.LocationSearching, contentDescription = null) - SendLocationState.Mode.SenderLocation -> Icon(imageVector = Icons.Default.MyLocation, contentDescription = null) + SendLocationState.Mode.PinLocation -> Icon(resourceId = CommonDrawables.ic_location_navigator, contentDescription = null) + SendLocationState.Mode.SenderLocation -> Icon(resourceId = CommonDrawables.ic_location_navigator_centered, contentDescription = null) } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index bd20a3704b..1e57de2466 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -26,6 +26,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -33,9 +35,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ListItem -import androidx.compose.material.Text import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -70,12 +69,17 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.utils.messagesummary.MessageSummaryFormatterImpl import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toSp import io.element.android.libraries.designsystem.theme.components.HorizontalDivider import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.hide import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail @@ -140,15 +144,13 @@ fun ActionListView( onEmojiReactionClicked = ::onEmojiReactionClicked, onCustomReactionClicked = ::onCustomReactionClicked, modifier = Modifier - .padding(bottom = 32.dp) -// .navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044 -// .imePadding() + .navigationBarsPadding() + .imePadding() ) } } } -@OptIn(ExperimentalMaterialApi::class) @Composable private fun SheetContent( state: ActionListState, @@ -198,18 +200,13 @@ private fun SheetContent( modifier = Modifier.clickable { onActionClicked(action) }, - text = { - Text( - text = stringResource(id = action.titleRes), - color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, - ) + headlineContent = { + Text(text = stringResource(id = action.titleRes)) }, - icon = { - Icon( - resourceId = action.icon, - contentDescription = "", - tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, - ) + leadingContent = ListItemContent.Icon(IconSource.Resource(action.icon)), + style = when { + action.destructive -> ListItemStyle.Destructive + else -> ListItemStyle.Primary } ) } @@ -373,7 +370,7 @@ private fun EmojiReactionsRow( contentAlignment = Alignment.Center ) { Icon( - resourceId = CommonDrawables.ic_september_add_reaction, + resourceId = CommonDrawables.ic_add_reaction, contentDescription = "Emojis", tint = MaterialTheme.colorScheme.secondary, modifier = Modifier @@ -410,8 +407,7 @@ private fun EmojiButton( ) { Text( emoji, - fontSize = 24.dp.toSp(), - color = Color.White, + style = ElementTheme.typography.fontBodyLgRegular.copy(fontSize = 24.dp.toSp(), color = Color.White), modifier = Modifier .clickable( enabled = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt index 9a71d7c179..d79b95ca70 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt @@ -28,13 +28,13 @@ sealed class TimelineItemAction( @DrawableRes val icon: Int, val destructive: Boolean = false ) { - data object Forward : TimelineItemAction(CommonStrings.action_forward, CommonDrawables.ic_september_forward) - data object Copy : TimelineItemAction(CommonStrings.action_copy, CommonDrawables.ic_september_copy) + data object Forward : TimelineItemAction(CommonStrings.action_forward, CommonDrawables.ic_forward) + data object Copy : TimelineItemAction(CommonStrings.action_copy, CommonDrawables.ic_copy) data object Redact : TimelineItemAction(CommonStrings.action_remove, CommonDrawables.ic_compound_delete, destructive = true) - data object Reply : TimelineItemAction(CommonStrings.action_reply, CommonDrawables.ic_september_reply) - data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CommonDrawables.ic_september_reply) - data object Edit : TimelineItemAction(CommonStrings.action_edit, CommonDrawables.ic_september_edit_outline) - data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_september_view_source) + data object Reply : TimelineItemAction(CommonStrings.action_reply, CommonDrawables.ic_reply) + data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CommonDrawables.ic_reply) + data object Edit : TimelineItemAction(CommonStrings.action_edit, CommonDrawables.ic_edit_outline) + data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_developer_options) data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CommonDrawables.ic_compound_chat_problem, destructive = true) data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CommonDrawables.ic_poll_end) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt index 4348b83a95..96e721e815 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/media/local/LocalMediaView.kt @@ -236,7 +236,7 @@ private fun MediaFileView( ) { Icon( imageVector = if (isAudio) Icons.Outlined.GraphicEq else null, - resourceId = if (isAudio) null else CommonDrawables.ic_september_attachment, + resourceId = if (isAudio) null else CommonDrawables.ic_attachment, contentDescription = null, tint = MaterialTheme.colorScheme.background, modifier = Modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt index b920e5967d..e29e64fae8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt @@ -19,9 +19,8 @@ package io.element.android.features.messages.impl.messagecomposer import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.ListItem +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -33,12 +32,14 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.R import io.element.android.libraries.androidutils.ui.hideKeyboard -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables @@ -93,7 +94,6 @@ internal fun AttachmentsBottomSheet( } } -@OptIn(ExperimentalMaterialApi::class) @Composable private fun AttachmentSourcePickerMenu( state: MessageComposerState, @@ -103,28 +103,33 @@ private fun AttachmentSourcePickerMenu( modifier: Modifier = Modifier, ) { Column( - modifier.padding(bottom = 32.dp) -// .navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044 + modifier = modifier + .navigationBarsPadding() + .imePadding() ) { ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) }, - icon = { Icon(CommonDrawables.ic_september_photo_video_library, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_image)), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_gallery)) }, + style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) }, - icon = { Icon(CommonDrawables.ic_september_attachment, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_files)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_attachment)), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_files)) }, + style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.PhotoFromCamera) }, - icon = { Icon(CommonDrawables.ic_september_take_photo_camera, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_take_photo_camera, )), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) }, + style = ListItemStyle.Primary, ) ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.PickAttachmentSource.VideoFromCamera) }, - icon = { Icon(CommonDrawables.ic_september_video_call, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_video_call)), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_camera_video)) }, + style = ListItemStyle.Primary, ) if (state.canShareLocation) { ListItem( @@ -132,8 +137,9 @@ private fun AttachmentSourcePickerMenu( state.eventSink(MessageComposerEvents.PickAttachmentSource.Location) onSendLocationClicked() }, - icon = { Icon(CommonDrawables.ic_september_location, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_location_pin) ), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_location)) }, + style = ListItemStyle.Primary, ) } if (state.canCreatePoll) { @@ -142,15 +148,17 @@ private fun AttachmentSourcePickerMenu( state.eventSink(MessageComposerEvents.PickAttachmentSource.Poll) onCreatePollClicked() }, - icon = { Icon(CommonDrawables.ic_compound_polls, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_source_poll)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_polls)), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_source_poll)) }, + style = ListItemStyle.Primary, ) } if (enableTextFormatting) { ListItem( modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) }, - icon = { Icon(CommonDrawables.ic_september_text_formatting, null) }, - text = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_text_formatting, null)), + headlineContent = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) }, + style = ListItemStyle.Primary, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt index 357c9b3c20..a5220e1d90 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessagesReactionButton.kt @@ -181,7 +181,7 @@ internal fun MessagesReactionButtonPreview(@PreviewParameter(AggregatedReactionP @Composable internal fun MessagesAddReactionButtonPreview() = ElementPreview { MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction), + content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_add_reaction), onClick = {}, onLongClick = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt index 50ba43f27c..17d1d11f83 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/ReplySwipeIndicator.kt @@ -49,7 +49,7 @@ fun RowScope.ReplySwipeIndicator( alpha = swipeProgress() }, contentDescription = null, - resourceId = CommonDrawables.ic_september_reply, + resourceId = CommonDrawables.ic_reply, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt index f0d6871bf6..d30a60c7a4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsLayout.kt @@ -196,7 +196,7 @@ internal fun TimelineItemReactionsLayoutPreview() = ElementPreview { }, addMoreButton = { MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction), + content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_add_reaction), onClick = {}, onLongClick = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index 3f625fd6e4..c5e94b0742 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -95,7 +95,7 @@ private fun TimelineItemReactionsView( }, addMoreButton = { MessagesReactionButton( - content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_september_add_reaction), + content = MessagesReactionsButtonContent.Icon(CommonDrawables.ic_add_reaction), onClick = onMoreReactionsClick, onLongClick = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt index 23c698cfc6..d488d01ef4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemEncryptedView.kt @@ -35,7 +35,7 @@ fun TimelineItemEncryptedView( TimelineItemInformativeView( text = stringResource(id = CommonStrings.common_waiting_for_decryption_key), iconDescription = stringResource(id = CommonStrings.dialog_title_warning), - iconResourceId = CommonDrawables.ic_september_decryption_error, + iconResourceId = CommonDrawables.ic_waiting_to_decrypt, extraPadding = extraPadding, modifier = modifier ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt index f0f08fa2f5..a8fb3e1fcc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemFileView.kt @@ -59,7 +59,7 @@ fun TimelineItemFileView( contentAlignment = Alignment.Center, ) { Icon( - resourceId = CommonDrawables.ic_september_attachment, + resourceId = CommonDrawables.ic_attachment, contentDescription = "OpenFile", tint = ElementTheme.materialColors.primary, modifier = Modifier diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt index 1fc47fdece..4fa20bde8f 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.outlined.InsertChart import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -135,7 +134,7 @@ fun PreferencesRootView( if (state.showLockScreenSettings) { ListItem( headlineContent = { Text(stringResource(id = CommonStrings.common_screen_lock)) }, - leadingContent = ListItemContent.Icon(IconSource.Vector(Icons.Default.Lock)), + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_lock_outline)), onClick = onOpenLockScreenSettings, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index f3c6b13253..b5b1b83b98 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -32,7 +32,6 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.Add -import androidx.compose.material.icons.outlined.Person import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -57,6 +56,7 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.MainActionButton +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreviewDark @@ -66,6 +66,9 @@ import io.element.android.libraries.designsystem.theme.components.DropdownMenu import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.IconButton +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar @@ -324,10 +327,10 @@ private fun NotificationSection( stringResource(R.string.screen_room_details_notification_mode_custom) } PreferenceCategory(modifier = modifier) { - PreferenceText( - title = stringResource(R.string.screen_room_details_notification_title), - subtitle = subtitle, - iconResourceId = CommonDrawables.ic_compound_notifications, + ListItem( + headlineContent = { Text(text = stringResource(R.string.screen_room_details_notification_title)) }, + supportingContent = { Text(text = subtitle) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_notifications)), onClick = openRoomNotificationSettings, ) } @@ -340,10 +343,10 @@ private fun MembersSection( modifier: Modifier = Modifier, ) { PreferenceCategory(modifier = modifier) { - PreferenceText( - title = stringResource(CommonStrings.common_people), - icon = Icons.Outlined.Person, - currentValue = memberCount.toString(), + ListItem( + headlineContent = { Text(stringResource(CommonStrings.common_people)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_user)), + trailingContent = ListItemContent.Text(memberCount.toString()), onClick = openRoomMemberList, ) } @@ -355,9 +358,9 @@ private fun InviteSection( modifier: Modifier = Modifier, ) { PreferenceCategory(modifier = modifier) { - PreferenceText( - title = stringResource(R.string.screen_room_details_invite_people_title), - iconResourceId = CommonDrawables.ic_compound_user_add, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_details_invite_people_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_user_add)), onClick = invitePeople, ) } @@ -366,10 +369,10 @@ private fun InviteSection( @Composable private fun SecuritySection(modifier: Modifier = Modifier) { PreferenceCategory(title = stringResource(R.string.screen_room_details_security_title), modifier = modifier) { - PreferenceText( - title = stringResource(R.string.screen_room_details_encryption_enabled_title), - subtitle = stringResource(R.string.screen_room_details_encryption_enabled_subtitle), - iconResourceId = CommonDrawables.ic_compound_lock, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_details_encryption_enabled_title)) }, + supportingContent = { Text(stringResource(R.string.screen_room_details_encryption_enabled_subtitle)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_encryption_enabled)), ) } } @@ -377,10 +380,10 @@ private fun SecuritySection(modifier: Modifier = Modifier) { @Composable private fun OtherActionsSection(onLeaveRoom: () -> Unit, modifier: Modifier = Modifier) { PreferenceCategory(showDivider = false, modifier = modifier) { - PreferenceText( - title = stringResource(R.string.screen_room_details_leave_room_title), - iconResourceId = CommonDrawables.ic_compound_leave, - tintColor = MaterialTheme.colorScheme.error, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_details_leave_room_title)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_leave)), + style = ListItemStyle.Destructive, onClick = onLeaveRoom, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt index c21e8520b3..05d75211e7 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/blockuser/BlockUserSection.kt @@ -16,20 +16,26 @@ package io.element.android.features.roomdetails.impl.blockuser -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Block -import androidx.compose.material3.MaterialTheme +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.progressSemantics import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import io.element.android.features.roomdetails.impl.R import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsEvents import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsState import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.components.dialogs.RetryDialog +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory -import io.element.android.libraries.designsystem.components.preferences.PreferenceText +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.ui.strings.CommonStrings @Composable @@ -65,21 +71,29 @@ private fun PreferenceBlockUser( eventSink: (RoomMemberDetailsEvents) -> Unit, modifier: Modifier = Modifier, ) { + val loadingCurrentValue = @Composable { + CircularProgressIndicator( + modifier = Modifier + .progressSemantics() + .size(20.dp), + strokeWidth = 2.dp + ) + } if (isBlocked.orFalse()) { - PreferenceText( - title = stringResource(R.string.screen_dm_details_unblock_user), - icon = Icons.Outlined.Block, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_dm_details_unblock_user)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_block)), onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true)) }, - loadingCurrentValue = isLoading, + trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, modifier = modifier, ) } else { - PreferenceText( - title = stringResource(R.string.screen_dm_details_block_user), - icon = Icons.Outlined.Block, - tintColor = MaterialTheme.colorScheme.error, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_dm_details_block_user)) }, + leadingContent = ListItemContent.Icon(IconSource.Resource(CommonDrawables.ic_compound_block)), + style = ListItemStyle.Destructive, onClick = { if (!isLoading) eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true)) }, - loadingCurrentValue = isLoading, + trailingContent = if (isLoading) ListItemContent.Custom(loadingCurrentValue) else null, modifier = modifier, ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt index f66c5d8b9b..e304ee2cfa 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/notificationsettings/UserDefinedRoomNotificationSettingsView.kt @@ -22,25 +22,22 @@ import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.roomdetails.impl.R import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.designsystem.components.async.AsyncView import io.element.android.libraries.designsystem.components.button.BackButton -import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar -import io.element.android.libraries.designsystem.utils.CommonDrawables @Composable fun UserDefinedRoomNotificationSettingsView( @@ -75,10 +72,9 @@ fun UserDefinedRoomNotificationSettingsView( ) } - PreferenceText( - title = stringResource(R.string.screen_room_notification_settings_edit_remove_setting), - icon = ImageVector.vectorResource(CommonDrawables.ic_compound_delete), - tintColor = MaterialTheme.colorScheme.error, + ListItem( + headlineContent = { Text(stringResource(R.string.screen_room_notification_settings_edit_remove_setting)) }, + style = ListItemStyle.Destructive, onClick = { state.eventSink(RoomNotificationSettingsEvents.DeleteCustomNotification) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt index 24f1a42883..863b7c80be 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListContextMenu.kt @@ -18,20 +18,18 @@ package io.element.android.features.roomlist.impl import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables @@ -88,34 +86,25 @@ private fun RoomListModalBottomSheetContent( ) }, modifier = Modifier.clickable { onRoomSettingsClicked(contextMenu.roomId) }, - leadingContent = { - Icon( - resourceId = CommonDrawables.ic_compound_settings, - contentDescription = stringResource(id = CommonStrings.common_settings), - modifier = Modifier.size(20.dp), - tint = MaterialTheme.colorScheme.onSurface, + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Resource( + CommonDrawables.ic_compound_settings, + contentDescription = stringResource(id = CommonStrings.common_settings) ) - } + ), + style = ListItemStyle.Primary, ) ListItem( - headlineContent = { - Text( - text = stringResource(id = CommonStrings.action_leave_room), - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyLarge, - ) - }, + headlineContent = { Text(text = stringResource(id = CommonStrings.action_leave_room)) }, modifier = Modifier.clickable { onLeaveRoomClicked(contextMenu.roomId) }, - leadingContent = { - Icon( - resourceId = CommonDrawables.ic_compound_leave, - contentDescription = stringResource(id = CommonStrings.action_leave_room), - modifier = Modifier.size(20.dp), - tint = MaterialTheme.colorScheme.error, + leadingContent = ListItemContent.Icon( + iconSource = IconSource.Resource( + CommonDrawables.ic_compound_leave, + contentDescription = stringResource(id = CommonStrings.action_leave_room) ) - } + ), + style = ListItemStyle.Destructive, ) - Spacer(modifier = Modifier.height(32.dp)) } } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt index afb110e4de..42192a3376 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListView.kt @@ -243,7 +243,7 @@ private fun RoomListContent( ) { Icon( // Note cannot use Icons.Outlined.EditSquare, it does not exist :/ - resourceId = CommonDrawables.ic_september_compose_button, + resourceId = CommonDrawables.ic_new_message, contentDescription = stringResource(id = R.string.screen_roomlist_a11y_create_message) ) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt index 57bfccabb7..2689aac427 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomListTopBar.kt @@ -253,7 +253,7 @@ private fun DefaultRoomListTopBar( showMenu = false onMenuActionClicked(RoomListMenuAction.ReportBug) }, - text = { Text(stringResource(id = CommonStrings.common_report_a_bug)) }, + text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) }, leadingIcon = { Icon( resourceId = CommonDrawables.ic_compound_chat_problem, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt index bdf0509560..7b722dd32b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/views/RecoveryKeyView.kt @@ -114,7 +114,7 @@ private fun RecoveryKeyStaticContent( modifier = Modifier.weight(1f), ) Icon( - resourceId = CommonDrawables.ic_september_copy, + resourceId = CommonDrawables.ic_copy, contentDescription = stringResource(id = CommonStrings.action_copy), tint = ElementTheme.colors.iconSecondary, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt index 8074d42056..afde4eaa4e 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/list/ListItemContent.kt @@ -16,10 +16,12 @@ package io.element.android.libraries.designsystem.components.list +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.DpSize @@ -126,7 +128,12 @@ sealed interface ListItemContent { ) } is Text -> TextComponent(modifier = Modifier.widthIn(max = 128.dp), text = text, maxLines = 1, overflow = TextOverflow.Ellipsis) - is Badge -> RedIndicatorAtom() + is Badge -> Box( + modifier = Modifier.size(maxCompactSize), + contentAlignment = Alignment.Center, + ) { + RedIndicatorAtom() + } is Custom -> content() } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt index 3faa030daf..8dee3921ac 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsList.kt @@ -96,43 +96,43 @@ internal val iconsCompound = listOf( R.drawable.ic_compound_web_browser, ) -internal val iconsSeptember = listOf( - R.drawable.ic_september_add_reaction, - R.drawable.ic_september_attachment, - R.drawable.ic_september_compose_button, - R.drawable.ic_september_copy, - R.drawable.ic_september_decryption_error, - R.drawable.ic_september_edit_outline, - R.drawable.ic_september_edit_solid_16, - R.drawable.ic_september_forward, - R.drawable.ic_september_location, - R.drawable.ic_september_photo_camera, - R.drawable.ic_september_photo_video_library, - R.drawable.ic_september_reply, - R.drawable.ic_september_send, - R.drawable.ic_september_take_photo_camera, - R.drawable.ic_september_text_formatting, - R.drawable.ic_september_video_call, - R.drawable.ic_september_view_source, -) - // This list and all the drawable it contains should be removed at some point. // All the icons should be defined in Compound. internal val iconsOther = listOf( + R.drawable.ic_add_reaction, + R.drawable.ic_attachment, + R.drawable.ic_copy, R.drawable.ic_developer_options, R.drawable.ic_devices, + R.drawable.ic_edit, + R.drawable.ic_edit_outline, + R.drawable.ic_edit_solid, + R.drawable.ic_encryption_enabled, + R.drawable.ic_forward, R.drawable.ic_groups, + R.drawable.ic_image, R.drawable.ic_indent_decrease, R.drawable.ic_indent_increase, R.drawable.ic_inline_code, R.drawable.ic_italic, R.drawable.ic_link, + R.drawable.ic_location_navigator, + R.drawable.ic_location_navigator_centered, + R.drawable.ic_new_message, R.drawable.ic_numbered_list, R.drawable.ic_plus, R.drawable.ic_poll_end, R.drawable.ic_quote, + R.drawable.ic_reply, + R.drawable.ic_send, + R.drawable.ic_sign_out, R.drawable.ic_strikethrough, + R.drawable.ic_take_photo_camera, + R.drawable.ic_text_formatting, R.drawable.ic_thread_decoration, R.drawable.ic_underline, - R.drawable.ic_sign_out, + R.drawable.ic_user, + R.drawable.ic_user_add, + R.drawable.ic_video_call, + R.drawable.ic_waiting_to_decrypt, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt index 1963e8e994..1e67f097e4 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/icons/IconsPreview.kt @@ -38,7 +38,7 @@ import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList -internal class IconChunkPreviewProvider : PreviewParameterProvider { +internal class CompoundIconListPreviewProvider : PreviewParameterProvider { override val values: Sequence get() { val chunks = iconsCompound.chunked(36) @@ -49,6 +49,17 @@ internal class IconChunkPreviewProvider : PreviewParameterProvider { } } +internal class OtherIconListPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() { + val chunks = iconsOther.chunked(36) + return chunks.mapIndexed { index, chunk -> + IconChunk(index = index+1, total = chunks.size, icons = chunk.toPersistentList()) + } + .asSequence() + } +} + internal data class IconChunk( val index: Int, val total: Int, @@ -57,7 +68,7 @@ internal data class IconChunk( @PreviewsDayNight @Composable -internal fun IconsCompoundPreview(@PreviewParameter(IconChunkPreviewProvider::class) chunk: IconChunk) = ElementPreview { +internal fun IconsCompoundPreview(@PreviewParameter(CompoundIconListPreviewProvider::class) chunk: IconChunk) = ElementPreview { IconsPreview( title = "R.drawable.ic_compound_* ${chunk.index}/${chunk.total}", iconsList = chunk.icons, @@ -69,22 +80,10 @@ internal fun IconsCompoundPreview(@PreviewParameter(IconChunkPreviewProvider::cl @PreviewsDayNight @Composable -internal fun IconsSeptemberPreview() = ElementPreview { +internal fun IconsOtherPreview(@PreviewParameter(OtherIconListPreviewProvider::class) iconChunk: IconChunk) = ElementPreview { IconsPreview( - title = "R.drawable.ic_september_*", - iconsList = iconsSeptember.toPersistentList(), - iconNameTransform = { name -> - name.removePrefix("ic_september_") - .replace("_", " ") - }) -} - -@PreviewsDayNight -@Composable -internal fun IconsOtherPreview() = ElementPreview { - IconsPreview( - title = "R.drawable.ic_*", - iconsList = iconsOther.toPersistentList(), + title = "R.drawable.ic_* ${iconChunk.index}/${iconChunk.total}", + iconsList = iconChunk.icons, iconNameTransform = { name -> name.removePrefix("ic_") .replace("_", " ") diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_add_reaction.xml b/libraries/designsystem/src/main/res/drawable/ic_add_reaction.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_add_reaction.xml rename to libraries/designsystem/src/main/res/drawable/ic_add_reaction.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_attachment.xml b/libraries/designsystem/src/main/res/drawable/ic_attachment.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_attachment.xml rename to libraries/designsystem/src/main/res/drawable/ic_attachment.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml index 6bfcc9331c..4d16775c66 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_compound_chat_problem.xml @@ -20,12 +20,12 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml b/libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml similarity index 58% rename from libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml rename to libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml index 49d06f1a40..a56299f569 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_september_photo_video_library.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_compound_plus.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_copy.xml b/libraries/designsystem/src/main/res/drawable/ic_copy.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_copy.xml rename to libraries/designsystem/src/main/res/drawable/ic_copy.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_edit.xml b/libraries/designsystem/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000000..187e190046 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_edit_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_edit_outline.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_edit_outline.xml rename to libraries/designsystem/src/main/res/drawable/ic_edit_outline.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_edit_solid_16.xml b/libraries/designsystem/src/main/res/drawable/ic_edit_solid.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_edit_solid_16.xml rename to libraries/designsystem/src/main/res/drawable/ic_edit_solid.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml b/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml new file mode 100644 index 0000000000..df9b6b2cad --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_encryption_enabled.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_forward.xml b/libraries/designsystem/src/main/res/drawable/ic_forward.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_forward.xml rename to libraries/designsystem/src/main/res/drawable/ic_forward.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml b/libraries/designsystem/src/main/res/drawable/ic_image.xml similarity index 51% rename from libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml rename to libraries/designsystem/src/main/res/drawable/ic_image.xml index fa706f56c8..f2f41c7369 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_september_compose_button.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_image.xml @@ -20,6 +20,9 @@ android:viewportWidth="24" android:viewportHeight="24"> + diff --git a/libraries/designsystem/src/main/res/drawable/ic_location_navigator.xml b/libraries/designsystem/src/main/res/drawable/ic_location_navigator.xml new file mode 100644 index 0000000000..0d66e9c237 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_location_navigator.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_location_navigator_centered.xml b/libraries/designsystem/src/main/res/drawable/ic_location_navigator_centered.xml new file mode 100644 index 0000000000..374af787a9 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_location_navigator_centered.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_location.xml b/libraries/designsystem/src/main/res/drawable/ic_location_pin.xml similarity index 75% rename from libraries/designsystem/src/main/res/drawable/ic_september_location.xml rename to libraries/designsystem/src/main/res/drawable/ic_location_pin.xml index 7843eb22cc..6e55afca13 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_september_location.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_location_pin.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> + android:pathData="M12,19.35C14.033,17.483 15.542,15.788 16.525,14.262C17.508,12.738 18,11.383 18,10.2C18,8.383 17.421,6.896 16.263,5.738C15.104,4.579 13.683,4 12,4C10.317,4 8.896,4.579 7.738,5.738C6.579,6.896 6,8.383 6,10.2C6,11.383 6.492,12.738 7.475,14.262C8.458,15.788 9.967,17.483 12,19.35ZM12,21.325C11.767,21.325 11.533,21.283 11.3,21.2C11.067,21.117 10.858,20.992 10.675,20.825C9.592,19.825 8.633,18.85 7.8,17.9C6.967,16.95 6.271,16.029 5.713,15.137C5.154,14.246 4.729,13.387 4.438,12.563C4.146,11.738 4,10.95 4,10.2C4,7.7 4.804,5.708 6.412,4.225C8.021,2.742 9.883,2 12,2C14.117,2 15.979,2.742 17.587,4.225C19.196,5.708 20,7.7 20,10.2C20,10.95 19.854,11.738 19.563,12.563C19.271,13.387 18.846,14.246 18.288,15.137C17.729,16.029 17.033,16.95 16.2,17.9C15.367,18.85 14.408,19.825 13.325,20.825C13.142,20.992 12.933,21.117 12.7,21.2C12.467,21.283 12.233,21.325 12,21.325ZM12,12C12.55,12 13.021,11.804 13.413,11.413C13.804,11.021 14,10.55 14,10C14,9.45 13.804,8.979 13.413,8.587C13.021,8.196 12.55,8 12,8C11.45,8 10.979,8.196 10.587,8.587C10.196,8.979 10,9.45 10,10C10,10.55 10.196,11.021 10.587,11.413C10.979,11.804 11.45,12 12,12Z" + android:fillColor="#1B1D22"/> diff --git a/libraries/designsystem/src/main/res/drawable/ic_lock_outline.xml b/libraries/designsystem/src/main/res/drawable/ic_lock_outline.xml index 51831bf3c1..349b532bd0 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_lock_outline.xml +++ b/libraries/designsystem/src/main/res/drawable/ic_lock_outline.xml @@ -15,15 +15,11 @@ --> - - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + diff --git a/libraries/designsystem/src/main/res/drawable/ic_new_message.xml b/libraries/designsystem/src/main/res/drawable/ic_new_message.xml new file mode 100644 index 0000000000..3e306ce7c5 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_new_message.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_reply.xml b/libraries/designsystem/src/main/res/drawable/ic_reply.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_reply.xml rename to libraries/designsystem/src/main/res/drawable/ic_reply.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_send.xml b/libraries/designsystem/src/main/res/drawable/ic_send.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_send.xml rename to libraries/designsystem/src/main/res/drawable/ic_send.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml b/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml deleted file mode 100644 index 4751e55ff0..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_september_decryption_error.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml b/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml deleted file mode 100644 index 38d7f8af44..0000000000 --- a/libraries/designsystem/src/main/res/drawable/ic_september_photo_camera.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_take_photo_camera.xml b/libraries/designsystem/src/main/res/drawable/ic_take_photo_camera.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_take_photo_camera.xml rename to libraries/designsystem/src/main/res/drawable/ic_take_photo_camera.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_text_formatting.xml b/libraries/designsystem/src/main/res/drawable/ic_text_formatting.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_text_formatting.xml rename to libraries/designsystem/src/main/res/drawable/ic_text_formatting.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_user.xml b/libraries/designsystem/src/main/res/drawable/ic_user.xml new file mode 100644 index 0000000000..25a1d604f2 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_user.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_user_add.xml b/libraries/designsystem/src/main/res/drawable/ic_user_add.xml new file mode 100644 index 0000000000..22f3f64a7e --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_user_add.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_video_call.xml b/libraries/designsystem/src/main/res/drawable/ic_video_call.xml similarity index 100% rename from libraries/designsystem/src/main/res/drawable/ic_september_video_call.xml rename to libraries/designsystem/src/main/res/drawable/ic_video_call.xml diff --git a/libraries/designsystem/src/main/res/drawable/ic_waiting_to_decrypt.xml b/libraries/designsystem/src/main/res/drawable/ic_waiting_to_decrypt.xml new file mode 100644 index 0000000000..7b4ea2ac6d --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_waiting_to_decrypt.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt index 651f276f7b..40c58fb578 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AttachmentThumbnail.kt @@ -105,7 +105,7 @@ fun AttachmentThumbnail( } AttachmentThumbnailType.File -> { Icon( - resourceId = CommonDrawables.ic_september_attachment, + resourceId = CommonDrawables.ic_attachment, contentDescription = info.textContent, modifier = Modifier.rotate(-45f) ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index 05287e04e1..343886c146 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -28,16 +28,18 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetValue -import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource -import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.IconSource +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.ListItemStyle import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.ui.media.AvatarAction @@ -98,12 +100,10 @@ private fun AvatarActionBottomSheetContent( color = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, ) }, - leadingContent = { - Icon( - resourceId = action.iconResourceId, - contentDescription = stringResource(action.titleResId), - tint = if (action.destructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.secondary, - ) + leadingContent = ListItemContent.Icon(IconSource.Resource(action.iconResourceId)), + style = when { + action.destructive -> ListItemStyle.Destructive + else -> ListItemStyle.Primary } ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt index f97227be7e..289c37530a 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/EditableAvatarView.kt @@ -26,8 +26,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.AddAPhoto import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -40,6 +38,7 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.utils.CommonDrawables @Composable fun EditableAvatarView( @@ -87,7 +86,7 @@ fun EditableAvatarView( ) { Icon( modifier = Modifier.size(16.dp), - imageVector = Icons.Outlined.AddAPhoto, + resourceId = CommonDrawables.ic_edit, contentDescription = "", tint = MaterialTheme.colorScheme.onPrimary, ) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt index 99a6083565..e5305b86ae 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt @@ -30,12 +30,12 @@ sealed class AvatarAction( ) { data object TakePhoto : AvatarAction( titleResId = CommonStrings.action_take_photo, - iconResourceId = CommonDrawables.ic_september_take_photo_camera, + iconResourceId = CommonDrawables.ic_take_photo_camera, ) data object ChoosePhoto : AvatarAction( titleResId = CommonStrings.action_choose_photo, - iconResourceId = CommonDrawables.ic_september_photo_video_library, + iconResourceId = CommonDrawables.ic_image, ) data object Remove : AvatarAction( diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 9f02254b4d..93e552768a 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -475,7 +475,7 @@ private fun EditingModeView( .padding(start = 12.dp) ) { Icon( - resourceId = CommonDrawables.ic_september_edit_solid_16, + resourceId = CommonDrawables.ic_edit_solid, contentDescription = stringResource(CommonStrings.common_editing), tint = ElementTheme.materialColors.secondary, modifier = Modifier diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt index bbe947f6a1..eecd9d6c26 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt @@ -54,7 +54,7 @@ internal fun SendButton( ) { val iconId = when (composerMode) { is MessageComposerMode.Edit -> CommonDrawables.ic_compound_check - else -> CommonDrawables.ic_september_send + else -> CommonDrawables.ic_send } val iconSize = when (composerMode) { is MessageComposerMode.Edit -> 24.dp diff --git a/libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_developer_options.xml similarity index 97% rename from libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml rename to libraries/textcomposer/impl/src/main/res/drawable/ic_developer_options.xml index 3ad615a748..ed18daf9a2 100644 --- a/libraries/designsystem/src/main/res/drawable/ic_september_view_source.xml +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_developer_options.xml @@ -21,5 +21,5 @@ android:viewportHeight="24"> + android:fillColor="#1B1D22"/> diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_devices.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_devices.xml new file mode 100644 index 0000000000..a369edca9f --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_devices.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_edit.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000000..5acb318c99 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_encryption_enabled.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_encryption_enabled.xml new file mode 100644 index 0000000000..d08f53b5bf --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_encryption_enabled.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_location_navigator.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_location_navigator.xml new file mode 100644 index 0000000000..0d66e9c237 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_location_navigator.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_new_message.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_new_message.xml new file mode 100644 index 0000000000..dd3584beb0 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_new_message.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_reply.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_reply.xml new file mode 100644 index 0000000000..bfd655cbac --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_reply.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_sign_out.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_sign_out.xml new file mode 100644 index 0000000000..0137b88cd4 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_sign_out.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_user.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_user.xml new file mode 100644 index 0000000000..a516526a49 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_user.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_user_add.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_user_add.xml new file mode 100644 index 0000000000..c497043188 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_user_add.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/libraries/textcomposer/impl/src/main/res/drawable/ic_waiting_to_decrypt.xml b/libraries/textcomposer/impl/src/main/res/drawable/ic_waiting_to_decrypt.xml new file mode 100644 index 0000000000..30975a62e9 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/res/drawable/ic_waiting_to_decrypt.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt index d906063ae3..9ecf90ebe6 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistClassNameTest.kt @@ -59,7 +59,7 @@ class KonsistClassNameTest { .substringBefore("<") .removeSuffix("?") .replace(".", "") - it.name.endsWith("Provider") && it.name.contains(providedType) + it.name.endsWith("Provider") && (it.name.contains("IconList") || it.name.contains(providedType)) } } } diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png index 37eb2546da..26a31135df 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Day-4_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b513c5dc3dcab57aa0161dda218a23bca21591e5d3e74c57affded1ee9160ba8 -size 23575 +oid sha256:79f771e31445db99b25df34ae686a940d0bb7e44745abe5ed2da79bf258122ed +size 23090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png index 0f8d42fc4d..4b79f83582 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.createroom.impl.root_CreateRoomRootView_null_CreateRoomRootView-Night-4_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a326bbd2a5b8682aba465f5ffe988243893121d4dcc615159982cc1d3a6c52f -size 22039 +oid sha256:e83a879d73aec0356e2a5a2db628183108b2d045a9bbb72d86324419d3407b0f +size 21460 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png index 8bff737507..bcc1a519e6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Day-2_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:812de54cf01d30f00db9606f7907f106453eba5f9bba4b9fd38a61d8944e780c -size 288364 +oid sha256:261face86126e646561ef480904b0af7e7aaf990e5a6fd198d0d953f4a613e11 +size 288233 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png index 565e3dd06d..d61546f9d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.ftue.impl.welcome_WelcomeView_null_WelcomeView-Night-2_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc89eabc29dd72962b2ccece5bc003ffaf233b81817acf5b59f4574d4774af41 -size 391205 +oid sha256:c935cbdc16bead41781b486da0427a2d6df3530fe8f35a8bb031c660db8e7e73 +size 391103 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png index c162c61c2d..6eb4f902e9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:322558b9928b3e408320e9c002439b0ebd6b09a86c03cd3668ffd64f03d0b5c8 -size 22317 +oid sha256:4fe166a1cb630b39bf4d21f78da1cca582a466e7cf50a879546db9ccb123cb7e +size 22385 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png index eef86b480c..80f5a454bd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3555a707ef787466d493247a5198131469c3e61d4f2aaf10e7a86e99f7fcccd6 -size 41368 +oid sha256:2ee8c200c15a0b9d9aefb2de0f3dec576742ac4fe59c71ce0ee016cf23415d5a +size 41422 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png index 764d0fbdd5..cb9b8a183b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d902d99d379baa2f4fb0eebb9479fc6c497cf1ed62059cda799f4acea72efd7 -size 39897 +oid sha256:cc7691f9c3643ad352bf687a898512b4adb4ea0b12e536ebda3bce7c8f2512d1 +size 39954 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png index c162c61c2d..6eb4f902e9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:322558b9928b3e408320e9c002439b0ebd6b09a86c03cd3668ffd64f03d0b5c8 -size 22317 +oid sha256:4fe166a1cb630b39bf4d21f78da1cca582a466e7cf50a879546db9ccb123cb7e +size 22385 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png index 50c97e76a1..f6d0210995 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d3b5105a703083f7ef66c74e54232a4626e47c2409637d72170381212e2becd -size 22456 +oid sha256:7e083f35c919d664603c4edeb24a32e9a9e594a480d4b4ebabd92863a77d7af3 +size 22504 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 6fb5eeaed2..cfd15d6bad 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3700451074385a0cf8601faa11432124b60c05060cf79459b8aab35cefa0bc0c -size 20723 +oid sha256:b1cd421332c29fe6ea004f95c113fa8bfb0ccc64b34c77b5d03c93debf6fd758 +size 20780 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png index 20dfafc434..0a931fcc53 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e7a461733795c789f0f6507874fbcb7d911b2b93ad0fd44a446d6882def8141 -size 38048 +oid sha256:38539e5bf70c45e79cea35d6d5cd15ac1476a51083a177d82eb2765673733fc0 +size 38110 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png index 144654d0e5..5123e9e4fe 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6d34735c7ad55744a0309897d826d6fd2d935ee2ffb7a4f6192d1b5b5e9925e -size 36499 +oid sha256:7d9fb2ef1976c318c4dc7191a10d482e98ceb8651a90cdef933c100cff4bdb6a +size 36560 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png index 6fb5eeaed2..cfd15d6bad 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3700451074385a0cf8601faa11432124b60c05060cf79459b8aab35cefa0bc0c -size 20723 +oid sha256:b1cd421332c29fe6ea004f95c113fa8bfb0ccc64b34c77b5d03c93debf6fd758 +size 20780 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png index d99081af1a..eab3525a36 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.location.impl.send_SendLocationView_null_SendLocationView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c9bba26f9a57eeb103087ed9b630eb9964983db2692e1019a3d6b4fc66c3296 -size 20888 +oid sha256:0e197302e95daa0a1bb1461e0564afd234723db0300521c623cc3640c56f431b +size 20938 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png index af7782f5d7..cd2c26834e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85692ea3847fe5a79f955bb71153ccb6e5b24cf451292c7b9d56f26e2eff95b7 -size 28840 +oid sha256:1aecc7465d731f46d32ea9222022b6f640a3aa86c81a77c93487d5f35bfcad1b +size 28689 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png index ac13aa8b52..8e3f139608 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2e7684929f93f437c50549b309e1ba75b95cddb506ad9c8ef97373d0552bddfb -size 39017 +oid sha256:28300c9143f5701e46ed3b8d77e93d34584f82949a335003bab685ffb4cd1434 +size 38850 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png index 18aaab9fd1..ae317f2718 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56f71535fd676a86e79b0e987701d34c85c96276e5338f77117b13e1b3a05144 -size 45715 +oid sha256:f79e915bd6c47b424fd34dfa38130a083b00288e0f6eb15c685d78bb9ce29e0b +size 45508 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png index 2f2e8ea4eb..d607db48b1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f594c95c1de47b3fa74add85f87b70005c54ed9230d29967742a8a8291b6844 -size 46255 +oid sha256:f332e7d12d5f64a1704d17e5adc42e9eb0ea9ed21a2d116955e2e0e276153182 +size 46090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png index 0c6cb3114f..9f763b3134 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d398070330d5c66358b7ac17d4e1b93795fb9001ffc6eabe533d3bae86de7d58 -size 39810 +oid sha256:ecc28b4311d27eaaa1c32128ed3c1f3ca03c5bb0c1390b8685c82caed2d6bc3b +size 39662 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png index c458b9a7b8..8907e18435 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d32becec465a3f01e35aac9ebbeb8f48507cc2146f35b8e6c6393bbd410cca3e -size 39370 +oid sha256:6b269d050154e28ebf9b1eaa51856e1199490ea7aecc6439fc567bd3b8cbb7d8 +size 39224 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png index 975fb15bc4..ecbc7a1bad 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8e50be96a1b204b8187fd76802650569a740a927739135a6b6307481f2d5568 -size 40453 +oid sha256:19f262d8c0786fe8e1781c5934e7ff86f89d36decb2d0566ca35772962b3866c +size 40306 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png index 74448d6967..be972081fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:979138753000ada021a67f4bc14a89b912430c4331ac69aa702b2e50c050d7cb -size 41200 +oid sha256:c3ca04db3a4e6096aa75e547fd059a199aca4b91a31b36e3ae84254a08fbfbf1 +size 41043 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png index 3cb12c4c94..718abd867e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Day-1_1_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e06e1a32d8c9d06a307a5116a396a03308d5e5486f911d34002139485e7d9ffe -size 28176 +oid sha256:d847380eed52b6842f2926143e189208f11baf8ce906acae23dcf797384cd1c1 +size 28015 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png index ee2fa7b46f..6dd671d42a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5df2d3b6e4698867277712214fa526e08a9bf790140cd000cfe2bf94c70e77f4 -size 27525 +oid sha256:eabb7309eef1e5bf0d496e053c1531f44262e109b3847e2de5e60ffd6df4e942 +size 27505 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png index 2e051be73c..90f1fbbf34 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb4be22a56036d53f7c394c3829a9f8f09820276d7529ace0d3731965a8a92f9 -size 37556 +oid sha256:9740a5e50f62b47a4daccccfb795b6844033b6578227b93584b3e29a949c0448 +size 37510 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png index ea837fc372..78a31c272b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:369e8b4e3f741476cfa5ff7e3f9d5d1e1399fcf3494fadb95540129436ef4d68 -size 43744 +oid sha256:6460478383d3dfd4043b129743c508ca6cc7bc64e0635dee51d1de7ba073d3c7 +size 43672 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png index 40a48bf88f..ff0850af1e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd4fad16706caabe092b40fc2ffbc748db5b763ba649f88fe7463ab17f73ca4e -size 44329 +oid sha256:b561a070230bb3fb50a189a969cda1fa723fb519915c8af0b21ec516a71cc630 +size 44220 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png index cb01f9579d..6476423b27 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b2aaa54bf065e6496957c3a60d61336f65e6b76f97e5dbc773284ee49fac91d -size 37988 +oid sha256:88c8e6bcecef8c8da37603cea72dfdeab1d975d674ff049d8994ab12e9799451 +size 37939 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png index dbe1f4d267..4ee31f59c8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a99566280864e66ec327d2d6d8016c74b12f1c62ff27fd0d2d4ba0109ae9756a -size 37747 +oid sha256:2a639070cb7293c750b153489d1b88a946db8231f818063da86ea38bce282863 +size 37693 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png index 872af5733e..85a62da13d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:83f6cbcdbc00c49aed762339769e0f639604286406c46b1336b7757e2aad8b41 -size 38641 +oid sha256:d1a18f1544bd6b2f9592acf3e0347792136116b0c5159591cc6bc4a6071638a3 +size 38557 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png index 8d45efac8c..91639aed2f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:752abe37a810cc8e4b36ae5f4c4aa38c9f22757b6458f272ba991050c69aee53 -size 39409 +oid sha256:0c18f92cda3af1e3a5524fd6191b45810e20eb3449335fa357e390465e051be4 +size 39309 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png index 811cb7e4cd..5d33fdbe24 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.actionlist_SheetContent_null_SheetContent-Night-1_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c53fdbe427ddb7088d9bd8f9b46a1281d145fd6f9ccc3a7de339750e3079075 -size 26498 +oid sha256:04c5ab1c0d9abebd7e189beb5cebecd5dfaa27a90a24ff499ad72a2ab4d003be +size 26307 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png index 920d470185..d743ef4fd4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Day-4_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b426d17bd2623e57d21ae8bb1c5f47247a729bb3c402da830dde4ca7554e8319 -size 26026 +oid sha256:4d3bd52edfa46104861e6362cdd608988d5ad1f3efba1e4318c0df4a5a129652 +size 26042 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png index b45fb3e146..331f2958d9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.messagecomposer_AttachmentSourcePickerMenu_null_AttachmentSourcePickerMenu-Night-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c3e621c437cbc681672bd938fc8e5f3e9c956463d7d1b185e3aabe6717fd024 -size 23729 +oid sha256:8a06d8d83e44809aa1f205a425de0f92dcd556ddcaea0904074cb05cd7ab87fd +size 23750 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png index be47b705f5..df5304ac9f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Day-29_29_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78884f6cdde3d493293186d078360d51d894e42f134b44729a9d44b7d9e1b260 -size 9822 +oid sha256:aa891e34644dca91750381bfdfa87b951f3326606ae517274e153c7959d20501 +size 10012 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png index 63f4ed66bc..3db0f7a27b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.event_TimelineItemEncryptedView_null_TimelineItemEncryptedView-Night-29_30_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3ee25c2e1aed91646e07465eabaa7419019f376b5870af6653016dc2403df0d -size 9701 +oid sha256:53286f8a92a448729ba15bb45076de77d0506112c6ca65ff5c35046314066552 +size 9879 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png index ea99de4248..96deb94eef 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Day-8_8_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56d2a4fe8ceadfb945622519910e13fbe3422561745efc4fe0362eb599fcb2ac -size 73633 +oid sha256:f0fa1ad2fe7085badb03368def655e9f175c497e91eb1de440df8fb64b2fe858 +size 74398 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png index 62c10e1bab..529f9ad95f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline_TimelineView_null_TimelineView-Night-8_9_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55fbd1123d267fea510345fc272a1b1a9bda7a8222eebf7e9d96a8202f071813 -size 70610 +oid sha256:d5537a6de5da545df178a198a402dd5fe67f08ff64f448c3ecd6245484766cc1 +size 71354 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png index ef6a89b482..197546dc67 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7ceba6d884500e6966d548aa048115f7dc326ed14420739b045de7fe75c0cce -size 45210 +oid sha256:b5c68ea146c7f1c3a8e39a6ecdb51e95fb99e371e4f2bb976fdde7d5849db071 +size 45105 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png index cfaff5a960..f2520abbf0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewDark_null_PreferencesRootViewDark--1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9ba978ff9b76534a9c90ca681268b249ee60865438361b13782269d3d4a7a53 -size 44542 +oid sha256:c1cc43c5157a456317171b85c98042dec9865d21121a0663530fda1fe1236a3f +size 44434 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png index 1617e91c7a..6995463dd3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0fe7924589afab07164f2c201c97c1715f38d39d0447614bc67eeb893e3701c -size 48261 +oid sha256:d4d1146622da22dd72d53d9d5e239b6f45d23ae4f3ed42f2b0d0902f25256569 +size 48168 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png index 9bbba9c45d..3c0beefa8a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.root_PreferencesRootViewLight_null_PreferencesRootViewLight--0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fa38f9897d0c743c0d44b5db8b57d88e522b8d603af04f02eb7b2f480e90756 -size 48162 +oid sha256:ad8dcdb0292dcdeed9c50d8abe2bc0bf09c728cac11bfddeac6e74512ee224e2 +size 48068 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png index 00ebc234da..096c9b98af 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Day-10_10_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a935000d8191f1682929c49d0dd3cf2fb8ab7908de574370717a1865967060c -size 22694 +oid sha256:066b94b72b0f5ee1be0e8eec636e927a5cb38d17e1a69ddf6355e588e60cbec0 +size 22538 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png index 90b2807e3e..64e940f97c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.user.editprofile_EditUserProfileView_null_EditUserProfileView-Night-10_11_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e13e24436b8d15cc981bc3e997af4c0140ee95513157bc23e0b4429de800aaf3 -size 21105 +oid sha256:86b8fa4f3b1b40e34fafe861c74ecdbb4a21dbd970f1b2445c9326d936e55630 +size 20888 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 8a25f4290a..93e1766598 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fd6f767bee84dc91389caa5b31699a2faba02c17cefbdbc033269bb7c9d79db -size 30376 +oid sha256:7550701aefd586a9ab2a4c17ac3d83f6d0055c38f13c8eb07a71fa04abbbed25 +size 30279 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png index 4179c023a7..aba49600c7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a56708fb7d9de71d778a675ab586876355fbd13384c9a348905dfb165e1f0554 -size 23701 +oid sha256:f2205a9500649d1faf74a6a327262b27a8ea51a040a0d98cb8485f5a23e0b226 +size 23616 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png index 08f65eefdb..a72956f4a2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a136414791bb62050d1972eb0cab956d47806c6ea21b1a7c2b7041c61151fe62 -size 56031 +oid sha256:0f827998598c1927cd389a470b26523741fa81b101af4378f61988012b28d88d +size 55922 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png index ae12b78071..0ff186467c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1184486c1440d78b181802ab5e67f9c2c52891e1c9d4e5eec36bc67ec4570b3 -size 30096 +oid sha256:8346a81bac3012380a5c437f5ad035aa59a195fc486268b62723e704d0c531ae +size 29995 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png index 44007d9af6..152cb2ef59 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88654c6ff85cb596e4bd07a63c263b8a25a01baa6859f9cf8fb3e96e1a9e17f1 -size 30097 +oid sha256:b47b1dffc574fedd3a8f973ee00b588e9c5d28f232d6f7d11aeb394b642f83cc +size 29999 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png index 0d931cfa14..ebef8377bd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e80a2dc627a919484e7a906546e6fd06169b5052124af6876c6f0f4b89fb06a -size 30309 +oid sha256:4374b71e56d6fef460876f3f6351091e1d91e3fc74ae09e14370b6213067194c +size 30182 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png index edffdf8e16..92a05bad78 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Day-0_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f507fac42cf9411f557e3b49e9979450c56ded313cb8071524fc2d9263054689 -size 27706 +oid sha256:d464efa5c974378b0b9565e6c4ebdaa183a5f92847f4cf8b63f5738972ea0d81 +size 27589 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 656ae5e057..fe1b454aae 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8949fa811aff6197026661079d4176e7199e399925470cd044d7e459ca10c23e -size 29049 +oid sha256:b259e49d427308ef4fa1c6a39dff305894f50c07251a12610d5c77ec80bad527 +size 28928 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png index d57e0b309b..963bb708d9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cda4b7b2b62f30e2d0bf6dea06545a9fa82bf1cf0c0278c5973f8e0e60f49784 -size 22717 +oid sha256:30c33c82a31fb2c2d8f4249fbf3ca1a835ff6364608f668224473fc65704fea4 +size 22609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png index e45f0574f3..19c1b05b87 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:403689283f938ad412caf12282d4a22deb49337a8e4039ece4618c08b1ac7347 -size 54301 +oid sha256:9769070b02ba47b3cda529def37ee0b8a926291e951d0c9ec7a2de055cc27826 +size 54186 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png index 38785f077f..6a002be107 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26377928764db3fa8a645a922a010c6450822ded6b55afa69667b15d5dfec4c7 -size 27978 +oid sha256:77a3989d4518a63b5ef1f076f4cd6902017fe6bb9dd32b8972b4a0fc86d5464f +size 27847 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png index f7de7aa2c9..c9228eac6d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b47d8274102e757e6374e1956e12b5ac5cbbe6fb9bc4991bc835e8464cc9e44 -size 28748 +oid sha256:2bf891ac65f3fd3f2a1e4f70e9bfd84e454fc547423b45c9c520c1b00d347119 +size 28628 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png index 0ecaac6847..60150a7702 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0b2e3d9518ba4a4b19b54ac2d7f5ccd1840d04a0f1fcf252e90c505107b2f36 -size 28164 +oid sha256:fe04e244412cd9aa62228b82e4bcb4c467dbadb9ce5a1018a2fc88ada7f8aa5f +size 28044 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png index 8e77061376..cf0421b84d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.edit_RoomDetailsEditView_null_RoomDetailsEditView-Night-0_1_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2be03a264d63f3e9d403cf0db50b099ce5d973acb17689303a83b9c332887376 -size 24376 +oid sha256:d8621a13a399b87d3a0f0bc68ae401bc3e69b4af45c659cf55fec0530f184407 +size 24272 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png index d5fe3105d2..5610ee9b86 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fdaa865f47a54001607d2c796255e0d50ec7f42f3908f9459bc2555af311274b -size 19572 +oid sha256:15c066163a768e20c319fcc8358eaf31ce618e617fc3959b08973dab1584a678 +size 19584 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png index b0b467544d..f79e1802f8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b43c9657a14b5dc8c6a33fb22d7227e99707f4b75286b1d2fa0d493bc1ef179e -size 17386 +oid sha256:6f47cf68a6c9d0ddf05e2e241af48a83348635980b372e60bb8697cebf92990e +size 17388 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png index b00c827576..0e4e7745e8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e722bb9e2d0782727f031ac115a6e06e7335cddf1f73201753c7474a1f42591 -size 19986 +oid sha256:712431fe032e6ee83d0d02a4218242128059e97619f9ff557652efb49dbf35dc +size 19943 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png index 60ce7700f0..8677b1c43c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fb6e059c2d95b2efe028f9995e90dccb42938eb56cfcc228d37c01427a1151e8 -size 36788 +oid sha256:36fca441b0648cce75502538df8e4ac077cc52f7bd4743a22f46a772d90d8d52 +size 36789 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png index 591358df1f..2aba1875d3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be13b77a299b2e55d46b51a277c1e1fbb668ee18e54cafbe96f49ef762e39864 +oid sha256:b60818b3142a0e67d650fc0449d51a7f3eb1199845650551ff3661d773146614 size 28151 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png index 06744c416e..0663c2391c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewDark_null_RoomMemberDetailsViewDark--3_5_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb0c078a9df374fed543ab11a55f7efa651c1d30f99370a48e1bcd5e8477f657 -size 20587 +oid sha256:fe1a6315150c3e372e9fc16cfcaa6f67917c54d9ce529dadd090045a4511c752 +size 20539 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png index 26058a7bfe..6c059ccac1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ccb14dda83f03265d2de3fcd8bfe1d88ded896563e06208887e779a1f1a776e -size 20035 +oid sha256:3071bfc9bdeb9049e28fa97d13d5b669462ceeea00ea0679db999616ff6c9b3d +size 20039 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png index 32030b1237..058cb80966 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dfad096895e851722bb777260f88ff843de36b9fb28993ad73ac927ec64343d -size 17787 +oid sha256:03037f931e2f922b5a1cd58d2000b9003e297605908a25545138972d025baec8 +size 17772 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png index 9487deb25b..2efbac461a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c91c4d504dee8f6ef3d86974f2e280bacbf749bf4d44690e706e08bf56b12b83 -size 20479 +oid sha256:4c875bc994695e3a3b0fe8b9fce149339ec4f0867ce0af1fce040e206f048256 +size 20453 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png index 4bbfb31331..c95a613f6f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:576027f683e1be5c343977e1a2808e2d8b9632db95552b3001bf6cb68eeef70c -size 41501 +oid sha256:7497745968e1c8cc9d46a3655cc8251f4c92c5f1f929aa6538ad32a395ff6ef4 +size 41499 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png index 2827c77e10..469b702dbf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64aed73e7a31bb58ef07eaee008c0fa2b1820319b180a4627e216e84919cf98a -size 32488 +oid sha256:0aa1c3bde0ddea037dd427d9a98b0563d4df9e754bf078eb52805d6a8967b4c7 +size 32493 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png index 7c16b7de84..dd4408bfca 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.members.details_RoomMemberDetailsViewLight_null_RoomMemberDetailsViewLight--2_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90b756e00831455e4e47df17587d382ae6986314aceb5bfe54bc9592bcd8b0dc -size 21074 +oid sha256:fd437191f000962ebb7f435c44d004530b265515bbaf5dd8af56938e2da074ff +size 21048 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png index d26299f528..e4ff848c76 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Day-5_5_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:372ff5eca3c8c9de6bb15d5ae9a55f1d8ec124c7e454d411be6bd28f78d3aed0 -size 24324 +oid sha256:2e7a76c0f4fe32692980089d2be095d9206c8fdec85aa2e7a057829d027b396e +size 24069 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png index f8beb00ae9..2ddb87f0ba 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettings_null_UserDefinedRoomNotificationSettings-Night-5_6_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3795832f75ba48402f228dbd5be5dc05b10dab00242922fb861c0f77fcd391e4 -size 22853 +oid sha256:e4d30bbb3b982c47a3632d2b7350a8e63e1c3430d4e588297c2ea55b5d5ecb94 +size 22642 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png index d48e98ffbf..dcdedbb448 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2d5651dbb53d95939d999538b86415cbc6e35334d418b4d922345b7e5413597 -size 52622 +oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 +size 52410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png index 4c88ba2eba..6b2561f178 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5e0c0d5cd843bc4b6b8010c845c311280ad03c96416ae311d6795d79e5126fcb -size 51046 +oid sha256:ce0882664a2cbf8d951003943ed36468e1e90b50ed8aca6ae3dfd87671c79899 +size 49421 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png index 70abc2078f..10c0da6c3b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:037159d025ba2c8b675b7cdbed7f223cd544471f96950b2f21b3c3e7a81fd69f -size 44133 +oid sha256:f198e19a1db870a5f7c8520f6b80f1d9e1cf74e864d0abeaeb843a9ccfd9b715 +size 37047 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png index 86d3e67efb..dcdedbb448 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8695ecfe2e090e382586008e79e767a9f2ca76fd09e92ae88d4a63cf245a0d54 -size 53487 +oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 +size 52410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png index 386bdd6eea..b10f766e50 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c890ffdf4d553dcbc4cecabc8acb434f32344a0b5e97c21b99485f113ab8aa6a -size 50466 +oid sha256:5c2f8f387a3d0998e73df5cc580c2199d6591c46aa86a87cc60c5659d9b5dee1 +size 50664 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png index 11c8c67636..a25e7ff0fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61083c142467bb3634710cc5739d55c04b89e2e705193e7f3e4375539b1e2bb5 -size 52149 +oid sha256:d88db5cc763396241af1692e70254e3114aeb090bd9489e16b42ba2cc313e710 +size 47505 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png index 11c8c67636..a25e7ff0fb 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61083c142467bb3634710cc5739d55c04b89e2e705193e7f3e4375539b1e2bb5 -size 52149 +oid sha256:d88db5cc763396241af1692e70254e3114aeb090bd9489e16b42ba2cc313e710 +size 47505 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png index 8f37512aae..dcdedbb448 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4438e1dde9935d4f1bbf651727edcce5b82b534fd4b45360622c063d82971da5 -size 53813 +oid sha256:cc3b73a8d026391bacbe66f774b2464a8455f35170bdd7e9316f06267b8913d7 +size 52410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png index 9dcb830def..de7e05d8c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetailsDark_null_RoomDetailsDark--1_3_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d95158f232b6d9339b5f5c62745897b046534018b6a68507ef7a0dbbd692813 -size 52463 +oid sha256:3f694f450626abb04d70854a9c99849b2dc306d791914664a1f9856066eb400e +size 52228 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png index d1f3592333..807bbab00e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b8490acab6fef18eda1b673caed8f499356532fe0383b5ea64eae3a98bf4d8c -size 53884 +oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade +size 53595 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png index 782e6bf7f6..6e124304fa 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:78974cfe5b86c3f6788c58250936c0e786f5843ddf24f8e9e3bb832d1a9e8ead -size 53935 +oid sha256:a9a983309ba1ca5b121ba68b9bafa705ed55b01c2592b8e74c0127fc3d6aee21 +size 52169 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png index b0a7809d5b..e734739cd2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:90065f5871a1a0886d5a51ac3334998e549326e49aa612dbe6c6e399422a453f -size 46716 +oid sha256:74b6720ebdc8ad46b9ee0efcbe0042008567b1565ef43c939064a51212ba6cae +size 39206 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png index 18db460f0c..807bbab00e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b365242f676e9fd7325e35b98580761b9c0dac1edf9a7b10e27c52c1604d68ce -size 54820 +oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade +size 53595 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png index 68579460b4..467e43fed1 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa7d1423e88bd4ff8abd0487525b25dfc30e912c03c626282f67f6b5a3681d56 -size 51948 +oid sha256:a376e6d8424bd49bbeeda9608ba5bdc65e7a0b4fcaf6811725dce2dac9c56837 +size 52134 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png index b792ec9bda..4ced14ce3b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edc60ef43aae57282fea83915e71d5a59fc38747631e13ddb9609b5393f01cd4 -size 54074 +oid sha256:cf97bb9e0548a8a733a8dcd092364f3b17efdb9816a3cd44e1d0c96f7b36874c +size 48603 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png index b792ec9bda..4ced14ce3b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edc60ef43aae57282fea83915e71d5a59fc38747631e13ddb9609b5393f01cd4 -size 54074 +oid sha256:cf97bb9e0548a8a733a8dcd092364f3b17efdb9816a3cd44e1d0c96f7b36874c +size 48603 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png index a1ecae8832..807bbab00e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a7ea54f3b28185d4af4ef042645daffc606bb09092a28e19f3a993aff086f5f -size 55111 +oid sha256:7d23ee0e0ad834b6bb8f5e971c968a711952df38bcb310a05f201542940fcade +size 53595 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png index 565e094dea..467668fa83 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomdetails.impl_RoomDetails_null_RoomDetails--0_2_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6769f788ba5731a7ee9af234827682800cf2031ccfcddca73d4ae0302ed34ddf -size 53633 +oid sha256:24a8a645034c340794e1bf3e30168af8f1e1083a3cece9a3d7dfa3e4077d24ed +size 53365 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png index 28386ad492..b7dc6f82be 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Day-1_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f990c6058d9dab7273cf886da59672f611c47b5507548fb18c66ceea16ccf1a8 -size 12257 +oid sha256:509e303b4e62632032881ec1fc3531ff5113e774316d80a1a76e3b04fd30506b +size 12555 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png index c234c4fd9a..d4b8e92df4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListModalBottomSheetContent_null_RoomListModalBottomSheetContent-Night-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0b16b915427c8e1e64d2f62c16cece51dc119e2844d50f585c9f245fca27f3c -size 11615 +oid sha256:76cd5aa507bcbcdba1ee3de894d2e79f4caa1831345c84d8458b46d358ac3da2 +size 11891 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png index 6f110aeb7c..48b4f45709 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68318c9078a0b3406c9a1b2608bac62e237aa484d1115744c209578520c679c0 -size 65331 +oid sha256:4c80b1212608cbc6fb00dcc5648e94df163eb02c01b98087a848e1d404af704a +size 65118 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png index 7e55524cc1..6267da6f44 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da1217533e35ecb2f5711d391810b121c08605ea80610257e4920486654b8e4b -size 86763 +oid sha256:65a353822daf6999f79499bc9e0c7038ed89b8bb28772a7c1ff487f338cf8e9d +size 86560 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png index 6f110aeb7c..48b4f45709 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68318c9078a0b3406c9a1b2608bac62e237aa484d1115744c209578520c679c0 -size 65331 +oid sha256:4c80b1212608cbc6fb00dcc5648e94df163eb02c01b98087a848e1d404af704a +size 65118 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png index 26c0e72094..4f1c518289 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:248ea17753be1b4c4024ea864187229b8004852c998567eee47bac86b7cca885 -size 65308 +oid sha256:93f7207d43babcf2810063e60c61a913507fadcb5d8597f019136894e01f9335 +size 65094 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png index c9569dde38..4c49e778c2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98ccf3edf3af713d99f11c785c9fa2a3aeebd59a7a653e3b742231a31640ea3d -size 66380 +oid sha256:f4457ac25047e469886b9fd2d547a87160bda23a0b398ecd4214e5426ebf36bf +size 66167 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png index 29da866545..b7fd617cab 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df66de28614a59895dd0af5cfa35615f67bcae5626c2c8bf6a1d36cf7246d3a2 -size 66770 +oid sha256:92a1f12cabc596dfa168a1746e1c8d0d1ba728c815fcb471bc1aa0fec9cee494 +size 66554 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png index 49ea3820da..329925b0bf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Day-2_2_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5df4007cd48966eee91becdca376c1da4951b1a33250fedc7978eca074bdd65 -size 89958 +oid sha256:0d4e0b8856d2a08170bc89e9ce9d8f53575a8e79ceac13625213e60ab9d7718c +size 89753 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png index f6da97df1c..5e0123fed5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ae852acf3b6b2c93a7bc06d0cff90bb0db127860ffe3627cccddcaebec7547a -size 67524 +oid sha256:3aba116389050f62ac9d61ae41599af35323fdf39a01a61a5bcb60a7ac5d9259 +size 67342 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png index 47a10ebca3..240ec106f0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:160f264d9b81551862685a9775c5ba64d95b2f9878bd63fef9d3514a861fd66b -size 88670 +oid sha256:9826ad9c429c5a6ece2ea2e5fa6fd1e602bb1453ce44ddcb9d952d33ec632729 +size 88498 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png index f6da97df1c..5e0123fed5 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ae852acf3b6b2c93a7bc06d0cff90bb0db127860ffe3627cccddcaebec7547a -size 67524 +oid sha256:3aba116389050f62ac9d61ae41599af35323fdf39a01a61a5bcb60a7ac5d9259 +size 67342 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png index 48a1315816..b81afb5e81 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04b542c31ec5426233733a95fa1c979942f38e96fde8b493eb6cbc0acad748a3 -size 67299 +oid sha256:fa09f985e9459b21d0663e3f1ffddbb57c7b47ce6255ad5bc10a285cbf55dbb2 +size 67122 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png index f28f423efa..f595818dc6 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9038678df851eabcffad0d471174a1f1ebb5762378277f60c0447a02b0dc2aed -size 69113 +oid sha256:d6b8d451c99fc0ed6e76b6bdfb809a409d975ef636500493f8021c1d2712b3fc +size 68942 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png index 16b3160fd8..df4b0812c3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:206a10817585b36643d53a89d2f8316f182276a3145955b9d90f456e8a999c5a -size 69474 +oid sha256:593f2f027fb111f205dd740c8ab193e10c2b8096fbf49181b5c066c4cd58d299 +size 69301 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png index 6ca01ebddb..134a0ebd5e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.roomlist.impl_RoomListView_null_RoomListView-Night-2_3_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c7f451e0284eecf1c37390b76749a7c37f753235b2285092d3995ae755a2042 -size 91566 +oid sha256:72d91b7b1b89f886c23123db3e297d84df46fc919bb60a4f8caa461b14d02f46 +size 91388 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 972ae5540e..ad273f4619 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2848fd51e5c11e31500ed1f1029d4dd5e3e9514e414c7bcb970baca30ff0d94f -size 60647 +oid sha256:8830c2822cdc08b72cf6b45d5b9becd29b1824c01002336f69dc4b8a432e28e2 +size 60504 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png index 77083c1d7e..944fb938d8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.signedout.impl_SignedOutView_null_SignedOutView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29ec7823e9c8eff8e62baef3fee40c5ce606a0d4618e9c4a7e3dfd9b1ebe2f69 -size 58881 +oid sha256:d0cf3d6480619228bace599debade40ccf67ecdeb545ba1a3115700e58db55e0 +size 58730 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png index 9d135a0b9b..f891a5843a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceCategory_null_Preferences_PreferenceCategory_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c38a3ec04f2e88faa51b95cebe18a61f6590150bf655a066544e4bcc5c3b00e6 -size 30441 +oid sha256:8d53b0847befd7041345248a6123de5dc8e8a1174ad473aaa9fc77a1e17c100c +size 30417 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png index f7c75aed61..d86d6ec555 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextDark_null_Preferences_PreferenceTextDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dc5c04af9085f12cbf4ffd5beddfe95f683358716bcb5676e235bf61cef0d037 -size 36257 +oid sha256:f03589a34474517da7faf7da9141ce59500be1c7169fe58dd5db0caafdae61ce +size 36339 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png index 03840a0a77..9546ffdacf 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextLight_null_Preferences_PreferenceTextLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30a1b09e10a4f0af425cf946b9cbb1f9c051b2437a1fe46024748b9bb61ea638 -size 37376 +oid sha256:e2b6e813796bbce262c8028785d52fa126c8a47ffab46f9782ab4695ab504bef +size 37350 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png index a8f6b06222..af08aa21f2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeDark_null_Preferences_PreferenceTextWithEndBadgeDark_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:719814e14c9adcefdd2bab346c1a510cdafed23f98e5e4dcb8eb7e958355992b -size 38154 +oid sha256:59a025a97a33c8fc8b17e1b666df6c6eb114d5580ed919108d72cd514094e80a +size 38234 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png index 10ed3ed71f..f59111209b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceTextWithEndBadgeLight_null_Preferences_PreferenceTextWithEndBadgeLight_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81ebf584035f87cf30edeb3d8e00c154154fa504c83471f6bf55b2e62946d99a -size 39486 +oid sha256:f596886595b77bf358da5c74422249c4e27426b5455b0c16ba2974dc3e4da1e4 +size 39448 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png index 41be7186b0..a9a6095b7e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Day_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:759a7ccc4c2cc545660cfad732330b19cb4701d305bfdf451ecb353f8de7500a -size 28588 +oid sha256:b22763db946d824e53440a1d65e6aaba8dac57edcb7e53bac1475fbac81b6feb +size 28567 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png index 8ea3601b6d..cf02bbd741 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.preferences_PreferenceView_null_PreferenceView-Night_1_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccc75c01551d2bf417ca005f0f2da7b916929d590d99257107362f6abe018653 -size 26556 +oid sha256:9994aec4ebcbad6458a4bbd8cf741ece95916187c283be38ea56fecbfe82f75e +size 26566 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png index ed55bd5463..47c301100c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Day_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:281766702461ba2844185d1a59c7ebfeed1acc1b216e3708340b963514848b11 -size 71438 +oid sha256:5c10bbc922df738a2b45424142dd476c71c336ac398fa93be98a1c1a421d566a +size 71443 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png index 08b6c7c8a3..32005147d7 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsCompound_null_IconsCompound-Night_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db09bdb47175a88a79a2166b030dfe682a18a631e3b78a7875e684326c4be9ef -size 68266 +oid sha256:a479cf4d877676d41df4e618571d8d51e44fe42dbb3e49d3db0f31cf0500928a +size 68262 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 781fea60c8..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e640dc434451e995e29594d50e0494f8f46792a6a76d498347abb9f48c7b40ea -size 36937 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0e233e8f64 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Day_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:244a6f7b697dcbff32ad6010145c2c97f60df2c3b9455560148f4c8265ba36f1 +size 77859 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 9e51298cb7..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:37c78d99633c381963224c091524236243273231a9698a632fec31e7131df3c7 -size 35085 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ff2ddc533c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsOther_null_IconsOther-Night_1_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5da6a3d6d4e7657a760054acb68449050ff16c34431fde68f2a718b0c7bd047f +size 75080 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Day_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Day_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 50f1985c0d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Day_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:750d377a57496379e29530e0e984a992a8a88b687a448862699e57cfcfb4d1d9 -size 45043 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Night_1_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Night_1_null,NEXUS_5,1.0,en].png deleted file mode 100644 index e60e74e4fb..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.icons_IconsSeptember_null_IconsSeptember-Night_1_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:89fcbc1c72c1020d58627c1782bfa7ef0e4844f0ea819b999be90c8b5d52e8ea -size 42998 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png index 1bbc577143..ce489e30e4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_DropdownMenuItem_null_Menus_DropdownMenuItem_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e9aaaad9ee2138d37f93e4d4adfbe235ca42c1cd5fd1eaa5fa55bbe55879379 -size 25687 +oid sha256:127101dd98df815f133e7f1ec1a21673d5552011279007d8964d0694fc529a9d +size 25714 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png index 7e75e60938..0bf007326e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Day-1_2_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c33717dcff9a7ba5f05ae7f84b1327c4e9a1834d58f40692c255a70d22d3860b -size 15144 +oid sha256:8669ba9062242c83bba1a040f1996289b545121e47866377d3f0199355045f44 +size 15184 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png index 321d1a07eb..9bd9804b74 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.matrix.ui.components_AvatarActionBottomSheet_null_AvatarActionBottomSheet-Night-1_3_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a93a7a7668f542a7984f0b6df5571acd202308b6d03500e0e2bedbddb544c30a -size 13312 +oid sha256:60582e5a930ca64ccae2dda6a3c9dcba5ca82df34c3aede655040feba92043b3 +size 13271 From f752147837c9b40e53683b9f0b5e0adb2f60e4f3 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 15 Nov 2023 14:02:24 +0100 Subject: [PATCH 046/102] Remove Element Call feature flag and revert base URL to `call.element.io` (#1809) Co-authored-by: ElementBot --- .../android/appconfig/ElementCallConfig.kt | 2 +- changelog.d/+remove-element-call-flag.misc | 4 + .../messages/impl/MessagesPresenter.kt | 4 +- .../impl/advanced/AdvancedSettingsEvents.kt | 1 - .../advanced/AdvancedSettingsPresenter.kt | 38 -------- .../impl/advanced/AdvancedSettingsState.kt | 7 -- .../advanced/AdvancedSettingsStateProvider.kt | 3 - .../impl/advanced/AdvancedSettingsView.kt | 25 ------ .../impl/developer/DeveloperSettingsEvents.kt | 1 + .../developer/DeveloperSettingsPresenter.kt | 31 ++++++- .../impl/developer/DeveloperSettingsState.kt | 9 +- .../DeveloperSettingsStateProvider.kt | 8 ++ .../impl/developer/DeveloperSettingsView.kt | 37 +++++++- .../advanced/AdvancedSettingsPresenterTest.kt | 64 +------------- .../DeveloperSettingsPresenterTest.kt | 86 +++++++++++++------ .../libraries/featureflag/api/FeatureFlags.kt | 6 -- .../impl/StaticFeatureFlagProvider.kt | 1 - ...ngsView-Day-1_1_null_3,NEXUS_5,1.0,en].png | 3 - ...sView-Night-1_2_null_3,NEXUS_5,1.0,en].png | 3 - ...ngsView-Day-3_3_null_0,NEXUS_5,1.0,en].png | 4 +- ...ngsView-Day-3_3_null_1,NEXUS_5,1.0,en].png | 4 +- ...ngsView-Day-3_3_null_2,NEXUS_5,1.0,en].png | 3 + ...sView-Night-3_4_null_0,NEXUS_5,1.0,en].png | 4 +- ...sView-Night-3_4_null_1,NEXUS_5,1.0,en].png | 4 +- ...sView-Night-3_4_null_2,NEXUS_5,1.0,en].png | 3 + 25 files changed, 164 insertions(+), 191 deletions(-) create mode 100644 changelog.d/+remove-element-call-flag.misc delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/ElementCallConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/ElementCallConfig.kt index 4182c32a42..bbd9f62689 100644 --- a/appconfig/src/main/kotlin/io/element/android/appconfig/ElementCallConfig.kt +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/ElementCallConfig.kt @@ -17,5 +17,5 @@ package io.element.android.appconfig object ElementCallConfig { - const val DEFAULT_BASE_URL = "https://call.element.dev" + const val DEFAULT_BASE_URL = "https://call.element.io" } diff --git a/changelog.d/+remove-element-call-flag.misc b/changelog.d/+remove-element-call-flag.misc new file mode 100644 index 0000000000..e019d57ac6 --- /dev/null +++ b/changelog.d/+remove-element-call-flag.misc @@ -0,0 +1,4 @@ +Remove Element Call feature flag, it's not always enabled. + +- Reverted the EC base URL to `https://call.element.io`. +- Moved the option to override this URL to developer settings from advanced settings. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 175cd34c97..a9a9e7fa50 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -157,10 +157,10 @@ class MessagesPresenter @AssistedInject constructor( val enableTextFormatting by preferencesStore.isRichTextEditorEnabledFlow().collectAsState(initial = true) var enableVoiceMessages by remember { mutableStateOf(false) } - var enableInRoomCalls by remember { mutableStateOf(false) } + // TODO add min power level to use this feature in the future? + val enableInRoomCalls = true LaunchedEffect(featureFlagsService) { enableVoiceMessages = featureFlagsService.isFeatureEnabled(FeatureFlags.VoiceMessages) - enableInRoomCalls = featureFlagsService.isFeatureEnabled(FeatureFlags.InRoomCalls) } fun handleEvents(event: MessagesEvents) { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index fea42baf5f..37641d684c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -19,5 +19,4 @@ package io.element.android.features.preferences.impl.advanced sealed interface AdvancedSettingsEvents { data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents - data class SetCustomElementCallBaseUrl(val baseUrl: String?) : AdvancedSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 4bb5abfa19..4b6b0354b6 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -17,25 +17,16 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import io.element.android.appconfig.ElementCallConfig import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import kotlinx.coroutines.launch -import java.net.URL import javax.inject.Inject class AdvancedSettingsPresenter @Inject constructor( private val preferencesStore: PreferencesStore, - private val featureFlagService: FeatureFlagService, ) : Presenter { @Composable @@ -47,14 +38,6 @@ class AdvancedSettingsPresenter @Inject constructor( val isDeveloperModeEnabled by preferencesStore .isDeveloperModeEnabledFlow() .collectAsState(initial = false) - val customElementCallBaseUrl by preferencesStore - .getCustomElementCallBaseUrlFlow() - .collectAsState(initial = null) - - var canDisplayElementCallSettings by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { - canDisplayElementCallSettings = featureFlagService.isFeatureEnabled(FeatureFlags.InRoomCalls) - } fun handleEvents(event: AdvancedSettingsEvents) { when (event) { @@ -64,34 +47,13 @@ class AdvancedSettingsPresenter @Inject constructor( is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch { preferencesStore.setDeveloperModeEnabled(event.enabled) } - is AdvancedSettingsEvents.SetCustomElementCallBaseUrl -> localCoroutineScope.launch { - // If the URL is either empty or the default one, we want to save 'null' to remove the custom URL - val urlToSave = event.baseUrl.takeIf { !it.isNullOrEmpty() && it != ElementCallConfig.DEFAULT_BASE_URL } - preferencesStore.setCustomElementCallBaseUrl(urlToSave) - } } } return AdvancedSettingsState( isRichTextEditorEnabled = isRichTextEditorEnabled, isDeveloperModeEnabled = isDeveloperModeEnabled, - customElementCallBaseUrlState = if (canDisplayElementCallSettings) { - CustomElementCallBaseUrlState( - baseUrl = customElementCallBaseUrl, - defaultUrl = ElementCallConfig.DEFAULT_BASE_URL, - validator = ::customElementCallUrlValidator, - ) - } else null, eventSink = { handleEvents(it) } ) } - - private fun customElementCallUrlValidator(url: String?): Boolean { - return runCatching { - if (url.isNullOrEmpty()) return@runCatching - val parsedUrl = URL(url) - if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol") - if (parsedUrl.host.isNullOrBlank()) error("Missing host") - }.isSuccess - } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index cd56078b27..899eac2744 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -19,12 +19,5 @@ package io.element.android.features.preferences.impl.advanced data class AdvancedSettingsState( val isRichTextEditorEnabled: Boolean, val isDeveloperModeEnabled: Boolean, - val customElementCallBaseUrlState: CustomElementCallBaseUrlState?, val eventSink: (AdvancedSettingsEvents) -> Unit ) - -data class CustomElementCallBaseUrlState( - val baseUrl: String?, - val defaultUrl: String, - val validator: (String?) -> Boolean, -) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index d3a2dee3f4..5ab50c8a16 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -24,17 +24,14 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider Unit, modifier: Modifier = Modifier, ) { - fun isUsingDefaultUrl(value: String?): Boolean { - val defaultUrl = state.customElementCallBaseUrlState?.defaultUrl ?: return false - return value.isNullOrEmpty() || value == defaultUrl - } - PreferencePage( modifier = modifier, onBackPressed = onBackPressed, @@ -58,23 +50,6 @@ fun AdvancedSettingsView( isChecked = state.isDeveloperModeEnabled, onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) }, ) - state.customElementCallBaseUrlState?.let { callUrlState -> - val supportingText = if (isUsingDefaultUrl(callUrlState.baseUrl)) { - stringResource(R.string.screen_advanced_settings_element_call_base_url_description) - } else { - callUrlState.baseUrl - } - PreferenceTextField( - headline = stringResource(R.string.screen_advanced_settings_element_call_base_url), - value = callUrlState.baseUrl ?: callUrlState.defaultUrl, - supportingText = supportingText, - validation = callUrlState.validator, - onValidationErrorMessage = stringResource(R.string.screen_advanced_settings_element_call_base_url_validation_error), - displayValue = { value -> !isUsingDefaultUrl(value) }, - keyboardOptions = KeyboardOptions.Default.copy(autoCorrect = false, keyboardType = KeyboardType.Uri), - onChange = { state.eventSink(AdvancedSettingsEvents.SetCustomElementCallBaseUrl(it)) } - ) - } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt index ce67916178..376f14f7bf 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvents.kt @@ -20,5 +20,6 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel sealed interface DeveloperSettingsEvents { data class UpdateEnabledFeature(val feature: FeatureUiModel, val isEnabled: Boolean) : DeveloperSettingsEvents + data class SetCustomElementCallBaseUrl(val baseUrl: String?) : DeveloperSettingsEvents data object ClearCache: DeveloperSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt index ebcd44b4db..ae6c70188b 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenter.kt @@ -19,12 +19,16 @@ package io.element.android.features.preferences.impl.developer import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshots.SnapshotStateMap +import io.element.android.appconfig.ElementCallConfig +import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter @@ -39,6 +43,7 @@ import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import java.net.URL import javax.inject.Inject class DeveloperSettingsPresenter @Inject constructor( @@ -46,6 +51,7 @@ class DeveloperSettingsPresenter @Inject constructor( private val computeCacheSizeUseCase: ComputeCacheSizeUseCase, private val clearCacheUseCase: ClearCacheUseCase, private val rageshakePresenter: RageshakePreferencesPresenter, + private val preferencesStore: PreferencesStore, ) : Presenter { @Composable @@ -64,8 +70,12 @@ class DeveloperSettingsPresenter @Inject constructor( val clearCacheAction = remember { mutableStateOf>(Async.Uninitialized) } + val customElementCallBaseUrl by preferencesStore + .getCustomElementCallBaseUrlFlow() + .collectAsState(initial = null) + LaunchedEffect(Unit) { - FeatureFlags.values().forEach { feature -> + FeatureFlags.entries.forEach { feature -> features[feature.key] = feature enabledFeatures[feature.key] = featureFlagService.isFeatureEnabled(feature) } @@ -86,6 +96,11 @@ class DeveloperSettingsPresenter @Inject constructor( event.isEnabled, triggerClearCache = { handleEvents(DeveloperSettingsEvents.ClearCache) } ) + is DeveloperSettingsEvents.SetCustomElementCallBaseUrl -> coroutineScope.launch { + // If the URL is either empty or the default one, we want to save 'null' to remove the custom URL + val urlToSave = event.baseUrl.takeIf { !it.isNullOrEmpty() && it != ElementCallConfig.DEFAULT_BASE_URL } + preferencesStore.setCustomElementCallBaseUrl(urlToSave) + } DeveloperSettingsEvents.ClearCache -> coroutineScope.clearCache(clearCacheAction) } } @@ -95,6 +110,11 @@ class DeveloperSettingsPresenter @Inject constructor( cacheSize = cacheSize.value, clearCacheAction = clearCacheAction.value, rageshakeState = rageshakeState, + customElementCallBaseUrlState = CustomElementCallBaseUrlState( + baseUrl = customElementCallBaseUrl, + defaultUrl = ElementCallConfig.DEFAULT_BASE_URL, + validator = ::customElementCallUrlValidator, + ), eventSink = ::handleEvents ) } @@ -145,5 +165,14 @@ class DeveloperSettingsPresenter @Inject constructor( } } +private fun customElementCallUrlValidator(url: String?): Boolean { + return runCatching { + if (url.isNullOrEmpty()) return@runCatching + val parsedUrl = URL(url) + if (parsedUrl.protocol !in listOf("http", "https")) error("Incorrect protocol") + if (parsedUrl.host.isNullOrBlank()) error("Missing host") + }.isSuccess +} + diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt index 8d79c9241d..d49e94b309 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsState.kt @@ -21,10 +21,17 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import kotlinx.collections.immutable.ImmutableList -data class DeveloperSettingsState constructor( +data class DeveloperSettingsState( val features: ImmutableList, val cacheSize: Async, val rageshakeState: RageshakePreferencesState, val clearCacheAction: Async, + val customElementCallBaseUrlState: CustomElementCallBaseUrlState, val eventSink: (DeveloperSettingsEvents) -> Unit ) + +data class CustomElementCallBaseUrlState( + val baseUrl: String?, + val defaultUrl: String, + val validator: (String?) -> Boolean, +) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt index ee5c897987..719b736c09 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsStateProvider.kt @@ -26,6 +26,13 @@ open class DeveloperSettingsStateProvider : PreviewParameterProvider !isUsingDefaultUrl(value) }, + keyboardOptions = KeyboardOptions.Default.copy(autoCorrect = false, keyboardType = KeyboardType.Uri), + onChange = { state.eventSink(DeveloperSettingsEvents.SetCustomElementCallBaseUrl(it)) } + ) + } +} + @Composable private fun FeatureListContent( state: DeveloperSettingsState, diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index c2a7fb3e20..1ea04c11dd 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -20,8 +20,6 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem @@ -37,23 +35,20 @@ class AdvancedSettingsPresenterTest { @Test fun `present - initial state`() = runTest { val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService() - val presenter = AdvancedSettingsPresenter(store, featureFlagService) + val presenter = AdvancedSettingsPresenter(store) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { val initialState = awaitLastSequentialItem() assertThat(initialState.isDeveloperModeEnabled).isFalse() assertThat(initialState.isRichTextEditorEnabled).isFalse() - assertThat(initialState.customElementCallBaseUrlState?.baseUrl).isNull() } } @Test fun `present - developer mode on off`() = runTest { val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService() - val presenter = AdvancedSettingsPresenter(store, featureFlagService) + val presenter = AdvancedSettingsPresenter(store) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -69,8 +64,7 @@ class AdvancedSettingsPresenterTest { @Test fun `present - rich text editor on off`() = runTest { val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService() - val presenter = AdvancedSettingsPresenter(store, featureFlagService) + val presenter = AdvancedSettingsPresenter(store) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -82,56 +76,4 @@ class AdvancedSettingsPresenterTest { assertThat(awaitItem().isRichTextEditorEnabled).isFalse() } } - - @Test - fun `present - custom element call url state is null if the feature flag is disabled`() = runTest { - val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService().apply { - setFeatureEnabled(FeatureFlags.InRoomCalls, false) - } - val presenter = AdvancedSettingsPresenter(store, featureFlagService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitLastSequentialItem() - assertThat(initialState.customElementCallBaseUrlState).isNull() - } - } - - @Test - fun `present - custom element call base url`() = runTest { - val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService(initialState = hashMapOf(FeatureFlags.InRoomCalls.key to true)) - val presenter = AdvancedSettingsPresenter(store, featureFlagService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitLastSequentialItem() - assertThat(initialState.customElementCallBaseUrlState).isNotNull() - assertThat(initialState.customElementCallBaseUrlState?.baseUrl).isNull() - - initialState.eventSink(AdvancedSettingsEvents.SetCustomElementCallBaseUrl("https://call.element.ahoy")) - val updatedItem = awaitItem() - assertThat(updatedItem.customElementCallBaseUrlState?.baseUrl).isEqualTo("https://call.element.ahoy") - } - } - - @Test - fun `present - custom element call base url validator needs at least an HTTP scheme and host`() = runTest { - val store = InMemoryPreferencesStore() - val featureFlagService = FakeFeatureFlagService().apply { - setFeatureEnabled(FeatureFlags.InRoomCalls, true) - } - val presenter = AdvancedSettingsPresenter(store, featureFlagService) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val urlValidator = awaitLastSequentialItem().customElementCallBaseUrlState!!.validator - assertThat(urlValidator("")).isTrue() // We allow empty string to clear the value and use the default one - assertThat(urlValidator("test")).isFalse() - assertThat(urlValidator("http://")).isFalse() - assertThat(urlValidator("geo://test")).isFalse() - assertThat(urlValidator("https://call.element.io")).isTrue() - } - } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt index 88778d4227..fb10ee272a 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsPresenterTest.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.appconfig.ElementCallConfig import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter @@ -28,7 +29,9 @@ import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataSto import io.element.android.libraries.architecture.Async import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -40,13 +43,7 @@ class DeveloperSettingsPresenterTest { @Test fun `present - ensures initial state is correct`() = runTest { - val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()) - val presenter = DeveloperSettingsPresenter( - FakeFeatureFlagService(), - FakeComputeCacheSizeUseCase(), - FakeClearCacheUseCase(), - rageshakePresenter - ) + val presenter = createDeveloperSettingsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -54,6 +51,8 @@ class DeveloperSettingsPresenterTest { assertThat(initialState.features).isEmpty() assertThat(initialState.clearCacheAction).isEqualTo(Async.Uninitialized) assertThat(initialState.cacheSize).isEqualTo(Async.Uninitialized) + assertThat(initialState.customElementCallBaseUrlState).isNotNull() + assertThat(initialState.customElementCallBaseUrlState.baseUrl).isNull() val loadedState = awaitItem() assertThat(loadedState.rageshakeState.isEnabled).isFalse() assertThat(loadedState.rageshakeState.isSupported).isTrue() @@ -64,32 +63,20 @@ class DeveloperSettingsPresenterTest { @Test fun `present - ensures feature list is loaded`() = runTest { - val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()) - val presenter = DeveloperSettingsPresenter( - FakeFeatureFlagService(), - FakeComputeCacheSizeUseCase(), - FakeClearCacheUseCase(), - rageshakePresenter, - ) + val presenter = createDeveloperSettingsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { skipItems(1) val state = awaitItem() - assertThat(state.features).hasSize(FeatureFlags.values().size) + assertThat(state.features).hasSize(FeatureFlags.entries.size) cancelAndIgnoreRemainingEvents() } } @Test fun `present - ensures state is updated when enabled feature event is triggered`() = runTest { - val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()) - val presenter = DeveloperSettingsPresenter( - FakeFeatureFlagService(), - FakeComputeCacheSizeUseCase(), - FakeClearCacheUseCase(), - rageshakePresenter, - ) + val presenter = createDeveloperSettingsPresenter() moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -109,12 +96,7 @@ class DeveloperSettingsPresenterTest { fun `present - clear cache`() = runTest { val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()) val clearCacheUseCase = FakeClearCacheUseCase() - val presenter = DeveloperSettingsPresenter( - FakeFeatureFlagService(), - FakeComputeCacheSizeUseCase(), - clearCacheUseCase, - rageshakePresenter, - ) + val presenter = createDeveloperSettingsPresenter(clearCacheUseCase = clearCacheUseCase, rageshakePresenter = rageshakePresenter) moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { @@ -130,4 +112,52 @@ class DeveloperSettingsPresenterTest { cancelAndIgnoreRemainingEvents() } } + + @Test + fun `present - custom element call base url`() = runTest { + val preferencesStore = InMemoryPreferencesStore() + val presenter = createDeveloperSettingsPresenter(preferencesStore = preferencesStore) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.customElementCallBaseUrlState.baseUrl).isNull() + initialState.eventSink(DeveloperSettingsEvents.SetCustomElementCallBaseUrl("https://call.element.ahoy")) + val updatedItem = awaitItem() + assertThat(updatedItem.customElementCallBaseUrlState.baseUrl).isEqualTo("https://call.element.ahoy") + assertThat(updatedItem.customElementCallBaseUrlState.defaultUrl).isEqualTo(ElementCallConfig.DEFAULT_BASE_URL) + } + } + + @Test + fun `present - custom element call base url validator needs at least an HTTP scheme and host`() = runTest { + val presenter = createDeveloperSettingsPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val urlValidator = awaitLastSequentialItem().customElementCallBaseUrlState.validator + assertThat(urlValidator("")).isTrue() // We allow empty string to clear the value and use the default one + assertThat(urlValidator("test")).isFalse() + assertThat(urlValidator("http://")).isFalse() + assertThat(urlValidator("geo://test")).isFalse() + assertThat(urlValidator("https://call.element.io")).isTrue() + } + } + + private fun createDeveloperSettingsPresenter( + featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), + cacheSizeUseCase: FakeComputeCacheSizeUseCase = FakeComputeCacheSizeUseCase(), + clearCacheUseCase: FakeClearCacheUseCase = FakeClearCacheUseCase(), + rageshakePresenter: DefaultRageshakePreferencesPresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore()), + preferencesStore: InMemoryPreferencesStore = InMemoryPreferencesStore(), + ): DeveloperSettingsPresenter { + return DeveloperSettingsPresenter( + featureFlagService = featureFlagService, + computeCacheSizeUseCase = cacheSizeUseCase, + clearCacheUseCase = clearCacheUseCase, + rageshakePresenter = rageshakePresenter, + preferencesStore = preferencesStore, + ) + } } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index b37dd538fd..0c08d25385 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -55,12 +55,6 @@ enum class FeatureFlags( description = "Allow user to lock/unlock the app with a pin code or biometrics", defaultValue = true, ), - InRoomCalls( - key = "feature.elementcall", - title = "Element call in rooms", - description = "Allow user to start or join a call in a room", - defaultValue = true, - ), Mentions( key = "feature.mentions", title = "Mentions", diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt index 6c9f24c979..89553cef3e 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt @@ -39,7 +39,6 @@ class StaticFeatureFlagProvider @Inject constructor() : FeatureFlags.NotificationSettings -> true FeatureFlags.VoiceMessages -> true FeatureFlags.PinUnlock -> true - FeatureFlags.InRoomCalls -> true FeatureFlags.Mentions -> false FeatureFlags.SecureStorage -> false } diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 2c48d3e55d..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a4e1af15c571d1f087005849b627d79387f8f5557bbc4233768bb3c2d940d628 -size 48510 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 519d9d4d10..0000000000 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa25ebf20fe62af56a548c3e962ae2e76e6e8e1b7e685d021306b733613e49eb -size 45462 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png index 00797a4f11..89d469e66d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c221b092112d787bdc0f9db9e9da3afe75d6b5754b98db119df3c63514da03b4 -size 53808 +oid sha256:b040737c81fb04596312a207e58c75580e48fdd70c1f5ee6abd3c73a846c0337 +size 58489 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png index 00797a4f11..89d469e66d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c221b092112d787bdc0f9db9e9da3afe75d6b5754b98db119df3c63514da03b4 -size 53808 +oid sha256:b040737c81fb04596312a207e58c75580e48fdd70c1f5ee6abd3c73a846c0337 +size 58489 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..312f3bb41c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Day-3_3_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b01d0d26eddb4b6edd900f0f17568d76108e4be97c979d63c5dcca1ca0ed83e +size 56942 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png index 46ec60efb6..0682509a00 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:419ba4098c8b77a6e6ea2af72eeb5a614a9982d04e003fedfcdee92772484eb1 -size 48839 +oid sha256:491beebfede0316e804b818c65794bd0c8f8125fc94f4a89f8e1b3ded5afbf3a +size 53597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png index 46ec60efb6..0682509a00 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:419ba4098c8b77a6e6ea2af72eeb5a614a9982d04e003fedfcdee92772484eb1 -size 48839 +oid sha256:491beebfede0316e804b818c65794bd0c8f8125fc94f4a89f8e1b3ded5afbf3a +size 53597 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..39d9744130 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.developer_DeveloperSettingsView_null_DeveloperSettingsView-Night-3_4_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16b24b31812e18fbf54568f2bae63c0827dc30e73d7924cdab6387df0c2ad41b +size 52062 From 5418ef00e1fe9ecb80851e0554f065594455868a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:32:35 +0000 Subject: [PATCH 047/102] Update plugin dependencycheck to v8.4.3 (#1810) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 39033f03e4..be85b30691 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -206,7 +206,7 @@ anvil = { id = "com.squareup.anvil", version.ref = "anvil" } detekt = "io.gitlab.arturbosch.detekt:1.23.3" ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" -dependencycheck = "org.owasp.dependencycheck:8.4.2" +dependencycheck = "org.owasp.dependencycheck:8.4.3" dependencyanalysis = "com.autonomousapps.dependency-analysis:1.25.0" paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" From d511b837a0c7b37391a848b15a50e2375fd57107 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 17:14:17 +0000 Subject: [PATCH 048/102] Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be85b30691..4e1329e3e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,7 +33,7 @@ test_core = "1.5.0" #other coil = "2.5.0" datetime = "0.4.1" -serialization_json = "1.6.0" +serialization_json = "1.6.1" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.0" From 8d8ec3adc76b8869874f8779469a3fbaa8968863 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 18:38:56 +0000 Subject: [PATCH 049/102] Update activity to v1.8.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be85b30691..9c48efa544 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" lifecycle = "2.7.0-beta01" -activity = "1.8.0" +activity = "1.8.1" media3 = "1.1.1" # Compose From fd5f8897f1d9bba15bd94ac60718239d9fae88a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 21:18:50 +0000 Subject: [PATCH 050/102] Update dependency androidx.browser:browser to v1.7.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be85b30691..8eb64e99b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayo androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayout_compose" } androidx_recyclerview = "androidx.recyclerview:recyclerview:1.3.2" -androidx_browser = "androidx.browser:browser:1.6.0" +androidx_browser = "androidx.browser:browser:1.7.0" androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } androidx_splash = "androidx.core:core-splashscreen:1.0.1" From adff64059bcd9985e6758f8a10602dfb2d6adc12 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Thu, 16 Nov 2023 08:34:53 +0100 Subject: [PATCH 051/102] Fix a changelog typo (#1820) --- changelog.d/+remove-element-call-flag.misc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/+remove-element-call-flag.misc b/changelog.d/+remove-element-call-flag.misc index e019d57ac6..a5f67489ed 100644 --- a/changelog.d/+remove-element-call-flag.misc +++ b/changelog.d/+remove-element-call-flag.misc @@ -1,4 +1,4 @@ -Remove Element Call feature flag, it's not always enabled. +Remove Element Call feature flag, it's now always enabled. - Reverted the EC base URL to `https://call.element.io`. - Moved the option to override this URL to developer settings from advanced settings. From 24235dccfbd3959571f5ed5e45c68f3b463bbc94 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 07:54:43 +0000 Subject: [PATCH 052/102] Update plugin dependencyanalysis to v1.26.0 (#1819) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be85b30691..33301d5ab2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -207,7 +207,7 @@ detekt = "io.gitlab.arturbosch.detekt:1.23.3" ktlint = "org.jlleitschuh.gradle.ktlint:11.6.1" dependencygraph = "com.savvasdalkitsis.module-dependency-graph:0.12" dependencycheck = "org.owasp.dependencycheck:8.4.3" -dependencyanalysis = "com.autonomousapps.dependency-analysis:1.25.0" +dependencyanalysis = "com.autonomousapps.dependency-analysis:1.26.0" paparazzi = "app.cash.paparazzi:1.3.1" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" } From f78ecee19f398891318a833252aa93d6880a357e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:14:05 +0000 Subject: [PATCH 053/102] Update lifecycle to v2.7.0-rc01 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dadea5b685..0743ac7457 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ core = "1.12.0" datastore = "1.0.0" constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" -lifecycle = "2.7.0-beta01" +lifecycle = "2.7.0-rc01" activity = "1.8.1" media3 = "1.1.1" From f80ea98e6c4b5f23f5a5ec9ea4252010a6015f9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:14:09 +0000 Subject: [PATCH 054/102] Update media3 to v1.2.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dadea5b685..ccf13c90bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ constraintlayout = "2.1.4" constraintlayout_compose = "1.0.1" lifecycle = "2.7.0-beta01" activity = "1.8.1" -media3 = "1.1.1" +media3 = "1.2.0" # Compose compose_bom = "2023.10.01" From 1b9e6ae71e7180d02cd38c622eea67dd5a57698a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Nov 2023 10:09:17 +0100 Subject: [PATCH 055/102] Read receipts: mapping --- .../timeline/item/event/EventTimelineItem.kt | 1 + .../matrix/api/timeline/item/event/Receipt.kt | 24 +++++++++++++++++++ .../item/event/EventTimelineItemMapper.kt | 14 ++++++++++- .../matrix/test/room/RoomSummaryFixture.kt | 3 +++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/Receipt.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt index 49108f8d54..9f38ce9441 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventTimelineItem.kt @@ -30,6 +30,7 @@ data class EventTimelineItem( val isRemote: Boolean, val localSendState: LocalEventSendState?, val reactions: List, + val receipts: List, val sender: UserId, val senderProfile: ProfileTimelineDetails, val timestamp: Long, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/Receipt.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/Receipt.kt new file mode 100644 index 0000000000..f638d71c89 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/Receipt.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.timeline.item.event + +import io.element.android.libraries.matrix.api.core.UserId + +data class Receipt( + val userId: UserId, + val timestamp: Long, +) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt index 21e7d51638..d761e91d6c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventTimelineItemMapper.kt @@ -20,18 +20,20 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo -import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.ReactionSender +import io.element.android.libraries.matrix.api.timeline.item.event.Receipt +import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import org.matrix.rustcomponents.sdk.Reaction import org.matrix.rustcomponents.sdk.EventItemOrigin as RustEventItemOrigin import org.matrix.rustcomponents.sdk.EventSendState as RustEventSendState import org.matrix.rustcomponents.sdk.EventTimelineItem as RustEventTimelineItem import org.matrix.rustcomponents.sdk.EventTimelineItemDebugInfo as RustEventTimelineItemDebugInfo import org.matrix.rustcomponents.sdk.ProfileDetails as RustProfileDetails +import org.matrix.rustcomponents.sdk.Receipt as RustReceipt class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMapper = TimelineEventContentMapper()) { @@ -45,6 +47,7 @@ class EventTimelineItemMapper(private val contentMapper: TimelineEventContentMap isRemote = it.isRemote(), localSendState = it.localSendState()?.map(), reactions = it.reactions().map(), + receipts = it.readReceipts().map(), sender = UserId(it.sender()), senderProfile = it.senderProfile().map(), timestamp = it.timestamp().toLong(), @@ -92,6 +95,15 @@ private fun List?.map(): List { } ?: emptyList() } +private fun Map.map(): List { + return map { + Receipt( + userId = UserId(it.key), + timestamp = it.value.timestamp?.toLong() ?: 0 + ) + }.sortedByDescending { it.timestamp } +} + private fun RustEventTimelineItemDebugInfo.map(): TimelineItemDebugInfo { return TimelineItemDebugInfo( model = model, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index ae473d8da8..d12f168789 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -37,6 +37,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageType import io.element.android.libraries.matrix.api.timeline.item.event.PollContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.Receipt import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -107,6 +108,7 @@ fun anEventTimelineItem( isRemote: Boolean = false, localSendState: LocalEventSendState? = null, reactions: List = emptyList(), + receipts: List = emptyList(), sender: UserId = A_USER_ID, senderProfile: ProfileTimelineDetails = aProfileTimelineDetails(), timestamp: Long = 0L, @@ -121,6 +123,7 @@ fun anEventTimelineItem( isRemote = isRemote, localSendState = localSendState, reactions = reactions, + receipts = receipts, sender = sender, senderProfile = senderProfile, timestamp = timestamp, From 8ec9bc2b848889ed44b70cc80a9403fcc8d1f068 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Nov 2023 10:16:37 +0100 Subject: [PATCH 056/102] Read receipts: feature flag --- .../android/libraries/featureflag/api/FeatureFlags.kt | 8 +++++++- .../featureflag/impl/StaticFeatureFlagProvider.kt | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 0c08d25385..9bb6d2f862 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -66,5 +66,11 @@ enum class FeatureFlags( title = "Chat backup", description = "Allow access to backup and restore chat history settings", defaultValue = false, - ) + ), + ReadReceipts( + key = "feature.readreceipts", + title = "Show read receipts", + description = null, + defaultValue = false, + ), } diff --git a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt index 89553cef3e..a1a2c3665c 100644 --- a/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt +++ b/libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt @@ -41,6 +41,7 @@ class StaticFeatureFlagProvider @Inject constructor() : FeatureFlags.PinUnlock -> true FeatureFlags.Mentions -> false FeatureFlags.SecureStorage -> false + FeatureFlags.ReadReceipts -> false } } else { false From 651a64b51b145fd8bf28573c4ee42fc6403af0c3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 15 Nov 2023 15:57:50 +0100 Subject: [PATCH 057/102] Fix typo --- .../impl/timeline/components/TimelineItemReactionsView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt index c5e94b0742..764b4cdea4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemReactionsView.kt @@ -69,7 +69,7 @@ private fun TimelineItemReactionsView( onToggleExpandClick: () -> Unit, modifier: Modifier = Modifier ) { - // In LTR languages we want an incoming message's reactions to be LRT and outgoing to be RTL. + // In LTR languages we want an incoming message's reactions to be LTR and outgoing to be RTL. // For RTL languages it should be the opposite. val currentLayout = LocalLayoutDirection.current val reactionsLayoutDirection = if (!isOutgoing) currentLayout From 87d5ed82b9e490042fbbf6c7243dbbd7402e5710 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 09:57:55 +0100 Subject: [PATCH 058/102] Read receipt: model and UI. --- .../android/appconfig/TimelineConfig.kt | 21 ++ features/messages/impl/build.gradle.kts | 1 + .../features/messages/impl/MessagesView.kt | 6 + .../impl/timeline/TimelinePresenter.kt | 18 +- .../impl/timeline/TimelineStateProvider.kt | 5 +- .../messages/impl/timeline/TimelineView.kt | 6 + .../components/TimelineItemEventRow.kt | 39 +++- .../receipt/ReadReceiptViewState.kt | 26 +++ .../receipt/ReadReceiptViewStateProvider.kt | 75 +++++++ .../receipt/TimelineItemReadReceiptView.kt | 186 ++++++++++++++++++ .../factories/TimelineItemsFactory.kt | 16 +- .../event/TimelineItemEventFactory.kt | 29 ++- .../impl/timeline/model/TimelineItem.kt | 1 + .../model/TimelineItemReadReceipts.kt | 40 ++++ .../components/avatar/AvatarSize.kt | 1 + .../src/main/res/drawable/ic_sending.xml | 29 +++ .../src/main/res/drawable/ic_sent.xml | 29 +++ 17 files changed, 519 insertions(+), 9 deletions(-) create mode 100644 appconfig/src/main/kotlin/io/element/android/appconfig/TimelineConfig.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt create mode 100644 libraries/designsystem/src/main/res/drawable/ic_sending.xml create mode 100644 libraries/designsystem/src/main/res/drawable/ic_sent.xml diff --git a/appconfig/src/main/kotlin/io/element/android/appconfig/TimelineConfig.kt b/appconfig/src/main/kotlin/io/element/android/appconfig/TimelineConfig.kt new file mode 100644 index 0000000000..625282f1e8 --- /dev/null +++ b/appconfig/src/main/kotlin/io/element/android/appconfig/TimelineConfig.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 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 io.element.android.appconfig + +object TimelineConfig { + const val maxReadReceiptToDisplay = 3 +} diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 809cc9441e..6faf07f916 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) api(projects.features.messages.api) + implementation(projects.appconfig) implementation(projects.features.call) implementation(projects.features.location.api) implementation(projects.features.poll.api) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index c9063f2a79..dbb8778131 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -212,6 +212,10 @@ fun MessagesView( onReactionClicked = ::onEmojiReactionClicked, onReactionLongClicked = ::onEmojiReactionLongClicked, onMoreReactionsClicked = ::onMoreReactionsClicked, + onReadReceiptClick = { // targetEvent -> + // TODO Open bottom sheet with read receipts + // state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ShowReadReceipts, targetEvent)) + }, onSendLocationClicked = onSendLocationClicked, onCreatePollClicked = onCreatePollClicked, onSwipeToReply = { targetEvent -> @@ -310,6 +314,7 @@ private fun MessagesViewContent( onReactionClicked: (key: String, TimelineItem.Event) -> Unit, onReactionLongClicked: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClicked: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, onMessageLongClicked: (TimelineItem.Event) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, onSendLocationClicked: () -> Unit, @@ -381,6 +386,7 @@ private fun MessagesViewContent( onReactionClicked = onReactionClicked, onReactionLongClicked = onReactionLongClicked, onMoreReactionsClicked = onMoreReactionsClicked, + onReadReceiptClick = onReadReceiptClick, onSwipeToReply = onSwipeToReply, ) }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0a0feedf65..fdd182b694 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -34,11 +34,14 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MessageEventType +import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus @@ -63,6 +66,7 @@ class TimelinePresenter @Inject constructor( private val analyticsService: AnalyticsService, private val verificationService: SessionVerificationService, private val encryptionService: EncryptionService, + private val featureFlagService: FeatureFlagService, ) : Presenter { private val timeline = room.timeline @@ -97,6 +101,9 @@ class TimelinePresenter @Inject constructor( } } + val readReceiptsEnabled by featureFlagService.isFeatureEnabledFlow(FeatureFlags.ReadReceipts).collectAsState(initial = false) + val membersState by room.membersStateFlow.collectAsState() + fun handleEvents(event: TimelineEvents) { when (event) { TimelineEvents.LoadMore -> localScope.paginateBackwards() @@ -136,7 +143,16 @@ class TimelinePresenter @Inject constructor( LaunchedEffect(Unit) { timeline .timelineItems - .onEach(timelineItemsFactory::replaceWith) + .onEach { + timelineItemsFactory.replaceWith( + timelineItems = it, + roomMembers = if (readReceiptsEnabled) { + membersState.roomMembers() + } else { + null + } + ) + } .onEach { timelineItems -> if (timelineItems.isEmpty()) { paginateBackwards() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index affb77e88a..ed98fedfa6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.timeline import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.model.anAggregatedReaction import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent @@ -118,11 +119,12 @@ internal fun aTimelineItemEvent( senderDisplayName: String = "Sender", content: TimelineItemEventContent = aTimelineItemTextContent(), groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, - sendState: LocalEventSendState = LocalEventSendState.Sent(eventId), + sendState: LocalEventSendState? = if (isMine) LocalEventSendState.Sent(eventId) else null, inReplyTo: InReplyTo? = null, isThreaded: Boolean = false, debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(), + readReceiptState: TimelineItemReadReceipts = TimelineItemReadReceipts.Hidden, ): TimelineItem.Event { return TimelineItem.Event( id = UUID.randomUUID().toString(), @@ -132,6 +134,7 @@ internal fun aTimelineItemEvent( senderAvatar = AvatarData("@senderId:domain", "sender", size = AvatarSize.TimelineSender), content = content, reactionsState = timelineItemReactions, + readReceiptState = readReceiptState, sentTime = "12:34", isMine = isMine, senderDisplayName = senderDisplayName, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 5b6b8c6d1d..1c08291f24 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -92,6 +92,7 @@ fun TimelineView( onReactionClicked: (emoji: String, TimelineItem.Event) -> Unit, onReactionLongClicked: (emoji: String, TimelineItem.Event) -> Unit, onMoreReactionsClicked: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier, ) { fun onReachedLoadMore() { @@ -135,6 +136,7 @@ fun TimelineView( onReactionClick = onReactionClicked, onReactionLongClick = onReactionLongClicked, onMoreReactionsClick = onMoreReactionsClicked, + onReadReceiptClick = onReadReceiptClick, onTimestampClicked = onTimestampClicked, sessionState = state.sessionState, eventSink = state.eventSink, @@ -179,6 +181,7 @@ private fun TimelineItemRow( onReactionClick: (key: String, TimelineItem.Event) -> Unit, onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, onMoreReactionsClick: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, onTimestampClicked: (TimelineItem.Event) -> Unit, onSwipeToReply: (TimelineItem.Event) -> Unit, eventSink: (TimelineEvents) -> Unit, @@ -214,6 +217,7 @@ private fun TimelineItemRow( onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, onTimestampClicked = onTimestampClicked, onSwipeToReply = { onSwipeToReply(timelineItem) }, eventSink = eventSink, @@ -255,6 +259,7 @@ private fun TimelineItemRow( onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, eventSink = eventSink, onSwipeToReply = {}, ) @@ -362,6 +367,7 @@ internal fun TimelineViewPreview( onReactionLongClicked = { _, _ -> }, onMoreReactionsClicked = {}, onSwipeToReply = {}, + onReadReceiptClick = {}, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index cfb81f71a7..ec277fb96b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -66,6 +66,8 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.event.toExtraPadding +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewState +import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState @@ -77,6 +79,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.features.messages.impl.timeline.model.receipts import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -124,6 +127,7 @@ fun TimelineItemEventRow( onReactionClick: (emoji: String, eventId: TimelineItem.Event) -> Unit, onReactionLongClick: (emoji: String, eventId: TimelineItem.Event) -> Unit, onMoreReactionsClick: (eventId: TimelineItem.Event) -> Unit, + onReadReceiptClick: (event: TimelineItem.Event) -> Unit, onSwipeToReply: () -> Unit, eventSink: (TimelineEvents) -> Unit, modifier: Modifier = Modifier @@ -183,6 +187,7 @@ fun TimelineItemEventRow( onReactionClicked = { emoji -> onReactionClick(emoji, event) }, onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, + onReadReceiptsClicked = { onReadReceiptClick(event) }, eventSink = eventSink, ) } @@ -200,6 +205,7 @@ fun TimelineItemEventRow( onReactionClicked = { emoji -> onReactionClick(emoji, event) }, onReactionLongClicked = { emoji -> onReactionLongClick(emoji, event) }, onMoreReactionsClicked = { onMoreReactionsClick(event) }, + onReadReceiptsClicked = { onReadReceiptClick(event) }, eventSink = eventSink, ) } @@ -240,6 +246,7 @@ private fun TimelineItemEventRowContent( inReplyToClicked: () -> Unit, onUserDataClicked: () -> Unit, onReactionClicked: (emoji: String) -> Unit, + onReadReceiptsClicked: () -> Unit, onReactionLongClicked: (emoji: String) -> Unit, onMoreReactionsClicked: (event: TimelineItem.Event) -> Unit, eventSink: (TimelineEvents) -> Unit, @@ -256,7 +263,12 @@ private fun TimelineItemEventRowContent( .wrapContentHeight() .fillMaxWidth(), ) { - val (sender, message, reactions) = createRefs() + val ( + sender, + message, + reactions, + readReceipts, + ) = createRefs() // Sender val avatarStrokeSize = 3.dp @@ -322,6 +334,23 @@ private fun TimelineItemEventRowContent( .padding(start = if (event.isMine) 16.dp else 36.dp, end = 16.dp) ) } + + // Read receipts / Send state + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + receipts = event.readReceiptState.receipts(), + ), + onReadReceiptsClicked = onReadReceiptsClicked, + modifier = Modifier + .constrainAs(readReceipts) { + if (event.reactionsState.reactions.isNotEmpty()) { + top.linkTo(reactions.bottom, margin = 4.dp) + } else { + top.linkTo(message.bottom, margin = 4.dp) + } + } + ) } } @@ -659,6 +688,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, eventSink = {}, @@ -680,6 +710,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, eventSink = {}, @@ -719,6 +750,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, eventSink = {}, @@ -742,6 +774,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, eventSink = {}, @@ -793,6 +826,7 @@ internal fun TimelineItemEventRowTimestampPreview( onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onTimestampClicked = {}, onSwipeToReply = {}, eventSink = {}, @@ -825,6 +859,7 @@ internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onSwipeToReply = {}, onTimestampClicked = {}, eventSink = {}, @@ -850,6 +885,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onSwipeToReply = {}, onTimestampClicked = {}, eventSink = {}, @@ -871,6 +907,7 @@ internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { onReactionClick = { _, _ -> }, onReactionLongClick = { _, _ -> }, onMoreReactionsClick = {}, + onReadReceiptClick = {}, onSwipeToReply = {}, onTimestampClicked = {}, eventSink = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt new file mode 100644 index 0000000000..2bf1d524ae --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt + +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import kotlinx.collections.immutable.ImmutableList + +data class ReadReceiptViewState( + val sendState: LocalEventSendState?, + val receipts: ImmutableList, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt new file mode 100644 index 0000000000..31134812f3 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.avatar.anAvatarData +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import kotlinx.collections.immutable.toImmutableList + +class ReadReceiptViewStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aReadReceiptViewState(), + aReadReceiptViewState(sendState = LocalEventSendState.NotSentYet), + aReadReceiptViewState(sendState = LocalEventSendState.Sent(EventId("\$eventId"))), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { repeat(1) { add(aReadReceiptData(it)) } }, + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { repeat(2) { add(aReadReceiptData(it)) } }, + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { repeat(3) { add(aReadReceiptData(it)) } }, + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { repeat(4) { add(aReadReceiptData(it)) } }, + ), + aReadReceiptViewState( + sendState = LocalEventSendState.Sent(EventId("\$eventId")), + receipts = mutableListOf().apply { repeat(5) { add(aReadReceiptData(it)) } }, + ), + ) +} + +private fun aReadReceiptViewState( + sendState: LocalEventSendState? = null, + receipts: List = emptyList(), +) = ReadReceiptViewState( + sendState = sendState, + receipts = receipts.toImmutableList(), +) + +private fun aReadReceiptData( + index: Int, + avatarData: AvatarData = anAvatarData( + id = "$index", + size = AvatarSize.TimelineReadReceipt + ), + timestamp: Long = 1629780000000L, +) = ReadReceiptData( + avatarData = avatarData, + timestamp = timestamp, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt new file mode 100644 index 0000000000..b826291c2f --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import io.element.android.appconfig.TimelineConfig +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.utils.CommonDrawables +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.theme.ElementTheme +import kotlinx.collections.immutable.ImmutableList + +@Composable +fun TimelineItemReadReceiptView( + state: ReadReceiptViewState, + onReadReceiptsClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + when (state.sendState) { + LocalEventSendState.Canceled -> Unit + LocalEventSendState.NotSentYet -> { + ReadReceiptsRow(modifier) { + Icon( + modifier = Modifier.padding(2.dp), + resourceId = CommonDrawables.ic_sending, + contentDescription = null, + tint = ElementTheme.colors.iconSecondary + ) + } + } + is LocalEventSendState.SendingFailed -> { + // Error? The timestamp is already displayed in red + } + is LocalEventSendState.Sent -> { + if (state.receipts.isEmpty()) { + ReadReceiptsRow(modifier = modifier) { + Icon( + modifier = Modifier.padding(2.dp), + resourceId = CommonDrawables.ic_sent, + contentDescription = null, + tint = ElementTheme.colors.iconSecondary + ) + } + } else { + ReadReceiptsRow(modifier = modifier) { + ReadReceiptsAvatars( + receipts = state.receipts, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .clickable { onReadReceiptsClicked() } + .padding(2.dp) + ) + } + } + } + null -> { + if (state.receipts.isNotEmpty()) { + ReadReceiptsRow(modifier = modifier) { + ReadReceiptsAvatars( + receipts = state.receipts, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .clickable { onReadReceiptsClicked() } + .padding(2.dp) + ) + } + } + } + } +} + +@Composable +private fun ReadReceiptsRow( + modifier: Modifier = Modifier, + content: @Composable () -> Unit = {}, +) { + Row( + modifier = modifier + .fillMaxWidth() + .height(AvatarSize.TimelineReadReceipt.dp + 8.dp) + .padding(horizontal = 18.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .padding(horizontal = 4.dp) + ) { + content() + } + } +} + +@Composable +private fun ReadReceiptsAvatars( + receipts: ImmutableList, + modifier: Modifier = Modifier +) { + val avatarSize = AvatarSize.TimelineReadReceipt.dp + val avatarStrokeSize = 1.dp + val avatarStrokeColor = MaterialTheme.colorScheme.background + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + contentAlignment = Alignment.CenterEnd, + ) { + receipts + .take(TimelineConfig.maxReadReceiptToDisplay) + .reversed() + .forEachIndexed { index, it -> + Box( + modifier = Modifier + .padding(end = (12.dp + avatarStrokeSize * 2) * index) + .size(size = avatarSize + avatarStrokeSize * 2) + .clip(CircleShape) + .background(avatarStrokeColor) + .zIndex(index.toFloat()), + contentAlignment = Alignment.Center, + ) { + Avatar( + avatarData = it.avatarData, + ) + } + } + } + if (receipts.size > 3) { + Text( + text = "+" + (receipts.size - TimelineConfig.maxReadReceiptToDisplay), + style = ElementTheme.typography.fontBodyXsRegular, + color = ElementTheme.colors.textSecondary, + ) + } + } +} + +@PreviewsDayNight +@Composable +internal fun TimelineItemReactionsViewPreview( + @PreviewParameter(ReadReceiptViewStateProvider::class) state: ReadReceiptViewState, +) = ElementPreview { + TimelineItemReadReceiptView( + state = state, + onReadReceiptsClicked = {}, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 8c894bc99a..e48720f92a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -66,19 +67,23 @@ class TimelineItemsFactory @Inject constructor( suspend fun replaceWith( timelineItems: List, + roomMembers: List?, ) = withContext(dispatchers.computation) { lock.withLock { diffCacheUpdater.updateWith(timelineItems) - buildAndEmitTimelineItemStates(timelineItems) + buildAndEmitTimelineItemStates(timelineItems, roomMembers) } } - private suspend fun buildAndEmitTimelineItemStates(timelineItems: List) { + private suspend fun buildAndEmitTimelineItemStates( + timelineItems: List, + roomMembers: List?, + ) { val newTimelineItemStates = ArrayList() for (index in diffCache.indices().reversed()) { val cacheItem = diffCache.get(index) if (cacheItem == null) { - buildAndCacheItem(timelineItems, index)?.also { timelineItemState -> + buildAndCacheItem(timelineItems, index, roomMembers)?.also { timelineItemState -> newTimelineItemStates.add(timelineItemState) } } else { @@ -91,11 +96,12 @@ class TimelineItemsFactory @Inject constructor( private suspend fun buildAndCacheItem( timelineItems: List, - index: Int + index: Int, + roomMembers: List?, ): TimelineItem? { val timelineItemState = when (val currentTimelineItem = timelineItems[index]) { - is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems) + is MatrixTimelineItem.Event -> eventItemFactory.create(currentTimelineItem, index, timelineItems, roomMembers) is MatrixTimelineItem.Virtual -> virtualItemFactory.create(currentTimelineItem) MatrixTimelineItem.Other -> null } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 14f8429c85..399eeec539 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -19,13 +19,16 @@ package io.element.android.features.messages.impl.timeline.factories.event import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.AggregatedReactionSender +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import kotlinx.collections.immutable.toImmutableList @@ -42,6 +45,7 @@ class TimelineItemEventFactory @Inject constructor( currentTimelineItem: MatrixTimelineItem.Event, index: Int, timelineItems: List, + roomMembers: List?, ): TimelineItem.Event { val currentSender = currentTimelineItem.event.sender val groupPosition = @@ -84,6 +88,7 @@ class TimelineItemEventFactory @Inject constructor( sentTime = sentTime, groupPosition = groupPosition, reactionsState = currentTimelineItem.computeReactionsState(), + readReceiptState = currentTimelineItem.computeReadReceiptState(roomMembers), localSendState = currentTimelineItem.event.localSendState, inReplyTo = currentTimelineItem.event.inReplyTo(), isThreaded = currentTimelineItem.event.isThreaded(), @@ -102,7 +107,7 @@ class TimelineItemEventFactory @Inject constructor( key = reaction.key, currentUserId = matrixClient.sessionId, senders = reaction.senders - .sortedByDescending{ it.timestamp } + .sortedByDescending { it.timestamp } .map { val date = Date(it.timestamp) AggregatedReactionSender( @@ -124,6 +129,28 @@ class TimelineItemEventFactory @Inject constructor( return TimelineItemReactions(aggregatedReactions.toImmutableList()) } + private fun MatrixTimelineItem.Event.computeReadReceiptState( + roomMembers: List?, + ): TimelineItemReadReceipts { + if (roomMembers == null) return TimelineItemReadReceipts.Hidden + return TimelineItemReadReceipts.ReadReceipts( + receipts = event.receipts + .map { receipt -> + val roomMember = roomMembers.find { it.userId == receipt.userId } + ReadReceiptData( + avatarData = AvatarData( + id = receipt.userId.value, + name = roomMember?.displayName ?: receipt.userId.value, + url = roomMember?.avatarUrl, + size = AvatarSize.TimelineReadReceipt, + ), + timestamp = receipt.timestamp + ) + } + .toImmutableList() + ) + } + private fun computeGroupPosition( currentTimelineItem: MatrixTimelineItem.Event, timelineItems: List, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index bd3090e390..5ceaba5550 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -65,6 +65,7 @@ sealed interface TimelineItem { val isMine: Boolean = false, val groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, val reactionsState: TimelineItemReactions, + val readReceiptState: TimelineItemReadReceipts, val localSendState: LocalEventSendState?, val inReplyTo: InReplyTo?, val isThreaded: Boolean, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt new file mode 100644 index 0000000000..fca0040790 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.model + +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +sealed interface TimelineItemReadReceipts { + /** Value when the feature is disabled */ + data object Hidden : TimelineItemReadReceipts + + data class ReadReceipts( + val receipts: ImmutableList, + ) : TimelineItemReadReceipts +} + +data class ReadReceiptData( + val avatarData: AvatarData, + val timestamp: Long +) + +fun TimelineItemReadReceipts.receipts(): ImmutableList = when (this) { + TimelineItemReadReceipts.Hidden -> persistentListOf() + is TimelineItemReadReceipts.ReadReceipts -> receipts +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt index b2004ed204..cb3858fa47 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt @@ -37,6 +37,7 @@ enum class AvatarSize(val dp: Dp) { TimelineRoom(32.dp), TimelineSender(32.dp), + TimelineReadReceipt(16.dp), MessageActionSender(32.dp), diff --git a/libraries/designsystem/src/main/res/drawable/ic_sending.xml b/libraries/designsystem/src/main/res/drawable/ic_sending.xml new file mode 100644 index 0000000000..92a8312f70 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_sending.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_sent.xml b/libraries/designsystem/src/main/res/drawable/ic_sent.xml new file mode 100644 index 0000000000..9a3ea31479 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_sent.xml @@ -0,0 +1,29 @@ + + + + + + + + From 900cf1881fb77a9a9c1840f8ae0618a461646f13 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 12:12:55 +0100 Subject: [PATCH 059/102] Read receipt: Bottom sheet --- .../messages/impl/MessagesPresenter.kt | 4 + .../features/messages/impl/MessagesState.kt | 2 + .../messages/impl/MessagesStateProvider.kt | 5 + .../features/messages/impl/MessagesView.kt | 20 ++-- .../receipt/ReadReceiptViewStateProvider.kt | 4 +- .../ReadReceiptBottomSheetEvents.kt | 24 ++++ .../ReadReceiptBottomSheetPresenter.kt | 52 +++++++++ .../ReadReceiptBottomSheetState.kt | 26 +++++ .../ReadReceiptBottomSheetStateProvider.kt | 44 +++++++ .../bottomsheet/ReadReceiptBottomSheetView.kt | 107 ++++++++++++++++++ .../event/TimelineItemEventFactory.kt | 6 +- .../model/TimelineItemReadReceipts.kt | 2 +- .../messages/fixtures/timelineItemsFactory.kt | 2 + .../components/avatar/AvatarSize.kt | 2 + .../matrix/ui/components/MatrixUserRow.kt | 2 + .../libraries/matrix/ui/components/UserRow.kt | 5 +- 16 files changed, 290 insertions(+), 17 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetEvents.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetState.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index a9a9e7fa50..2bb6e85df4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -42,6 +42,7 @@ import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.TimelineState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent @@ -97,6 +98,7 @@ class MessagesPresenter @AssistedInject constructor( private val customReactionPresenter: CustomReactionPresenter, private val reactionSummaryPresenter: ReactionSummaryPresenter, private val retrySendMenuPresenter: RetrySendMenuPresenter, + private val readReceiptBottomSheetPresenter: ReadReceiptBottomSheetPresenter, private val networkMonitor: NetworkMonitor, private val snackbarDispatcher: SnackbarDispatcher, private val messageSummaryFormatter: MessageSummaryFormatter, @@ -124,6 +126,7 @@ class MessagesPresenter @AssistedInject constructor( val customReactionState = customReactionPresenter.present() val reactionSummaryState = reactionSummaryPresenter.present() val retryState = retrySendMenuPresenter.present() + val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present() val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value) @@ -201,6 +204,7 @@ class MessagesPresenter @AssistedInject constructor( customReactionState = customReactionState, reactionSummaryState = reactionSummaryState, retrySendMenuState = retryState, + readReceiptBottomSheetState = readReceiptBottomSheetState, hasNetworkConnection = networkConnectionStatus == NetworkStatus.Online, snackbarMessage = snackbarMessage, showReinvitePrompt = showReinvitePrompt, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt index 5bce4f19a1..325c695988 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt @@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.timeline.TimelineState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState import io.element.android.libraries.architecture.Async @@ -43,6 +44,7 @@ data class MessagesState( val customReactionState: CustomReactionState, val reactionSummaryState: ReactionSummaryState, val retrySendMenuState: RetrySendMenuState, + val readReceiptBottomSheetState: ReadReceiptBottomSheetState, val hasNetworkConnection: Boolean, val snackbarMessage: SnackbarMessage?, val inviteProgress: Async, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 38d6289a88..720c385f89 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.aTimelineItemList import io.element.android.features.messages.impl.timeline.aTimelineState import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionState import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuState import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMessageComposerState @@ -96,6 +97,10 @@ fun aMessagesState() = MessagesState( selectedEvent = null, eventSink = {}, ), + readReceiptBottomSheetState = ReadReceiptBottomSheetState( + selectedEvent = null, + eventSink = {}, + ), actionListState = anActionListState(), customReactionState = CustomReactionState( target = CustomReactionState.Target.None, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index dbb8778131..c47bb99112 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -70,6 +70,8 @@ import io.element.android.features.messages.impl.timeline.components.customreact import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetView import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -212,9 +214,8 @@ fun MessagesView( onReactionClicked = ::onEmojiReactionClicked, onReactionLongClicked = ::onEmojiReactionLongClicked, onMoreReactionsClicked = ::onMoreReactionsClicked, - onReadReceiptClick = { // targetEvent -> - // TODO Open bottom sheet with read receipts - // state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.ShowReadReceipts, targetEvent)) + onReadReceiptClick = { event -> + state.readReceiptBottomSheetState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(event)) }, onSendLocationClicked = onSendLocationClicked, onCreatePollClicked = onCreatePollClicked, @@ -250,13 +251,9 @@ fun MessagesView( ) ReactionSummaryView(state = state.reactionSummaryState) - RetrySendMessageMenu( - state = state.retrySendMenuState - ) - - ReinviteDialog( - state = state - ) + RetrySendMessageMenu(state = state.retrySendMenuState) + ReadReceiptBottomSheetView(state = state.readReceiptBottomSheetState) + ReinviteDialog(state = state) // Since the textfield is now based on an Android view, this is no longer done automatically. // We need to hide the keyboard automatically when navigating out of this screen. @@ -412,7 +409,8 @@ private fun MessagesViewComposerBottomSheetContents( if (state.userHasPermissionToSendMessage) { Column(modifier = modifier.fillMaxWidth()) { MentionSuggestionsPickerView( - modifier = Modifier.heightIn(max = 230.dp) + modifier = Modifier + .heightIn(max = 230.dp) // Consume all scrolling, preventing the bottom sheet from being dragged when interacting with the list of suggestions .nestedScroll(object : NestedScrollConnection { override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt index 31134812f3..06520b1808 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt @@ -68,8 +68,8 @@ private fun aReadReceiptData( id = "$index", size = AvatarSize.TimelineReadReceipt ), - timestamp: Long = 1629780000000L, + formattedDate: String = "12:34", ) = ReadReceiptData( avatarData = avatarData, - timestamp = timestamp, + formattedDate = formattedDate, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetEvents.kt new file mode 100644 index 0000000000..213a43277d --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetEvents.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet + +import io.element.android.features.messages.impl.timeline.model.TimelineItem + +sealed interface ReadReceiptBottomSheetEvents { + data class EventSelected(val event: TimelineItem.Event) : ReadReceiptBottomSheetEvents + data object Dismiss : ReadReceiptBottomSheetEvents +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt new file mode 100644 index 0000000000..a4a55cbc9e --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenter.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.libraries.architecture.Presenter +import javax.inject.Inject + +class ReadReceiptBottomSheetPresenter @Inject constructor( +) : Presenter { + + @Composable + override fun present(): ReadReceiptBottomSheetState { + var selectedEvent: TimelineItem.Event? by remember { mutableStateOf(null) } + + fun handleEvent(event: ReadReceiptBottomSheetEvents) { + @Suppress("LiftReturnOrAssignment") + when (event) { + is ReadReceiptBottomSheetEvents.EventSelected -> { + selectedEvent = event.event + } + ReadReceiptBottomSheetEvents.Dismiss -> { + selectedEvent = null + } + } + } + + return ReadReceiptBottomSheetState( + selectedEvent = selectedEvent, + eventSink = { handleEvent(it) }, + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetState.kt new file mode 100644 index 0000000000..34db5488fa --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetState.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet + +import androidx.compose.runtime.Immutable +import io.element.android.features.messages.impl.timeline.model.TimelineItem + +@Immutable +data class ReadReceiptBottomSheetState( + val selectedEvent: TimelineItem.Event?, + val eventSink: (ReadReceiptBottomSheetEvents) -> Unit, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt new file mode 100644 index 0000000000..9b51dac76e --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.components.receipt.ReadReceiptViewStateProvider +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import kotlinx.collections.immutable.toImmutableList + +class ReadReceiptBottomSheetStateProvider : PreviewParameterProvider { + // Reuse the provider ReadReceiptViewStateProvider + private val readReceiptViewStateProvider = ReadReceiptViewStateProvider() + override val values: Sequence = readReceiptViewStateProvider.values + .filter { it.sendState is LocalEventSendState.Sent } + .map { readReceiptViewState -> + ReadReceiptBottomSheetState( + selectedEvent = aTimelineItemEvent( + readReceiptState = TimelineItemReadReceipts.ReadReceipts( + receipts = readReceiptViewState.receipts.map { readReceiptData -> + readReceiptData + .copy(avatarData = readReceiptData.avatarData.copy(id = "@${readReceiptData.avatarData.id}:localhost")) + }.toImmutableList() + ) + ), + eventSink = {}, + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt new file mode 100644 index 0000000000..bf46165cc6 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.features.messages.impl.timeline.model.receipts +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.theme.ElementTheme +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun ReadReceiptBottomSheetView( + state: ReadReceiptBottomSheetState, + modifier: Modifier = Modifier, +) { + val isVisible = state.selectedEvent != null + + val sheetState = rememberModalBottomSheetState() + val coroutineScope = rememberCoroutineScope() + if (isVisible) { + ModalBottomSheet( + modifier = modifier, +// modifier = modifier.navigationBarsPadding() - FIXME after https://issuetracker.google.com/issues/275849044 +// .imePadding() + sheetState = sheetState, + onDismissRequest = { + coroutineScope.launch { + sheetState.hide() + state.eventSink(ReadReceiptBottomSheetEvents.Dismiss) + } + } + ) { + ReadReceiptBottomSheetContents( + state = state, + ) + // FIXME remove after https://issuetracker.google.com/issues/275849044 + Spacer(modifier = Modifier.height(32.dp)) + } + } +} + +@Composable +private fun ColumnScope.ReadReceiptBottomSheetContents( + state: ReadReceiptBottomSheetState, +) { + val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty() + receipts.forEach { + MatrixUserRow( + matrixUser = MatrixUser( + UserId(it.avatarData.id), + it.avatarData.name, + it.avatarData.url, + ), + avatarSize = AvatarSize.ReadReceiptList, + trailingContent = { + Text( + text = it.formattedDate, + style = ElementTheme.typography.fontBodySmRegular, + color = ElementTheme.colors.textSecondary, + ) + } + ) + } +} + +@PreviewsDayNight +@Composable +internal fun ReadReceiptBottomSheetViewPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview { + // TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed + Column { + ReadReceiptBottomSheetContents( + state = state + ) + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 399eeec539..cde3f32d95 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -25,6 +25,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemGrou import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.libraries.core.bool.orTrue +import io.element.android.libraries.dateformatter.api.LastMessageTimestampFormatter import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.MatrixClient @@ -39,6 +40,7 @@ import javax.inject.Inject class TimelineItemEventFactory @Inject constructor( private val contentFactory: TimelineItemContentFactory, private val matrixClient: MatrixClient, + private val lastMessageTimestampFormatter: LastMessageTimestampFormatter, ) { suspend fun create( @@ -140,11 +142,11 @@ class TimelineItemEventFactory @Inject constructor( ReadReceiptData( avatarData = AvatarData( id = receipt.userId.value, - name = roomMember?.displayName ?: receipt.userId.value, + name = roomMember?.displayName, url = roomMember?.avatarUrl, size = AvatarSize.TimelineReadReceipt, ), - timestamp = receipt.timestamp + formattedDate = lastMessageTimestampFormatter.format(receipt.timestamp) ) } .toImmutableList() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt index fca0040790..417fa415ee 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt @@ -31,7 +31,7 @@ sealed interface TimelineItemReadReceipts { data class ReadReceiptData( val avatarData: AvatarData, - val timestamp: Long + val formattedDate: String, ) fun TimelineItemReadReceipts.receipts(): ImmutableList = when (this) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt index 18fa0e390e..972f105a4a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt @@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.timeline.groups.TimelineItemGro import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractorWithoutValidation import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter +import io.element.android.libraries.dateformatter.test.FakeLastMessageTimestampFormatter import io.element.android.libraries.eventformatter.api.TimelineEventFormatter import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem @@ -65,6 +66,7 @@ internal fun TestScope.aTimelineItemsFactory(): TimelineItemsFactory { failedToParseStateFactory = TimelineItemContentFailedToParseStateFactory(), ), matrixClient = matrixClient, + lastMessageTimestampFormatter = FakeLastMessageTimestampFormatter(), ), virtualItemFactory = TimelineItemVirtualFactory( daySeparatorFactory = TimelineItemDaySeparatorFactory( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt index cb3858fa47..0b4264e8e6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt @@ -39,6 +39,8 @@ enum class AvatarSize(val dp: Dp) { TimelineSender(32.dp), TimelineReadReceipt(16.dp), + ReadReceiptList(32.dp), + MessageActionSender(32.dp), RoomInviteItem(52.dp), diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt index 1380d15e8b..947afd8bbd 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/MatrixUserRow.kt @@ -31,11 +31,13 @@ fun MatrixUserRow( matrixUser: MatrixUser, modifier: Modifier = Modifier, avatarSize: AvatarSize = AvatarSize.UserListItem, + trailingContent: @Composable (() -> Unit)? = null, ) = UserRow( avatarData = matrixUser.getAvatarData(avatarSize), name = matrixUser.getBestName(), subtext = if (matrixUser.displayName.isNullOrEmpty()) null else matrixUser.userId.value, modifier = modifier, + trailingContent, ) @PreviewsDayNight diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt index 5e59ce7aef..7aa11ba193 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UserRow.kt @@ -38,6 +38,7 @@ internal fun UserRow( name: String, subtext: String?, modifier: Modifier = Modifier, + trailingContent: @Composable (() -> Unit)? = null, ) { Row( modifier = modifier @@ -49,7 +50,8 @@ internal fun UserRow( Avatar(avatarData) Column( modifier = Modifier - .padding(start = 12.dp), + .padding(start = 12.dp) + .weight(1f), ) { // Name Text( @@ -70,5 +72,6 @@ internal fun UserRow( ) } } + trailingContent?.invoke() } } From 8c7809e3161125aed3a215f95d792677a5937fd9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 13:20:48 +0100 Subject: [PATCH 060/102] Read receipt: fix test and add test for ReadReceiptBottomSheetPresenter --- .../messages/MessagesPresenterTest.kt | 4 ++ .../messages/fixtures/aMessageEvent.kt | 4 ++ .../timeline/TimelinePresenterTest.kt | 5 +- .../ReadReceiptBottomSheetPresenterTests.kt | 65 +++++++++++++++++++ .../groups/TimelineItemGrouperTest.kt | 3 + 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 2969f26580..5cb4609f1c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -35,6 +35,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionPresenter import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryPresenter +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent @@ -653,9 +654,11 @@ class MessagesPresenterTest { analyticsService = analyticsService, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + featureFlagService = FakeFeatureFlagService(), ) val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true) val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore) + val readReceiptBottomSheetPresenter = ReadReceiptBottomSheetPresenter() val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider()) val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom) val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom) @@ -668,6 +671,7 @@ class MessagesPresenterTest { customReactionPresenter = customReactionPresenter, reactionSummaryPresenter = reactionSummaryPresenter, retrySendMenuPresenter = retrySendMenuPresenter, + readReceiptBottomSheetPresenter = readReceiptBottomSheetPresenter, networkMonitor = FakeNetworkMonitor(), snackbarDispatcher = SnackbarDispatcher(), messageSummaryFormatter = FakeMessageSummaryFormatter(), diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt index 6d944075d1..91e1c2454e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt @@ -17,7 +17,9 @@ package io.element.android.features.messages.fixtures import io.element.android.features.messages.impl.timeline.aTimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.libraries.designsystem.components.avatar.AvatarData @@ -31,6 +33,7 @@ import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.room.aTimelineItemDebugInfo +import kotlinx.collections.immutable.toImmutableList internal fun aMessageEvent( eventId: EventId? = AN_EVENT_ID, @@ -50,6 +53,7 @@ internal fun aMessageEvent( sentTime = "", isMine = isMine, reactionsState = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts.ReadReceipts(emptyList().toImmutableList()), localSendState = sendState, inReplyTo = inReplyTo, debugInfo = debugInfo, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index eeca4026bf..9b8580a0ce 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -24,11 +24,12 @@ import im.vector.app.features.analytics.plan.PollEnd import im.vector.app.features.analytics.plan.PollVote import io.element.android.features.messages.fixtures.aMessageEvent import io.element.android.features.messages.fixtures.aTimelineItemsFactory -import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.session.SessionState +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem @@ -322,6 +323,7 @@ class TimelinePresenterTest { analyticsService = FakeAnalyticsService(), encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + featureFlagService = FakeFeatureFlagService(), ) } @@ -337,6 +339,7 @@ class TimelinePresenterTest { analyticsService = analyticsService, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + featureFlagService = FakeFeatureFlagService(), ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt new file mode 100644 index 0000000000..248fcad399 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetPresenterTests.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.timeline.components.receipt.bottomsheet + +import app.cash.molecule.RecompositionMode +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetPresenter +import io.element.android.tests.testutils.WarmUpRule +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class ReadReceiptBottomSheetPresenterTests { + + @get:Rule + val warmUpRule = WarmUpRule() + + @Test + fun `present - handle event selected`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val selectedEvent = aTimelineItemEvent() + initialState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(selectedEvent)) + assertThat(awaitItem().selectedEvent).isSameInstanceAs(selectedEvent) + } + } + + @Test + fun `present - handle dismiss`() = runTest { + val presenter = createPresenter() + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + val selectedEvent = aTimelineItemEvent() + initialState.eventSink(ReadReceiptBottomSheetEvents.EventSelected(selectedEvent)) + skipItems(1) + initialState.eventSink(ReadReceiptBottomSheetEvents.Dismiss) + assertThat(awaitItem().selectedEvent).isNull() + } + } + + private fun createPresenter() = ReadReceiptBottomSheetPresenter() +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt index 4c43a8552f..14bd7e00e1 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt @@ -21,7 +21,9 @@ import io.element.android.features.messages.fixtures.aMessageEvent import io.element.android.features.messages.impl.timeline.aTimelineItemReactions import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.impl.timeline.groups.computeGroupIdWith +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemReadReceipts import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel import io.element.android.libraries.designsystem.components.avatar.anAvatarData @@ -42,6 +44,7 @@ class TimelineItemGrouperTest { senderDisplayName = "", content = TimelineItemStateEventContent(body = "a state event"), reactionsState = aTimelineItemReactions(count = 0), + readReceiptState = TimelineItemReadReceipts.ReadReceipts(emptyList().toImmutableList()), localSendState = LocalEventSendState.Sent(AN_EVENT_ID), inReplyTo = null, isThreaded = false, From c25e01fe3987931875a348e03862635df9068786 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 13:26:09 +0100 Subject: [PATCH 061/102] Read receipt: Cleanup --- .../android/features/messages/impl/MessagesView.kt | 4 ++-- .../components/receipt/TimelineItemReadReceiptView.kt | 4 ++-- ...iptBottomSheetView.kt => ReadReceiptBottomSheet.kt} | 10 +++++----- .../impl/timeline/model/TimelineItemReadReceipts.kt | 4 +++- tools/detekt/detekt.yml | 3 +++ 5 files changed, 15 insertions(+), 10 deletions(-) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/{ReadReceiptBottomSheetView.kt => ReadReceiptBottomSheet.kt} (91%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index c47bb99112..0ba868ba8a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -71,7 +71,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents -import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetView +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -252,7 +252,7 @@ fun MessagesView( ReactionSummaryView(state = state.reactionSummaryState) RetrySendMessageMenu(state = state.retrySendMenuState) - ReadReceiptBottomSheetView(state = state.readReceiptBottomSheetState) + ReadReceiptBottomSheet(state = state.readReceiptBottomSheetState) ReinviteDialog(state = state) // Since the textfield is now based on an Android view, this is no longer done automatically. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index b826291c2f..c77a2e50dd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -148,7 +148,7 @@ private fun ReadReceiptsAvatars( receipts .take(TimelineConfig.maxReadReceiptToDisplay) .reversed() - .forEachIndexed { index, it -> + .forEachIndexed { index, readReceiptData -> Box( modifier = Modifier .padding(end = (12.dp + avatarStrokeSize * 2) * index) @@ -159,7 +159,7 @@ private fun ReadReceiptsAvatars( contentAlignment = Alignment.Center, ) { Avatar( - avatarData = it.avatarData, + avatarData = readReceiptData.avatarData, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt similarity index 91% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index bf46165cc6..d4fd994e3e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -41,7 +41,7 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun ReadReceiptBottomSheetView( +internal fun ReadReceiptBottomSheet( state: ReadReceiptBottomSheetState, modifier: Modifier = Modifier, ) { @@ -62,7 +62,7 @@ internal fun ReadReceiptBottomSheetView( } } ) { - ReadReceiptBottomSheetContents( + ReadReceiptBottomSheetContent( state = state, ) // FIXME remove after https://issuetracker.google.com/issues/275849044 @@ -72,7 +72,7 @@ internal fun ReadReceiptBottomSheetView( } @Composable -private fun ColumnScope.ReadReceiptBottomSheetContents( +private fun ColumnScope.ReadReceiptBottomSheetContent( state: ReadReceiptBottomSheetState, ) { val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty() @@ -97,10 +97,10 @@ private fun ColumnScope.ReadReceiptBottomSheetContents( @PreviewsDayNight @Composable -internal fun ReadReceiptBottomSheetViewPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview { +internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSheetStateProvider::class) state: ReadReceiptBottomSheetState) = ElementPreview { // TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed Column { - ReadReceiptBottomSheetContents( + ReadReceiptBottomSheetContent( state = state ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt index 417fa415ee..d9fc6c55af 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt @@ -21,7 +21,9 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf sealed interface TimelineItemReadReceipts { - /** Value when the feature is disabled */ + /** + * Value when the feature is disabled. + */ data object Hidden : TimelineItemReadReceipts data class ReadReceipts( diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index ca8370ae23..69608a0a1a 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -31,6 +31,9 @@ style: active: false UnusedPrivateMember: active: true + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 5 UnusedParameter: active: true UnnecessaryInnerClass: From 1684e89b7c9e64bac1b161d41a8541b8a3f28696 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 14:02:06 +0100 Subject: [PATCH 062/102] Read receipt: Open room member page when clicking on a read receipt from the bottom sheet. --- .../features/messages/impl/MessagesView.kt | 7 +++++-- .../bottomsheet/ReadReceiptBottomSheet.kt | 21 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 0ba868ba8a..d07afd43b5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -70,8 +70,8 @@ import io.element.android.features.messages.impl.timeline.components.customreact import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView -import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet +import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuEvents import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMessageMenu import io.element.android.features.messages.impl.timeline.model.TimelineItem @@ -252,7 +252,10 @@ fun MessagesView( ReactionSummaryView(state = state.reactionSummaryState) RetrySendMessageMenu(state = state.retrySendMenuState) - ReadReceiptBottomSheet(state = state.readReceiptBottomSheetState) + ReadReceiptBottomSheet( + state = state.readReceiptBottomSheetState, + onUserDataClicked = onUserDataClicked, + ) ReinviteDialog(state = state) // Since the textfield is now based on an Android view, this is no longer done automatically. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index d4fd994e3e..32c34ea2f9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -16,6 +16,7 @@ package io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer @@ -43,6 +44,7 @@ import kotlinx.coroutines.launch @Composable internal fun ReadReceiptBottomSheet( state: ReadReceiptBottomSheetState, + onUserDataClicked: (UserId) -> Unit, modifier: Modifier = Modifier, ) { val isVisible = state.selectedEvent != null @@ -64,6 +66,13 @@ internal fun ReadReceiptBottomSheet( ) { ReadReceiptBottomSheetContent( state = state, + onUserDataClicked = { + coroutineScope.launch { + sheetState.hide() + state.eventSink(ReadReceiptBottomSheetEvents.Dismiss) + onUserDataClicked.invoke(it) + } + }, ) // FIXME remove after https://issuetracker.google.com/issues/275849044 Spacer(modifier = Modifier.height(32.dp)) @@ -74,14 +83,17 @@ internal fun ReadReceiptBottomSheet( @Composable private fun ColumnScope.ReadReceiptBottomSheetContent( state: ReadReceiptBottomSheetState, + onUserDataClicked: (UserId) -> Unit, ) { val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty() receipts.forEach { + val userId = UserId(it.avatarData.id) MatrixUserRow( + modifier = Modifier.clickable { onUserDataClicked(userId) }, matrixUser = MatrixUser( - UserId(it.avatarData.id), - it.avatarData.name, - it.avatarData.url, + userId = userId, + displayName = it.avatarData.name, + avatarUrl = it.avatarData.url, ), avatarSize = AvatarSize.ReadReceiptList, trailingContent = { @@ -101,7 +113,8 @@ internal fun ReadReceiptBottomSheetPreview(@PreviewParameter(ReadReceiptBottomSh // TODO restore RetrySendMessageMenuBottomSheet once the issue with bottom sheet not being previewable is fixed Column { ReadReceiptBottomSheetContent( - state = state + state = state, + onUserDataClicked = {}, ) } } From d36943cc41b4a654bce33f66211a1b22fbf516ad Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 16 Nov 2023 14:11:00 +0100 Subject: [PATCH 063/102] Suppress usage of removeTimeline method (#1824) --- changelog.d/1824.misc | 1 + .../matrix/impl/room/RoomContentForwarder.kt | 19 ++++++------------- .../impl/timeline/RoomTimelineExtensions.kt | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 changelog.d/1824.misc diff --git a/changelog.d/1824.misc b/changelog.d/1824.misc new file mode 100644 index 0000000000..16cdc522ed --- /dev/null +++ b/changelog.d/1824.misc @@ -0,0 +1 @@ +Suppress usage of removeTimeline method. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index d539ec6a53..1c203e30d6 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -21,12 +21,11 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.ForwardEventException import io.element.android.libraries.matrix.impl.roomlist.roomOrNull +import io.element.android.libraries.matrix.impl.timeline.runWithTimelineListenerRegistered import kotlinx.coroutines.CancellationException import kotlinx.coroutines.withTimeout import org.matrix.rustcomponents.sdk.Room import org.matrix.rustcomponents.sdk.RoomListService -import org.matrix.rustcomponents.sdk.TimelineDiff -import org.matrix.rustcomponents.sdk.TimelineListener import kotlin.time.Duration.Companion.milliseconds /** @@ -56,16 +55,14 @@ class RoomContentForwarder( val failedForwardingTo = mutableSetOf() targetRooms.parallelMap { room -> room.use { targetRoom -> - val result = runCatching { + runCatching { // Sending a message requires a registered timeline listener - targetRoom.addTimelineListener(NoOpTimelineListener) - withTimeout(timeoutMs.milliseconds) { - targetRoom.send(content) + targetRoom.runWithTimelineListenerRegistered { + withTimeout(timeoutMs.milliseconds) { + targetRoom.send(content) + } } } - // After sending, we remove the timeline - targetRoom.removeTimeline() - result }.onFailure { failedForwardingTo.add(RoomId(room.id())) if (it is CancellationException) { @@ -78,8 +75,4 @@ class RoomContentForwarder( throw ForwardEventException(toRoomIds.toList()) } } - - private object NoOpTimelineListener : TimelineListener { - override fun onUpdate(diff: List) = Unit - } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt index bddd2bc872..e87ae74f30 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RoomTimelineExtensions.kt @@ -70,3 +70,17 @@ internal fun Room.backPaginationStatusFlow(): Flow = subscribeToBackPaginationStatus(listener) } }.buffer(Channel.UNLIMITED) + +internal suspend fun Room.runWithTimelineListenerRegistered(action: suspend () -> Unit) { + val result = addTimelineListener(NoOpTimelineListener) + try { + action() + } finally { + result.itemsStream.cancelAndDestroy() + result.items.destroyAll() + } +} + +private object NoOpTimelineListener : TimelineListener { + override fun onUpdate(diff: List) = Unit +} From b70b7f6fac42dc579e30eafb1fd9bde707dc46e9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 14:27:14 +0100 Subject: [PATCH 064/102] Read receipt: Do not impact screenshot test for timeline. --- .../features/messages/impl/timeline/TimelineStateProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index ed98fedfa6..51fddf0c6d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -119,7 +119,7 @@ internal fun aTimelineItemEvent( senderDisplayName: String = "Sender", content: TimelineItemEventContent = aTimelineItemTextContent(), groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, - sendState: LocalEventSendState? = if (isMine) LocalEventSendState.Sent(eventId) else null, + sendState: LocalEventSendState? = null, inReplyTo: InReplyTo? = null, isThreaded: Boolean = false, debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), From f80ec8a8038b39a9e581acc407354cdb0d137441 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:25:56 +0000 Subject: [PATCH 065/102] Update wysiwyg to v2.17.0 (#1827) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc2f1c74c7..6139803131 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,7 @@ serialization_json = "1.6.0" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.0" -wysiwyg = "2.16.0" +wysiwyg = "2.17.0" # DI dagger = "2.48.1" From 589eeb4a515b426025d7924e45077cedcff96bbe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 16 Nov 2023 17:53:34 +0100 Subject: [PATCH 066/102] Fix wrong list used when forwarding an Event to some rooms fails. --- .../android/libraries/matrix/impl/room/RoomContentForwarder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt index 1c203e30d6..2bfef368a9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomContentForwarder.kt @@ -72,7 +72,7 @@ class RoomContentForwarder( } if (failedForwardingTo.isNotEmpty()) { - throw ForwardEventException(toRoomIds.toList()) + throw ForwardEventException(failedForwardingTo.toList()) } } } From 8bc2a4d3a5abdd5a56c63543117b7c9277220e55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:51:59 +0000 Subject: [PATCH 067/102] Update android.gradle.plugin to v8.1.4 (#1830) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6139803131..f009190dd2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ [versions] # Project -android_gradle_plugin = "8.1.3" +android_gradle_plugin = "8.1.4" kotlin = "1.9.20" ksp = "1.9.20-1.0.14" firebaseAppDistribution = "4.0.1" From 63d03be28c7b207d817797a0f8e6f1c9127b69b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 07:58:41 +0000 Subject: [PATCH 068/102] Update dependency com.google.firebase:firebase-bom to v32.6.0 (#1831) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f009190dd2..b43b4e7072 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,7 +59,7 @@ android_desugar = "com.android.tools:desugar_jdk_libs:2.0.4" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } gms_google_services = "com.google.gms:google-services:4.4.0" # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:32.5.0" +google_firebase_bom = "com.google.firebase:firebase-bom:32.6.0" firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } # AndroidX From fe1ffe3aae99443c235b76cca4b1f8d4d03610e1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 09:44:50 +0000 Subject: [PATCH 069/102] Update dependency org.matrix.rustcomponents:sdk-android to v0.1.68 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b43b4e7072..4f33b98fe0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -144,7 +144,7 @@ jsoup = "org.jsoup:jsoup:1.16.2" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.67" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.68" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } From 3ec5c0c52b0888c42f27d7555be40fa2b4e5088b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 10:07:15 +0100 Subject: [PATCH 070/102] Sync strings --- .../ui-strings/src/main/res/values/localazy.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 6eb51a87c8..c2f969551c 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -9,12 +9,14 @@ "Play" "Poll" "Ended poll" + "Read by %1$s" "Send files" "Show password" "Start a call" "User menu" "Record voice message." "Stop recording" + "Read by %1$s and %2$s" "Accept" "Add to timeline" "Back" @@ -81,6 +83,7 @@ "Start verification" "Tap to load map" "Take photo" + "Tap for options" "Try again" "View source" "Yes" @@ -89,12 +92,14 @@ "Acceptable use policy" "Advanced settings" "Analytics" + "Appearance" "Audio" "Bubbles" "Chat backup" "Copyright" "Creating room…" "Left room" + "Dark" "Decryption error" "Developer options" "(edited)" @@ -113,6 +118,7 @@ "Install APK" "This Matrix ID can\'t be found, so the invite might not be received." "Leaving room" + "Light" "Link copied to clipboard" "Loading…" "Message" @@ -146,7 +152,10 @@ "Search for someone" "Search results" "Security" + "Seen by" "Sending…" + "Sending failed" + "Sent" "Server not supported" "Server URL" "Settings" @@ -157,6 +166,7 @@ "Success" "Suggestions" "Syncing" + "System" "Text" "Third-party notices" "Thread" @@ -197,6 +207,10 @@ "%1$d digit entered" "%1$d digits entered" + + "Read by %1$s and %2$d other" + "Read by %1$s and %2$d others" + "%1$d member" "%1$d members" From d1df0e47f1cf0192bf341276a0c69a590d1469ab Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 10:35:36 +0100 Subject: [PATCH 071/102] Read receipt: Improve accessibility --- .../receipt/TimelineItemReadReceiptView.kt | 35 ++++++++++++++++++- .../components/avatar/AvatarData.kt | 4 +++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index c77a2e50dd..810dc31541 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -32,6 +32,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex @@ -39,6 +43,7 @@ import io.element.android.appconfig.TimelineConfig import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.avatar.getBestName import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.Icon @@ -46,6 +51,8 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonPlurals +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @Composable @@ -137,8 +144,12 @@ private fun ReadReceiptsAvatars( val avatarSize = AvatarSize.TimelineReadReceipt.dp val avatarStrokeSize = 1.dp val avatarStrokeColor = MaterialTheme.colorScheme.background + val receiptDescription = computeReceiptDescription(receipts) Row( - modifier = modifier, + modifier = modifier + .clearAndSetSemantics { + stateDescription = receiptDescription + }, horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize), verticalAlignment = Alignment.CenterVertically, ) { @@ -174,6 +185,28 @@ private fun ReadReceiptsAvatars( } } +@Composable +private fun computeReceiptDescription(receipts: ImmutableList): String { + return when (receipts.size) { + 0 -> "" // Cannot happen + 1 -> stringResource( + id = CommonStrings.a11y_read_receipts_single, + receipts[0].avatarData.getBestName() + ) + 2 -> stringResource( + id = CommonStrings.a11y_read_receipts_multiple, + receipts[0].avatarData.getBestName(), + receipts[1].avatarData.getBestName(), + ) + else -> pluralStringResource( + id = CommonPlurals.a11y_read_receipts_multiple_with_others, + count = receipts.size - 1, + receipts[0].avatarData.getBestName(), + receipts.size - 1 + ) + } +} + @PreviewsDayNight @Composable internal fun TimelineItemReactionsViewPreview( diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarData.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarData.kt index 9ba984f565..7bcb8bf1dd 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarData.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarData.kt @@ -58,3 +58,7 @@ data class AvatarData( .uppercase() } } + +fun AvatarData.getBestName(): String { + return name?.takeIf { it.isNotEmpty() } ?: id +} From c8b8938aa83390b349cf019edb69fbade36cac1b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 11:49:25 +0100 Subject: [PATCH 072/102] Read receipt: BottomSheet: Add "seen by" title. --- .../receipt/bottomsheet/ReadReceiptBottomSheet.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index 32c34ea2f9..d17debe7da 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -26,18 +26,21 @@ import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.timeline.model.receipts import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.ListItem import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserRow import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @@ -85,6 +88,11 @@ private fun ColumnScope.ReadReceiptBottomSheetContent( state: ReadReceiptBottomSheetState, onUserDataClicked: (UserId) -> Unit, ) { + ListItem( + headlineContent = { + Text(text = stringResource(id = CommonStrings.common_seen_by)) + } + ) val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty() receipts.forEach { val userId = UserId(it.avatarData.id) From 014b15771b0f8940670cecc86c21290cf71e97b0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 16:40:15 +0100 Subject: [PATCH 073/102] Read receipt: Rework how the feature flag is used. tom --- .../impl/timeline/TimelinePresenter.kt | 6 ++- .../messages/impl/timeline/TimelineState.kt | 1 + .../impl/timeline/TimelineStateProvider.kt | 10 ++++- .../messages/impl/timeline/TimelineView.kt | 4 ++ .../components/TimelineItemEventRow.kt | 43 ++++++++++++------- .../bottomsheet/ReadReceiptBottomSheet.kt | 3 +- .../ReadReceiptBottomSheetStateProvider.kt | 2 +- .../factories/TimelineItemsFactory.kt | 6 +-- .../event/TimelineItemEventFactory.kt | 7 ++- .../model/TimelineItemReadReceipts.kt | 19 ++------ .../messages/fixtures/aMessageEvent.kt | 2 +- .../groups/TimelineItemGrouperTest.kt | 2 +- 12 files changed, 59 insertions(+), 46 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index fdd182b694..1361415dca 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -147,9 +147,10 @@ class TimelinePresenter @Inject constructor( timelineItemsFactory.replaceWith( timelineItems = it, roomMembers = if (readReceiptsEnabled) { - membersState.roomMembers() + membersState.roomMembers().orEmpty() } else { - null + // Give an empty list to not affect performance + emptyList() } ) } @@ -166,6 +167,7 @@ class TimelinePresenter @Inject constructor( userHasPermissionToSendMessage = userHasPermissionToSendMessage, paginationState = paginationState, timelineItems = timelineItems, + showReadReceipts = readReceiptsEnabled, hasNewItems = hasNewItems.value, sessionState = sessionState, eventSink = ::handleEvents diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt index 173e33b9c9..62836db130 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt @@ -26,6 +26,7 @@ import kotlinx.collections.immutable.ImmutableList @Immutable data class TimelineState( val timelineItems: ImmutableList, + val showReadReceipts: Boolean, val highlightedEventId: EventId?, val userHasPermissionToSendMessage: Boolean, val paginationState: MatrixTimeline.PaginationState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index 51fddf0c6d..3defab97f5 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -16,6 +16,7 @@ package io.element.android.features.messages.impl.timeline +import io.element.android.features.messages.impl.timeline.model.ReadReceiptData import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions @@ -44,6 +45,7 @@ import kotlin.random.Random fun aTimelineState(timelineItems: ImmutableList = persistentListOf()) = TimelineState( timelineItems = timelineItems, + showReadReceipts = false, paginationState = MatrixTimeline.PaginationState( isBackPaginating = false, hasMoreToLoadBackwards = true, @@ -124,7 +126,7 @@ internal fun aTimelineItemEvent( isThreaded: Boolean = false, debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(), - readReceiptState: TimelineItemReadReceipts = TimelineItemReadReceipts.Hidden, + readReceiptState: TimelineItemReadReceipts = aTimelineItemReadReceipts(), ): TimelineItem.Event { return TimelineItem.Event( id = UUID.randomUUID().toString(), @@ -176,6 +178,12 @@ internal fun aTimelineItemDebugInfo( model, originalJson, latestEditedJson ) +internal fun aTimelineItemReadReceipts(): TimelineItemReadReceipts { + return TimelineItemReadReceipts( + receipts = emptyList().toImmutableList(), + ) +} + fun aGroupedEvents(id: Long = 0): TimelineItem.GroupedEvents { val event = aTimelineItemEvent( isMine = true, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 1c08291f24..7e4030a7c3 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -127,6 +127,7 @@ fun TimelineView( ) { timelineItem -> TimelineItemRow( timelineItem = timelineItem, + showReadReceipts = state.showReadReceipts, highlightedItem = state.highlightedEventId?.value, userHasPermissionToSendMessage = state.userHasPermissionToSendMessage, onClick = onMessageClicked, @@ -171,6 +172,7 @@ fun TimelineView( @Composable private fun TimelineItemRow( timelineItem: TimelineItem, + showReadReceipts: Boolean, highlightedItem: String?, userHasPermissionToSendMessage: Boolean, sessionState: SessionState, @@ -208,6 +210,7 @@ private fun TimelineItemRow( } else { TimelineItemEventRow( event = timelineItem, + showReadReceipts = showReadReceipts, isHighlighted = highlightedItem == timelineItem.identifier(), canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(), onClick = { onClick(timelineItem) }, @@ -248,6 +251,7 @@ private fun TimelineItemRow( timelineItem.events.forEach { subGroupEvent -> TimelineItemRow( timelineItem = subGroupEvent, + showReadReceipts = showReadReceipts, highlightedItem = highlightedItem, sessionState = sessionState, userHasPermissionToSendMessage = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index ec277fb96b..5c816bef7b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -79,7 +79,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent -import io.element.android.features.messages.impl.timeline.model.receipts import io.element.android.libraries.designsystem.colors.AvatarColorsProvider import io.element.android.libraries.designsystem.components.EqualWidthColumn import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -117,6 +116,7 @@ import kotlin.math.roundToInt @Composable fun TimelineItemEventRow( event: TimelineItem.Event, + showReadReceipts: Boolean, isHighlighted: Boolean, canReply: Boolean, onClick: () -> Unit, @@ -177,6 +177,7 @@ fun TimelineItemEventRow( state = state.draggableState, ), event = event, + showReadReceipts = showReadReceipts, isHighlighted = isHighlighted, interactionSource = interactionSource, onClick = onClick, @@ -195,6 +196,7 @@ fun TimelineItemEventRow( } else { TimelineItemEventRowContent( event = event, + showReadReceipts = showReadReceipts, isHighlighted = isHighlighted, interactionSource = interactionSource, onClick = onClick, @@ -238,6 +240,7 @@ private fun SwipeSensitivity( @Composable private fun TimelineItemEventRowContent( event: TimelineItem.Event, + showReadReceipts: Boolean, isHighlighted: Boolean, interactionSource: MutableInteractionSource, onClick: () -> Unit, @@ -336,21 +339,23 @@ private fun TimelineItemEventRowContent( } // Read receipts / Send state - TimelineItemReadReceiptView( - state = ReadReceiptViewState( - sendState = event.localSendState, - receipts = event.readReceiptState.receipts(), - ), - onReadReceiptsClicked = onReadReceiptsClicked, - modifier = Modifier - .constrainAs(readReceipts) { - if (event.reactionsState.reactions.isNotEmpty()) { - top.linkTo(reactions.bottom, margin = 4.dp) - } else { - top.linkTo(message.bottom, margin = 4.dp) + if (showReadReceipts) { + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + receipts = event.readReceiptState.receipts, + ), + onReadReceiptsClicked = onReadReceiptsClicked, + modifier = Modifier + .constrainAs(readReceipts) { + if (event.reactionsState.reactions.isNotEmpty()) { + top.linkTo(reactions.bottom, margin = 4.dp) + } else { + top.linkTo(message.bottom, margin = 4.dp) + } } - } - ) + ) + } } } @@ -679,6 +684,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { ), groupPosition = TimelineItemGroupPosition.First, ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -701,6 +707,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { ), groupPosition = TimelineItemGroupPosition.Last, ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -741,6 +748,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { inReplyTo = aInReplyToReady(replyContent), groupPosition = TimelineItemGroupPosition.First, ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -765,6 +773,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { isThreaded = true, groupPosition = TimelineItemGroupPosition.Last, ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -817,6 +826,7 @@ internal fun TimelineItemEventRowTimestampPreview( reactionsState = aTimelineItemReactions(count = 0), senderDisplayName = if (useDocument) "Document case" else "Text case", ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -850,6 +860,7 @@ internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { ), timelineItemReactions = aTimelineItemReactions(count = 20), ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -876,6 +887,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { event = aTimelineItemEvent( senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", ), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, @@ -898,6 +910,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { TimelineItemEventRow( event = aTimelineItemEvent(content = aTimelineItemPollContent()), + showReadReceipts = false, isHighlighted = false, canReply = true, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt index d17debe7da..391ec3de7f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheet.kt @@ -29,7 +29,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import io.element.android.features.messages.impl.timeline.model.receipts import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight @@ -93,7 +92,7 @@ private fun ColumnScope.ReadReceiptBottomSheetContent( Text(text = stringResource(id = CommonStrings.common_seen_by)) } ) - val receipts = state.selectedEvent?.readReceiptState?.receipts().orEmpty() + val receipts = state.selectedEvent?.readReceiptState?.receipts.orEmpty() receipts.forEach { val userId = UserId(it.avatarData.id) MatrixUserRow( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt index 9b51dac76e..3a3bf1dbc4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/bottomsheet/ReadReceiptBottomSheetStateProvider.kt @@ -31,7 +31,7 @@ class ReadReceiptBottomSheetStateProvider : PreviewParameterProvider ReadReceiptBottomSheetState( selectedEvent = aTimelineItemEvent( - readReceiptState = TimelineItemReadReceipts.ReadReceipts( + readReceiptState = TimelineItemReadReceipts( receipts = readReceiptViewState.receipts.map { readReceiptData -> readReceiptData .copy(avatarData = readReceiptData.avatarData.copy(id = "@${readReceiptData.avatarData.id}:localhost")) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index e48720f92a..ffc1a1b3f1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -67,7 +67,7 @@ class TimelineItemsFactory @Inject constructor( suspend fun replaceWith( timelineItems: List, - roomMembers: List?, + roomMembers: List, ) = withContext(dispatchers.computation) { lock.withLock { diffCacheUpdater.updateWith(timelineItems) @@ -77,7 +77,7 @@ class TimelineItemsFactory @Inject constructor( private suspend fun buildAndEmitTimelineItemStates( timelineItems: List, - roomMembers: List?, + roomMembers: List, ) { val newTimelineItemStates = ArrayList() for (index in diffCache.indices().reversed()) { @@ -97,7 +97,7 @@ class TimelineItemsFactory @Inject constructor( private suspend fun buildAndCacheItem( timelineItems: List, index: Int, - roomMembers: List?, + roomMembers: List, ): TimelineItem? { val timelineItemState = when (val currentTimelineItem = timelineItems[index]) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index cde3f32d95..4c9217a884 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -47,7 +47,7 @@ class TimelineItemEventFactory @Inject constructor( currentTimelineItem: MatrixTimelineItem.Event, index: Int, timelineItems: List, - roomMembers: List?, + roomMembers: List, ): TimelineItem.Event { val currentSender = currentTimelineItem.event.sender val groupPosition = @@ -132,10 +132,9 @@ class TimelineItemEventFactory @Inject constructor( } private fun MatrixTimelineItem.Event.computeReadReceiptState( - roomMembers: List?, + roomMembers: List, ): TimelineItemReadReceipts { - if (roomMembers == null) return TimelineItemReadReceipts.Hidden - return TimelineItemReadReceipts.ReadReceipts( + return TimelineItemReadReceipts( receipts = event.receipts .map { receipt -> val roomMember = roomMembers.find { it.userId == receipt.userId } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt index d9fc6c55af..dc5ae8289c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemReadReceipts.kt @@ -18,25 +18,12 @@ package io.element.android.features.messages.impl.timeline.model import io.element.android.libraries.designsystem.components.avatar.AvatarData import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -sealed interface TimelineItemReadReceipts { - /** - * Value when the feature is disabled. - */ - data object Hidden : TimelineItemReadReceipts - - data class ReadReceipts( - val receipts: ImmutableList, - ) : TimelineItemReadReceipts -} +data class TimelineItemReadReceipts( + val receipts: ImmutableList, +) data class ReadReceiptData( val avatarData: AvatarData, val formattedDate: String, ) - -fun TimelineItemReadReceipts.receipts(): ImmutableList = when (this) { - TimelineItemReadReceipts.Hidden -> persistentListOf() - is TimelineItemReadReceipts.ReadReceipts -> receipts -} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt index 91e1c2454e..68c80d6b80 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/aMessageEvent.kt @@ -53,7 +53,7 @@ internal fun aMessageEvent( sentTime = "", isMine = isMine, reactionsState = aTimelineItemReactions(count = 0), - readReceiptState = TimelineItemReadReceipts.ReadReceipts(emptyList().toImmutableList()), + readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), localSendState = sendState, inReplyTo = inReplyTo, debugInfo = debugInfo, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt index 14bd7e00e1..e8a8eb30cf 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt @@ -44,7 +44,7 @@ class TimelineItemGrouperTest { senderDisplayName = "", content = TimelineItemStateEventContent(body = "a state event"), reactionsState = aTimelineItemReactions(count = 0), - readReceiptState = TimelineItemReadReceipts.ReadReceipts(emptyList().toImmutableList()), + readReceiptState = TimelineItemReadReceipts(emptyList().toImmutableList()), localSendState = LocalEventSendState.Sent(AN_EVENT_ID), inReplyTo = null, isThreaded = false, From 2f3333af6913696f23ca4168f8564ec66383b331 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 15:46:33 +0100 Subject: [PATCH 074/102] Remove hard-coded value. --- .../timeline/components/receipt/TimelineItemReadReceiptView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index 810dc31541..3f35a10392 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -175,7 +175,7 @@ private fun ReadReceiptsAvatars( } } } - if (receipts.size > 3) { + if (receipts.size > TimelineConfig.maxReadReceiptToDisplay) { Text( text = "+" + (receipts.size - TimelineConfig.maxReadReceiptToDisplay), style = ElementTheme.typography.fontBodyXsRegular, From 51654e4b96a4d58c43d02d4c145eadb304173684 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 16:32:00 +0100 Subject: [PATCH 075/102] Send state: show if null and is last message. Read receipt: Also show the send state even if the flag for read receipt is set to false. --- .../messages/impl/timeline/TimelineView.kt | 5 +++ .../components/TimelineItemEventRow.kt | 44 ++++++++++++------- .../receipt/ReadReceiptViewState.kt | 1 + .../receipt/ReadReceiptViewStateProvider.kt | 2 + .../receipt/TimelineItemReadReceiptView.kt | 44 ++++++++----------- 5 files changed, 54 insertions(+), 42 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 7e4030a7c3..1d831e5e21 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -128,6 +128,8 @@ fun TimelineView( TimelineItemRow( timelineItem = timelineItem, showReadReceipts = state.showReadReceipts, + isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true + && state.timelineItems.first().identifier() == timelineItem.identifier(), highlightedItem = state.highlightedEventId?.value, userHasPermissionToSendMessage = state.userHasPermissionToSendMessage, onClick = onMessageClicked, @@ -173,6 +175,7 @@ fun TimelineView( private fun TimelineItemRow( timelineItem: TimelineItem, showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, highlightedItem: String?, userHasPermissionToSendMessage: Boolean, sessionState: SessionState, @@ -211,6 +214,7 @@ private fun TimelineItemRow( TimelineItemEventRow( event = timelineItem, showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = highlightedItem == timelineItem.identifier(), canReply = userHasPermissionToSendMessage && timelineItem.content.canBeRepliedTo(), onClick = { onClick(timelineItem) }, @@ -252,6 +256,7 @@ private fun TimelineItemRow( TimelineItemRow( timelineItem = subGroupEvent, showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, highlightedItem = highlightedItem, sessionState = sessionState, userHasPermissionToSendMessage = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 5c816bef7b..839501e429 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -117,6 +117,7 @@ import kotlin.math.roundToInt fun TimelineItemEventRow( event: TimelineItem.Event, showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, isHighlighted: Boolean, canReply: Boolean, onClick: () -> Unit, @@ -178,6 +179,7 @@ fun TimelineItemEventRow( ), event = event, showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = isHighlighted, interactionSource = interactionSource, onClick = onClick, @@ -197,6 +199,7 @@ fun TimelineItemEventRow( TimelineItemEventRowContent( event = event, showReadReceipts = showReadReceipts, + isLastOutgoingMessage = isLastOutgoingMessage, isHighlighted = isHighlighted, interactionSource = interactionSource, onClick = onClick, @@ -241,6 +244,7 @@ private fun SwipeSensitivity( private fun TimelineItemEventRowContent( event: TimelineItem.Event, showReadReceipts: Boolean, + isLastOutgoingMessage: Boolean, isHighlighted: Boolean, interactionSource: MutableInteractionSource, onClick: () -> Unit, @@ -339,23 +343,23 @@ private fun TimelineItemEventRowContent( } // Read receipts / Send state - if (showReadReceipts) { - TimelineItemReadReceiptView( - state = ReadReceiptViewState( - sendState = event.localSendState, - receipts = event.readReceiptState.receipts, - ), - onReadReceiptsClicked = onReadReceiptsClicked, - modifier = Modifier - .constrainAs(readReceipts) { - if (event.reactionsState.reactions.isNotEmpty()) { - top.linkTo(reactions.bottom, margin = 4.dp) - } else { - top.linkTo(message.bottom, margin = 4.dp) - } + TimelineItemReadReceiptView( + state = ReadReceiptViewState( + sendState = event.localSendState, + isLastOutgoingMessage = isLastOutgoingMessage, + receipts = event.readReceiptState.receipts, + ), + showReadReceipts = showReadReceipts, + onReadReceiptsClicked = onReadReceiptsClicked, + modifier = Modifier + .constrainAs(readReceipts) { + if (event.reactionsState.reactions.isNotEmpty()) { + top.linkTo(reactions.bottom, margin = 4.dp) + } else { + top.linkTo(message.bottom, margin = 4.dp) } - ) - } + } + ) } } @@ -685,6 +689,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { groupPosition = TimelineItemGroupPosition.First, ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -708,6 +713,7 @@ internal fun TimelineItemEventRowPreview() = ElementPreview { groupPosition = TimelineItemGroupPosition.Last, ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -749,6 +755,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { groupPosition = TimelineItemGroupPosition.First, ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -774,6 +781,7 @@ internal fun TimelineItemEventRowWithReplyPreview() = ElementPreview { groupPosition = TimelineItemGroupPosition.Last, ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -827,6 +835,7 @@ internal fun TimelineItemEventRowTimestampPreview( senderDisplayName = if (useDocument) "Document case" else "Text case", ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -861,6 +870,7 @@ internal fun TimelineItemEventRowWithManyReactionsPreview() = ElementPreview { timelineItemReactions = aTimelineItemReactions(count = 20), ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -888,6 +898,7 @@ internal fun TimelineItemEventRowLongSenderNamePreview() = ElementPreviewLight { senderDisplayName = "a long sender display name to test single line and ellipsis at the end of the line", ), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, @@ -911,6 +922,7 @@ internal fun TimelineItemEventTimestampBelowPreview() = ElementPreviewLight { TimelineItemEventRow( event = aTimelineItemEvent(content = aTimelineItemPollContent()), showReadReceipts = false, + isLastOutgoingMessage = false, isHighlighted = false, canReply = true, onClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt index 2bf1d524ae..a15ecc781b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewState.kt @@ -22,5 +22,6 @@ import kotlinx.collections.immutable.ImmutableList data class ReadReceiptViewState( val sendState: LocalEventSendState?, + val isLastOutgoingMessage: Boolean, val receipts: ImmutableList, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt index 06520b1808..e0f72b010d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/ReadReceiptViewStateProvider.kt @@ -56,9 +56,11 @@ class ReadReceiptViewStateProvider : PreviewParameterProvider = emptyList(), ) = ReadReceiptViewState( sendState = sendState, + isLastOutgoingMessage = isLastOutgoingMessage, receipts = receipts.toImmutableList(), ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index 3f35a10392..8725b365d0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -58,11 +58,23 @@ import kotlinx.collections.immutable.ImmutableList @Composable fun TimelineItemReadReceiptView( state: ReadReceiptViewState, + showReadReceipts: Boolean, onReadReceiptsClicked: () -> Unit, modifier: Modifier = Modifier, ) { - when (state.sendState) { - LocalEventSendState.Canceled -> Unit + if (state.receipts.isNotEmpty()) { + if (showReadReceipts) { + ReadReceiptsRow(modifier = modifier) { + ReadReceiptsAvatars( + receipts = state.receipts, + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .clickable { onReadReceiptsClicked() } + .padding(2.dp) + ) + } + } + } else when (state.sendState) { LocalEventSendState.NotSentYet -> { ReadReceiptsRow(modifier) { Icon( @@ -73,11 +85,13 @@ fun TimelineItemReadReceiptView( ) } } + LocalEventSendState.Canceled -> Unit is LocalEventSendState.SendingFailed -> { // Error? The timestamp is already displayed in red } + null, is LocalEventSendState.Sent -> { - if (state.receipts.isEmpty()) { + if (state.isLastOutgoingMessage) { ReadReceiptsRow(modifier = modifier) { Icon( modifier = Modifier.padding(2.dp), @@ -86,29 +100,6 @@ fun TimelineItemReadReceiptView( tint = ElementTheme.colors.iconSecondary ) } - } else { - ReadReceiptsRow(modifier = modifier) { - ReadReceiptsAvatars( - receipts = state.receipts, - modifier = Modifier - .clip(RoundedCornerShape(4.dp)) - .clickable { onReadReceiptsClicked() } - .padding(2.dp) - ) - } - } - } - null -> { - if (state.receipts.isNotEmpty()) { - ReadReceiptsRow(modifier = modifier) { - ReadReceiptsAvatars( - receipts = state.receipts, - modifier = Modifier - .clip(RoundedCornerShape(4.dp)) - .clickable { onReadReceiptsClicked() } - .padding(2.dp) - ) - } } } } @@ -214,6 +205,7 @@ internal fun TimelineItemReactionsViewPreview( ) = ElementPreview { TimelineItemReadReceiptView( state = state, + showReadReceipts = true, onReadReceiptsClicked = {}, ) } From 7eee8fab60dfd4b465d1eedb0c3214a3be171a4c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Fri, 17 Nov 2023 15:49:52 +0000 Subject: [PATCH 076/102] Update screenshots --- ...eadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png | 3 +++ ...eadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png | 3 +++ ...eadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png | 3 +++ ...eadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png | 3 +++ ...eadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png | 3 +++ ...eadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png | 3 +++ ...dReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png | 3 +++ ...lineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png | 3 +++ ...neItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png | 3 +++ ...RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png} | 0 ...RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png} | 0 ...trySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png} | 0 ...trySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png} | 0 ...tedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png} | 0 ...tedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png} | 0 ...tedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png} | 0 ...dHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png} | 0 ...dHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png} | 0 ...dHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png} | 0 ...ItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png} | 0 ...ItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png} | 0 ...emDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png} | 0 ...emDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png} | 0 ...lineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png} | 0 ...neItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png} | 0 ...eItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png} | 0 ...temRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png} | 0 ...neLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png} | 0 ...LoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png} | 0 ...ull_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png} | 0 ...l_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png} | 0 ...r_Avatar_null_Avatars_Avatar_0_null_33,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_34,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_35,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_36,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_37,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_38,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_39,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_40,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_41,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_42,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_43,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_44,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_45,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_46,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_47,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_48,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_49,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_50,NEXUS_5,1.0,en].png | 4 ++-- ...r_Avatar_null_Avatars_Avatar_0_null_51,NEXUS_5,1.0,en].png | 3 +++ ...r_Avatar_null_Avatars_Avatar_0_null_52,NEXUS_5,1.0,en].png | 3 +++ ...r_Avatar_null_Avatars_Avatar_0_null_53,NEXUS_5,1.0,en].png | 3 +++ ...r_Avatar_null_Avatars_Avatar_0_null_54,NEXUS_5,1.0,en].png | 3 +++ ...r_Avatar_null_Avatars_Avatar_0_null_55,NEXUS_5,1.0,en].png | 3 +++ ...r_Avatar_null_Avatars_Avatar_0_null_56,NEXUS_5,1.0,en].png | 3 +++ 74 files changed, 138 insertions(+), 36 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png => ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png} (100%) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_51,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_52,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_53,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_54,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_55,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_56,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4b829f5c0e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2d69362d225e97cf9ca07e071de06c89d8ca4694214dfebde90b771a2c11f54 +size 6707 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..aeaf2f7dd7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5af53d9bbf0fb985efeb48b3e8f1c808e10fff496d6aafc068e7e50cfafcd798 +size 12367 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6ddbf182b0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9779c9c980c32b3e8ede34e5f9e7beeca6e0000fc77e1fc1cf0a8c0eb1da0be7 +size 17760 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d8023b8b76 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbcfa1b91e13109969c31b6e2d09c5099a1c27ed15912401db7ac946323a1027 +size 22729 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ac6c8fa54 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97fd46b3b79c90a4104c2fd4e9f5667abf67d998d9f7136b9c18da920af4cb99 +size 27836 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8881b0cfed --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Day-48_48_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e767451a900ff3629047e9931784c1b5edd3a52e44dd4d172107c71fe92671d3 +size 33561 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..366399824d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63620fd5bfea92c632a0a4b1d88eabd51a973e7e4281af2b662b8955cc209e0a +size 6549 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..65bb8111f9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a8a1900a56b87bb96135cbbfe92e504b486775b5b0ba02bb6626a351081ef37 +size 12272 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..13a2cf3633 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63e229114afc8317c1690f817e4ba4a275c581c9f107c75e88c99728c6698219 +size 17755 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cd31a0814a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:175c2bed417fdfd3c80b8ce3079675decc3a27cd8df6d06c00db0015d193cb4e +size 22808 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4b7eec1e1a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb15b22c4fc99d3f7c655e85d1e2c1b9d0ae4478908a4f40234a8a4651df7d49 +size 28080 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7372f4b2a7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_null_ReadReceiptBottomSheet-Night-48_49_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21078a2cfa738bc3a2a7f36c52ea7c6ff9c83a9885f6059fe472121f08a08c05 +size 33681 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..601cda52a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec996e41aaecde031e8b4568f9dbe08b359b4390589b7e25c8da084a0fa9fc93 +size 5502 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e13c280efc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:379c809e565791ae199367bf9e7fe5d2e7767bbd5f33f51309d0caacc976dd5f +size 5356 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..601cda52a2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec996e41aaecde031e8b4568f9dbe08b359b4390589b7e25c8da084a0fa9fc93 +size 5502 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..54d5ed96d5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61d4e36b624371171b6ee1909318dcc2ba96c55b42a92fb880b25abbbff48645 +size 5080 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c9e7c2f15d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:821606acb7dcd9bdcad17bff0c33268f2ab22569e846e610c4401d852e17be2e +size 5628 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..173509112b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3552965164107b4a6881a5834bef98dfba2494c7c2186f6a6b8315a64c0c0d6b +size 6233 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..523cbf9120 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fedc10863be4b1777614ecf777ee24b43a4d9fb48059833cd08a122e6cec221 +size 6410 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2de7d24afd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Day-47_47_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b980fa478c1d508284323e094281db096a7b6360339493c59b0c1fd69c2288c3 +size 6609 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c7bd8a176b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3663e96a724a0a75e693c4c516e6adedc4c20967604c6931142972fcee8fe488 +size 5475 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..86623d35e8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fec37a2d969d74fba0c26a0ff179f204c52c8e3d0c4a2b2d189ba071e4428887 +size 5339 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c7bd8a176b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3663e96a724a0a75e693c4c516e6adedc4c20967604c6931142972fcee8fe488 +size 5475 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ad0545303d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d33ed61dd503fa4b3956e27cc7a06090c6eb589cf5dbdfd0dd781dfd335b774d +size 5213 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7bb404a775 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d29f6726900317e6869ae00759b9a861a1036a1a8280621bc1ed47c37752a18 +size 5901 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7c235a780c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e69990e066a3eb36e66820e2414397fdb415151ba76d9c062588e58e01e6e97 +size 6425 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..234380bde4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3cfd9aaf78a8e66993d269e3d5b49966eb067f2df1dc2582ba066d5e6a086b0 +size 6608 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8d0f250d51 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.receipt_TimelineItemReactionsView_null_TimelineItemReactionsView-Night-47_48_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91961cd20e51f2065a48720fe3a5067d3f640a7e87d5f369eb45842ba1f256f7 +size 6811 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-47_47_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Day-49_49_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-47_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.retrysendmenu_RetrySendMessageMenu_null_RetrySendMessageMenu-Night-49_50_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-48_48_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Day-50_50_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-48_49_null_2,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_EncryptedHistoryBannerView_null_EncryptedHistoryBannerView-Night-50_51_null_2,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-49_49_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Day-51_51_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_0,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_0,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-49_50_null_1,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_null_TimelineItemDaySeparatorView-Night-51_52_null_1,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-50_50_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Day-52_52_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-50_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_null_TimelineItemReadMarkerView-Night-52_53_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-51_51_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Day-53_53_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-51_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_null_TimelineItemRoomBeginningView-Night-53_54_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-52_52_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Day-54_54_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-52_53_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_null_TimelineLoadingMoreIndicator-Night-54_55_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-53_53_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Day-55_55_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-53_54_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/ui_S_t[f.messages.impl.timeline.debug_EventDebugInfoView_null_EventDebugInfoView-Night-55_56_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_33,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_33,NEXUS_5,1.0,en].png index df93febb6f..608ac4b27d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_33,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_33,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:748a2562d8c3e9b5e4def3b8ea3c782868e56f449de1b1ddb4bc36cd84945318 -size 20642 +oid sha256:a5953d21cc18cb10614361f7343fed9375ca851677b4a380e97e86851a082c3b +size 17118 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_34,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_34,NEXUS_5,1.0,en].png index f97e401957..70d8326e1c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_34,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_34,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a6ce0c3b6fe3ac503ee7019dee234a9f96552e1106fe6858030a7c70c506d4f -size 19869 +oid sha256:d8134edc79b20771f89e8b15eed3b3660e46ab6f3805130252f32d2fb6b26cb6 +size 16745 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_35,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_35,NEXUS_5,1.0,en].png index 4e2e281f59..7814f8545b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_35,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_35,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5af89d5988d168ee2cbd30e3ba6447f5cd1399d35a2ca04c7348e74cb623f37 -size 22501 +oid sha256:9e6022f750c6e26d822d4ffd1172cd8c34f7afe0188cce2da9f72d3dd8e4f45a +size 18030 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_36,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_36,NEXUS_5,1.0,en].png index d21aa10955..174d9c208d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_36,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_36,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c49437c9f943cf044ec64a0ecab2b70d1fa811a34aff4baa8ed0ed846a4f5c5 -size 18595 +oid sha256:d5901d9bf468a0ca5fc7c3d6d308898b802b57967afe4d61c814f86e2194ee16 +size 17829 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_37,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_37,NEXUS_5,1.0,en].png index 726d7087b0..a0c98f1f74 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_37,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_37,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:063cdf0bbdfc846d3040af143f79a0b6eb325beab62cba5657236d028db6bc26 -size 17267 +oid sha256:6aa7aa8f18b1a7d3a68c65e5275a69d49138e51046d913686f111f809cb2235f +size 17054 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_38,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_38,NEXUS_5,1.0,en].png index 1f2604180e..085f22c2d0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_38,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_38,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8118e782f4ec7b67b6aa148f1bb31c9d30f03f72a7abfc73dc8d5094a5a7545c -size 21791 +oid sha256:e0dd5b1c404dd33a70083e7d775bf4c89e8eb1823e72711e8aa6c4947fb49b6e +size 19756 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_39,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_39,NEXUS_5,1.0,en].png index 38c479cfdc..df93febb6f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_39,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_39,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:032ed67865d3ad800371e193ea922589f27cf03697e3c91c8d698957befa8d42 -size 14590 +oid sha256:748a2562d8c3e9b5e4def3b8ea3c782868e56f449de1b1ddb4bc36cd84945318 +size 20642 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_40,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_40,NEXUS_5,1.0,en].png index 8152088666..f97e401957 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_40,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_40,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54b00e4382ea77eb90e6de983ec1b363b504456cebd1754946759fd2ee1e9b85 -size 14232 +oid sha256:9a6ce0c3b6fe3ac503ee7019dee234a9f96552e1106fe6858030a7c70c506d4f +size 19869 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_41,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_41,NEXUS_5,1.0,en].png index d8b2b5928a..4e2e281f59 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_41,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_41,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8b9cdb7894f9fe410183d73a2febebe576de6967ad3f2183cfda504f081c567b -size 15475 +oid sha256:b5af89d5988d168ee2cbd30e3ba6447f5cd1399d35a2ca04c7348e74cb623f37 +size 22501 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_42,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_42,NEXUS_5,1.0,en].png index cb0e6b6fc9..d21aa10955 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_42,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_42,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eac20dcc3e285cde5f3d2515d65416162362de196940b3c208886934592070e4 -size 21346 +oid sha256:0c49437c9f943cf044ec64a0ecab2b70d1fa811a34aff4baa8ed0ed846a4f5c5 +size 18595 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_43,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_43,NEXUS_5,1.0,en].png index f939fb7b9e..726d7087b0 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_43,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_43,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:96d3a1b71b8372ca80b667ae57bf9f87b5e8167684ea62c8f71d90994113929f -size 19530 +oid sha256:063cdf0bbdfc846d3040af143f79a0b6eb325beab62cba5657236d028db6bc26 +size 17267 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_44,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_44,NEXUS_5,1.0,en].png index f64b9f6a2d..1f2604180e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_44,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_44,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f3a728b5791495710209e5b308dda4b38defded1a44d00bbd69fb1c45877218 -size 25254 +oid sha256:8118e782f4ec7b67b6aa148f1bb31c9d30f03f72a7abfc73dc8d5094a5a7545c +size 21791 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_45,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_45,NEXUS_5,1.0,en].png index d69da80047..38c479cfdc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_45,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_45,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:820b23e9b9175b48461a4e7e359c2440b8c45cb598f512ea360519b0815621fc -size 18259 +oid sha256:032ed67865d3ad800371e193ea922589f27cf03697e3c91c8d698957befa8d42 +size 14590 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_46,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_46,NEXUS_5,1.0,en].png index 4cf9f8f839..8152088666 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_46,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_46,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ce4abbf622711cf6a106cb6c74ed729c6e609d329579b7b00ba8c1581b4b953 -size 17463 +oid sha256:54b00e4382ea77eb90e6de983ec1b363b504456cebd1754946759fd2ee1e9b85 +size 14232 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_47,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_47,NEXUS_5,1.0,en].png index a475b2e7d1..d8b2b5928a 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_47,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_47,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0cb196c59e3bbd845f0e110150ac358ca78c9c08c212e184423ead5d841f67c -size 20219 +oid sha256:8b9cdb7894f9fe410183d73a2febebe576de6967ad3f2183cfda504f081c567b +size 15475 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_48,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_48,NEXUS_5,1.0,en].png index a3d0076294..cb0e6b6fc9 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_48,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_48,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2619a9a70eca12d5931ebc21adce48a1aed8383f35908bb09546f44b40f04543 -size 23094 +oid sha256:eac20dcc3e285cde5f3d2515d65416162362de196940b3c208886934592070e4 +size 21346 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_49,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_49,NEXUS_5,1.0,en].png index 81b9668b0f..f939fb7b9e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_49,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_49,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d7d71d58b250bdec2d9b6e1ed46c5e3ffd98ffeefcaf4267b9979970a89750b -size 22226 +oid sha256:96d3a1b71b8372ca80b667ae57bf9f87b5e8167684ea62c8f71d90994113929f +size 19530 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_50,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_50,NEXUS_5,1.0,en].png index 5cb8c0a0bf..f64b9f6a2d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_50,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_50,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e7bdea3caef3f1be9fac1fbb5511fe2d76b7985576c038f8c1d920615c3d49cd -size 25005 +oid sha256:1f3a728b5791495710209e5b308dda4b38defded1a44d00bbd69fb1c45877218 +size 25254 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_51,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_51,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d69da80047 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_51,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:820b23e9b9175b48461a4e7e359c2440b8c45cb598f512ea360519b0815621fc +size 18259 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_52,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_52,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4cf9f8f839 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_52,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ce4abbf622711cf6a106cb6c74ed729c6e609d329579b7b00ba8c1581b4b953 +size 17463 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_53,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_53,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a475b2e7d1 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_53,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0cb196c59e3bbd845f0e110150ac358ca78c9c08c212e184423ead5d841f67c +size 20219 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_54,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_54,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a3d0076294 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_54,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2619a9a70eca12d5931ebc21adce48a1aed8383f35908bb09546f44b40f04543 +size 23094 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_55,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_55,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..81b9668b0f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_55,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d7d71d58b250bdec2d9b6e1ed46c5e3ffd98ffeefcaf4267b9979970a89750b +size 22226 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_56,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_56,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5cb8c0a0bf --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.components.avatar_Avatar_null_Avatars_Avatar_0_null_56,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7bdea3caef3f1be9fac1fbb5511fe2d76b7985576c038f8c1d920615c3d49cd +size 25005 From d352f146650c6f9c2ace53ffbdd23891b68261ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 17 Nov 2023 17:31:22 +0100 Subject: [PATCH 077/102] BackupState.DISABLED has been removed. Now when the value is UNKNOWN, the app need to invoke `EncryptionService.doesBackupExistOnServer()` to check if a Backup exists. --- .../impl/root/SecureBackupRootPresenter.kt | 17 +++++++++++ .../impl/root/SecureBackupRootState.kt | 1 + .../root/SecureBackupRootStateProvider.kt | 7 +++-- .../impl/root/SecureBackupRootView.kt | 28 ++++++++++++++----- .../matrix/api/encryption/BackupState.kt | 3 +- .../api/encryption/EncryptionService.kt | 2 ++ .../impl/encryption/BackupStateMapper.kt | 1 - .../impl/encryption/RustEncryptionService.kt | 6 ++++ .../test/encryption/FakeEncryptionService.kt | 4 +++ 9 files changed, 57 insertions(+), 12 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt index 8a8fa6075c..4ed898ff9a 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt @@ -17,13 +17,18 @@ package io.element.android.features.securebackup.impl.root import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import io.element.android.features.securebackup.impl.loggerTagRoot import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState +import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService import timber.log.Timber import javax.inject.Inject @@ -44,8 +49,20 @@ class SecureBackupRootPresenter @Inject constructor( Timber.tag(loggerTagRoot.value).d("backupState: $backupState") Timber.tag(loggerTagRoot.value).d("recoveryState: $recoveryState") + var doesBackupExistOnServer: Boolean? by remember { mutableStateOf(null) } + + LaunchedEffect(backupState) { + doesBackupExistOnServer = if (backupState == BackupState.UNKNOWN) { + encryptionService.doesBackupExistOnServer().getOrNull() == true + } else { + // The value will not be used when the backupState is not UNKNOWN. + false + } + } + return SecureBackupRootState( backupState = backupState, + doesBackupExistOnServer = doesBackupExistOnServer, recoveryState = recoveryState, appName = buildMeta.applicationName, snackbarMessage = snackbarMessage, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt index 1eacd9e81a..a6dadf83c9 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState data class SecureBackupRootState( val backupState: BackupState, + val doesBackupExistOnServer: Boolean?, val recoveryState: RecoveryState, val appName: String, val snackbarMessage: SnackbarMessage?, diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt index ee15a40f50..efe59f23b3 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt @@ -24,9 +24,10 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState open class SecureBackupRootStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aSecureBackupRootState(backupState = BackupState.UNKNOWN), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = null), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = true), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = false), aSecureBackupRootState(backupState = BackupState.ENABLED), - aSecureBackupRootState(backupState = BackupState.DISABLED), aSecureBackupRootState(recoveryState = RecoveryState.UNKNOWN), aSecureBackupRootState(recoveryState = RecoveryState.ENABLED), aSecureBackupRootState(recoveryState = RecoveryState.DISABLED), @@ -37,10 +38,12 @@ open class SecureBackupRootStateProvider : PreviewParameterProvider Unit - BackupState.DISABLED -> { - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), - onClick = onEnableClicked, - ) + BackupState.WAITING_FOR_SYNC -> Unit + BackupState.UNKNOWN -> { + when (state.doesBackupExistOnServer) { + true -> { + // Should not happen, we will have the state BackupState.ENABLED + PreferenceText( + title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), + tintColor = ElementTheme.colors.textCriticalPrimary, + onClick = onDisableClicked, + ) + } + false -> { + PreferenceText( + title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), + onClick = onEnableClicked, + ) + } + null -> { + AsyncLoading() + } + } } BackupState.CREATING, BackupState.ENABLING, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupState.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupState.kt index 98c45a1c9e..03f650e316 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupState.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/BackupState.kt @@ -31,6 +31,5 @@ enum class BackupState { RESUMING, ENABLED, DOWNLOADING, - DISABLING, - DISABLED; + DISABLING; } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt index 9652c53678..534f5b68e2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/encryption/EncryptionService.kt @@ -40,6 +40,8 @@ interface EncryptionService { suspend fun disableRecovery(): Result + suspend fun doesBackupExistOnServer(): Result + /** * Note: accept bot recoveryKey and passphrase. */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupStateMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupStateMapper.kt index dbdd48da6c..ec7bc8e4d9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupStateMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/BackupStateMapper.kt @@ -29,7 +29,6 @@ class BackupStateMapper { RustBackupState.ENABLED -> BackupState.ENABLED RustBackupState.DOWNLOADING -> BackupState.DOWNLOADING RustBackupState.DISABLING -> BackupState.DISABLING - RustBackupState.DISABLED -> BackupState.DISABLED } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt index 11f9842d62..30b8c651a0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/encryption/RustEncryptionService.kt @@ -130,6 +130,12 @@ internal class RustEncryptionService( } } + override suspend fun doesBackupExistOnServer(): Result = withContext(dispatchers.io) { + runCatching { + service.backupExistsOnServer() + } + } + override fun waitForBackupUploadSteadyState(): Flow { return callbackFlow { runCatching { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index 825d929c0e..4d7cbf5c09 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -52,6 +52,10 @@ class FakeEncryptionService : EncryptionService { return Result.success(Unit) } + override suspend fun doesBackupExistOnServer(): Result = simulateLongTask { + return Result.success(true) + } + override suspend fun fixRecoveryIssues(recoveryKey: String): Result = simulateLongTask { fixRecoveryIssuesFailure?.let { return Result.failure(it) } return Result.success(Unit) From a0f289592effbff07907e0ab1fff9cecf6a68219 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 17 Nov 2023 17:53:45 +0100 Subject: [PATCH 078/102] Timeline : do not use SubcomposeLayout if not needed --- .../components/TimelineItemEventRow.kt | 106 ++++++++++-------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index cfb81f71a7..d4fccc296b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -383,22 +383,6 @@ private fun MessageEventBubbleContent( // to its `combinedClickable` parent so we do it manually fun onTimestampLongClick() = onMessageLongClick() - @Composable - fun ContentView( - modifier: Modifier = Modifier - ) { - TimelineItemEventContentView( - content = event.content, - isMine = event.isMine, - interactionSource = interactionSource, - onClick = onMessageClick, - onLongClick = onMessageLongClick, - extraPadding = event.toExtraPadding(), - eventSink = eventSink, - modifier = modifier, - ) - } - @Composable fun ThreadDecoration( modifier: Modifier = Modifier @@ -422,21 +406,20 @@ private fun MessageEventBubbleContent( } @Composable - fun ContentAndTimestampView( + fun WithTimestampLayout( timestampPosition: TimestampPosition, modifier: Modifier = Modifier, - contentModifier: Modifier = Modifier, - timestampModifier: Modifier = Modifier, + content: @Composable () -> Unit, ) { when (timestampPosition) { TimestampPosition.Overlay -> Box(modifier) { - ContentView(modifier = contentModifier) + content() TimelineEventTimestampView( event = event, onClick = onTimestampClicked, onLongClick = ::onTimestampLongClick, - modifier = timestampModifier + modifier = Modifier .padding(horizontal = 4.dp, vertical = 4.dp) // Outer padding .background(ElementTheme.colors.bgSubtleSecondary, RoundedCornerShape(10.0.dp)) .align(Alignment.BottomEnd) @@ -445,24 +428,24 @@ private fun MessageEventBubbleContent( } TimestampPosition.Aligned -> Box(modifier) { - ContentView(modifier = contentModifier) + content() TimelineEventTimestampView( event = event, onClick = onTimestampClicked, onLongClick = ::onTimestampLongClick, - modifier = timestampModifier + modifier = Modifier .align(Alignment.BottomEnd) .padding(horizontal = 8.dp, vertical = 4.dp) ) } TimestampPosition.Below -> Column(modifier) { - ContentView(modifier = contentModifier) + content() TimelineEventTimestampView( event = event, onClick = onTimestampClicked, onLongClick = ::onTimestampLongClick, - modifier = timestampModifier + modifier = Modifier .align(Alignment.End) .padding(horizontal = 8.dp, vertical = 4.dp) ) @@ -478,52 +461,77 @@ private fun MessageEventBubbleContent( inReplyToDetails: InReplyTo.Ready?, modifier: Modifier = Modifier ) { - val modifierWithPadding: Modifier + val timestampLayoutModifier: Modifier val contentModifier: Modifier when { inReplyToDetails != null -> { if (timestampPosition == TimestampPosition.Overlay) { - modifierWithPadding = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) + timestampLayoutModifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp) contentModifier = Modifier.clip(RoundedCornerShape(12.dp)) } else { contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 0.dp, bottom = 8.dp) - modifierWithPadding = Modifier + timestampLayoutModifier = Modifier } } timestampPosition != TimestampPosition.Overlay -> { - modifierWithPadding = Modifier + timestampLayoutModifier = Modifier contentModifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 8.dp) } else -> { - modifierWithPadding = Modifier + timestampLayoutModifier = Modifier contentModifier = Modifier } } - - EqualWidthColumn(modifier = modifier, spacing = 8.dp) { + val threadDecoration = @Composable { if (showThreadDecoration) { ThreadDecoration(modifier = Modifier.padding(top = 8.dp, start = 12.dp, end = 12.dp)) } - if (inReplyToDetails != null) { - val senderName = inReplyToDetails.senderDisplayName ?: inReplyToDetails.senderId.value - val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToDetails) - val text = textForInReplyTo(inReplyToDetails) - val topPadding = if (showThreadDecoration) 0.dp else 8.dp - ReplyToContent( - senderName = senderName, - text = text, - attachmentThumbnailInfo = attachmentThumbnailInfo, - modifier = Modifier - .padding(top = topPadding, start = 8.dp, end = 8.dp) - .clip(RoundedCornerShape(6.dp)) - .clickable(enabled = true, onClick = inReplyToClick), + } + val contentWithTimestamp = @Composable { + WithTimestampLayout( + timestampPosition = timestampPosition, + modifier = timestampLayoutModifier, + ) { + TimelineItemEventContentView( + content = event.content, + isMine = event.isMine, + interactionSource = interactionSource, + onClick = onMessageClick, + onLongClick = onMessageLongClick, + extraPadding = event.toExtraPadding(), + eventSink = eventSink, + modifier = contentModifier, ) } - ContentAndTimestampView( - timestampPosition = timestampPosition, - modifier = modifierWithPadding, - contentModifier = contentModifier, + } + val inReplyTo = @Composable { inReplyToReady: InReplyTo.Ready -> + val senderName = inReplyToReady.senderDisplayName ?: inReplyToReady.senderId.value + val attachmentThumbnailInfo = attachmentThumbnailInfoForInReplyTo(inReplyToReady) + val text = textForInReplyTo(inReplyToReady) + val topPadding = if (showThreadDecoration) 0.dp else 8.dp + ReplyToContent( + senderName = senderName, + text = text, + attachmentThumbnailInfo = attachmentThumbnailInfo, + modifier = Modifier + .padding(top = topPadding, start = 8.dp, end = 8.dp) + .clip(RoundedCornerShape(6.dp)) + .clickable(enabled = true, onClick = inReplyToClick), ) + + } + if (inReplyToDetails != null) { + // Use SubComposeLayout only if necessary as it can have consequences on the performance. + EqualWidthColumn(modifier = modifier, spacing = 8.dp) { + threadDecoration() + inReplyTo(inReplyToDetails) + contentWithTimestamp() + } + } else { + Column(modifier = modifier, verticalArrangement = spacedBy(8.dp)) { + threadDecoration() + contentWithTimestamp() + } } } From 5267597b69b4a560ad2a3788805902633c81af01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:55:48 +0000 Subject: [PATCH 079/102] Update wysiwyg to v2.18.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b43b4e7072..0d0e423a0a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,7 @@ serialization_json = "1.6.0" showkase = "1.0.2" appyx = "1.4.0" sqldelight = "2.0.0" -wysiwyg = "2.17.0" +wysiwyg = "2.18.0" # DI dagger = "2.48.1" From 99dbbc722ddee3d235125df981a35f06331186db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:44:00 +0000 Subject: [PATCH 080/102] Update dependency me.saket.telephoto:zoomable-image-coil to v0.7.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b43b4e7072..8e2fbff726 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -155,7 +155,7 @@ sqlite = "androidx.sqlite:sqlite-ktx:2.4.0" unifiedpush = "com.github.UnifiedPush:android-connector:2.1.1" otaliastudios_transcoder = "com.otaliastudios:transcoder:0.10.5" vanniktech_blurhash = "com.vanniktech:blurhash:0.1.0" -telephoto_zoomableimage = "me.saket.telephoto:zoomable-image-coil:0.6.2" +telephoto_zoomableimage = "me.saket.telephoto:zoomable-image-coil:0.7.1" statemachine = "com.freeletics.flowredux:compose:1.2.0" maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" From 7c3804888f966ca504e2c8721b72daa72345fab8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 19 Nov 2023 12:48:52 +0000 Subject: [PATCH 081/102] Update dependency com.squareup:kotlinpoet to v1.15.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b43b4e7072..6d7dd7d904 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -161,7 +161,7 @@ maplibre = "org.maplibre.gl:android-sdk:10.2.0" maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.2" maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.2" opusencoder = "io.element.android:opusencoder:1.1.0" -kotlinpoet = "com.squareup:kotlinpoet:1.14.2" +kotlinpoet = "com.squareup:kotlinpoet:1.15.1" # Analytics posthog = "com.posthog.android:posthog:2.0.3" From 1937d0e67d3057d4abfdaf473a6c094397e0b9de Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 08:52:15 +0000 Subject: [PATCH 082/102] Update rnkdsh/action-upload-diawi action to v1.5.4 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f89f5972d7..63ac00e289 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: name: elementx-debug path: | app/build/outputs/apk/debug/*.apk - - uses: rnkdsh/action-upload-diawi@v1.5.3 + - uses: rnkdsh/action-upload-diawi@v1.5.4 id: diawi # Do not fail the whole build if Diawi upload fails continue-on-error: true From c07a0321576c1adbbcd00e5acea213b5034c801c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 20 Nov 2023 10:00:21 +0100 Subject: [PATCH 083/102] SecureBackup: improve error flow when backup state cannot be retrieved, and add tests. --- .../impl/root/SecureBackupRootEvents.kt | 21 +++++++ .../impl/root/SecureBackupRootPresenter.kt | 32 +++++++--- .../impl/root/SecureBackupRootState.kt | 4 +- .../root/SecureBackupRootStateProvider.kt | 11 ++-- .../impl/root/SecureBackupRootView.kt | 63 +++++++++++++++---- .../root/SecureBackupRootPresenterTest.kt | 35 ++++++++++- .../test/encryption/FakeEncryptionService.kt | 7 ++- 7 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt new file mode 100644 index 0000000000..0b2859b250 --- /dev/null +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootEvents.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.securebackup.impl.root + +sealed interface SecureBackupRootEvents { + data object RetryKeyBackupState : SecureBackupRootEvents +} diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt index 4ed898ff9a..9036200ac9 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenter.kt @@ -18,18 +18,23 @@ package io.element.android.features.securebackup.impl.root import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.securebackup.impl.loggerTagRoot +import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -41,6 +46,7 @@ class SecureBackupRootPresenter @Inject constructor( @Composable override fun present(): SecureBackupRootState { + val localCoroutineScope = rememberCoroutineScope() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val backupState by encryptionService.backupStateStateFlow.collectAsState() @@ -49,23 +55,33 @@ class SecureBackupRootPresenter @Inject constructor( Timber.tag(loggerTagRoot.value).d("backupState: $backupState") Timber.tag(loggerTagRoot.value).d("recoveryState: $recoveryState") - var doesBackupExistOnServer: Boolean? by remember { mutableStateOf(null) } + val doesBackupExistOnServerAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) } LaunchedEffect(backupState) { - doesBackupExistOnServer = if (backupState == BackupState.UNKNOWN) { - encryptionService.doesBackupExistOnServer().getOrNull() == true - } else { - // The value will not be used when the backupState is not UNKNOWN. - false + if (backupState == BackupState.UNKNOWN) { + getKeyBackupStatus(doesBackupExistOnServerAction) + } + } + + fun handleEvents(event: SecureBackupRootEvents) { + when (event) { + SecureBackupRootEvents.RetryKeyBackupState -> localCoroutineScope.getKeyBackupStatus(doesBackupExistOnServerAction) } } return SecureBackupRootState( backupState = backupState, - doesBackupExistOnServer = doesBackupExistOnServer, + doesBackupExistOnServer = doesBackupExistOnServerAction.value, recoveryState = recoveryState, appName = buildMeta.applicationName, snackbarMessage = snackbarMessage, + eventSink = ::handleEvents, ) } + + private fun CoroutineScope.getKeyBackupStatus(action: MutableState>) = launch { + suspend { + encryptionService.doesBackupExistOnServer().getOrThrow() + }.runCatchingUpdatingState(action) + } } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt index a6dadf83c9..e2c6c1154c 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootState.kt @@ -16,14 +16,16 @@ package io.element.android.features.securebackup.impl.root +import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState data class SecureBackupRootState( val backupState: BackupState, - val doesBackupExistOnServer: Boolean?, + val doesBackupExistOnServer: Async, val recoveryState: RecoveryState, val appName: String, val snackbarMessage: SnackbarMessage?, + val eventSink: (SecureBackupRootEvents) -> Unit, ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt index efe59f23b3..ae8b2aa63b 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.securebackup.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.RecoveryState @@ -24,9 +25,10 @@ import io.element.android.libraries.matrix.api.encryption.RecoveryState open class SecureBackupRootStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = null), - aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = true), - aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = false), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = Async.Uninitialized), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = Async.Success(true)), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = Async.Success(false)), + aSecureBackupRootState(backupState = BackupState.UNKNOWN, doesBackupExistOnServer = Async.Failure(Exception("An error"))), aSecureBackupRootState(backupState = BackupState.ENABLED), aSecureBackupRootState(recoveryState = RecoveryState.UNKNOWN), aSecureBackupRootState(recoveryState = RecoveryState.ENABLED), @@ -38,7 +40,7 @@ open class SecureBackupRootStateProvider : PreviewParameterProvider = Async.Uninitialized, recoveryState: RecoveryState = RecoveryState.UNKNOWN, snackbarMessage: SnackbarMessage? = null, ) = SecureBackupRootState( @@ -47,4 +49,5 @@ fun aSecureBackupRootState( recoveryState = recoveryState, appName = "Element", snackbarMessage = snackbarMessage, + eventSink = {}, ) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index 8cee413c7e..e4ea588df0 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -16,18 +16,27 @@ package io.element.android.features.securebackup.impl.root +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import io.element.android.features.securebackup.impl.R +import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.components.async.AsyncLoading +import io.element.android.libraries.designsystem.components.list.ListItemContent import io.element.android.libraries.designsystem.components.preferences.PreferenceDivider import io.element.android.libraries.designsystem.components.preferences.PreferencePage import io.element.android.libraries.designsystem.components.preferences.PreferenceText import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.buildAnnotatedStringWithStyledPart +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator +import io.element.android.libraries.designsystem.theme.components.ListItem +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState import io.element.android.libraries.matrix.api.encryption.BackupState @@ -73,23 +82,55 @@ fun SecureBackupRootView( BackupState.WAITING_FOR_SYNC -> Unit BackupState.UNKNOWN -> { when (state.doesBackupExistOnServer) { - true -> { - // Should not happen, we will have the state BackupState.ENABLED - PreferenceText( - title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), - tintColor = ElementTheme.colors.textCriticalPrimary, - onClick = onDisableClicked, - ) + is Async.Success -> when (state.doesBackupExistOnServer.data) { + true -> { + // Should not happen, we will have the state BackupState.ENABLED + PreferenceText( + title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), + tintColor = ElementTheme.colors.textCriticalPrimary, + onClick = onDisableClicked, + ) + } + false -> { + PreferenceText( + title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), + onClick = onEnableClicked, + ) + } } - false -> { + is Async.Loading, + Async.Uninitialized -> { + ListItem(headlineContent = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + CircularProgressIndicator() + } + }) + } + is Async.Failure -> { + ListItem( + headlineContent = { + Text( + text = stringResource(id = CommonStrings.error_unknown), + ) + }, + trailingContent = ListItemContent.Custom { + TextButton( + text = stringResource( + id = CommonStrings.action_retry + ), + onClick = { state.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) } + ) + } + ) + PreferenceText( title = stringResource(id = R.string.screen_chat_backup_key_backup_action_enable), onClick = onEnableClicked, ) } - null -> { - AsyncLoading() - } } } BackupState.CREATING, diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt index 93031b6d2a..ba20439e79 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt @@ -20,12 +20,16 @@ import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.api.encryption.BackupState import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.matrix.api.encryption.RecoveryState +import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -40,9 +44,38 @@ class SecureBackupRootPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitItem() + val initialState = awaitLastSequentialItem() assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN) + assertThat(initialState.doesBackupExistOnServer.dataOrNull()).isTrue() + assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN) assertThat(initialState.appName).isEqualTo("Element") + assertThat(initialState.snackbarMessage).isNull() + } + } + + @Test + fun `present - Unknown state`() = runTest { + val encryptionService = FakeEncryptionService() + val presenter = createSecureBackupRootPresenter( + encryptionService = encryptionService, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + encryptionService.givenDoesBackupExistOnServerResult(Result.failure(AN_EXCEPTION)) + assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN) + assertThat(initialState.doesBackupExistOnServer).isEqualTo(Async.Uninitialized) + val loadingState1 = awaitItem() + assertThat(loadingState1.doesBackupExistOnServer).isInstanceOf(Async.Loading::class.java) + val errorState = awaitItem() + assertThat(errorState.doesBackupExistOnServer).isEqualTo(Async.Failure(AN_EXCEPTION)) + encryptionService.givenDoesBackupExistOnServerResult(Result.success(false)) + errorState.eventSink.invoke(SecureBackupRootEvents.RetryKeyBackupState) + val loadingState2 = awaitItem() + assertThat(loadingState2.doesBackupExistOnServer).isInstanceOf(Async.Loading::class.java) + val finalState = awaitItem() + assertThat(finalState.doesBackupExistOnServer.dataOrNull()).isFalse() } } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt index 4d7cbf5c09..9f10f4ba35 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/encryption/FakeEncryptionService.kt @@ -34,6 +34,7 @@ class FakeEncryptionService : EncryptionService { private var waitForBackupUploadSteadyStateFlow: Flow = flowOf() private var fixRecoveryIssuesFailure: Exception? = null + private var doesBackupExistOnServerResult: Result = Result.success(true) override suspend fun enableBackups(): Result = simulateLongTask { return Result.success(Unit) @@ -52,8 +53,12 @@ class FakeEncryptionService : EncryptionService { return Result.success(Unit) } + fun givenDoesBackupExistOnServerResult(result: Result) { + doesBackupExistOnServerResult = result + } + override suspend fun doesBackupExistOnServer(): Result = simulateLongTask { - return Result.success(true) + return doesBackupExistOnServerResult } override suspend fun fixRecoveryIssues(recoveryKey: String): Result = simulateLongTask { From eeb9b30d802e2310ac496c7be3003de4da6aac50 Mon Sep 17 00:00:00 2001 From: ElementBot <110224175+ElementBot@users.noreply.github.com> Date: Mon, 20 Nov 2023 09:07:53 +0000 Subject: [PATCH 084/102] Sync Strings (#1839) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Sync Strings from Localazy * Fix 'Report a problem' screen title --------- Co-authored-by: bmarty Co-authored-by: Jorge Martín --- .maestro/tests/settings/settings.yaml | 2 +- .../impl/src/main/res/values-ru/translations.xml | 1 + .../impl/src/main/res/values-sk/translations.xml | 1 + .../impl/src/main/res/values-ru/translations.xml | 1 + .../impl/src/main/res/values-sk/translations.xml | 1 + .../rageshake/impl/bugreport/BugReportView.kt | 2 +- .../impl/src/main/res/values-ru/translations.xml | 4 ++-- .../impl/src/main/res/values/localazy.xml | 4 ++-- .../impl/src/main/res/values-ru/translations.xml | 1 + .../impl/src/main/res/values-sk/translations.xml | 1 + .../src/main/res/values-ru/translations.xml | 15 +++++++++++++++ .../src/main/res/values-sk/translations.xml | 15 +++++++++++++++ .../ui-strings/src/main/res/values/localazy.xml | 14 ++++++++++++++ ...gReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...gReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...gReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...gReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...eportView-Night-0_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...eportView-Night-0_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...eportView-Night-0_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...eportView-Night-0_1_null_3,NEXUS_5,1.0,en].png | 4 ++-- 21 files changed, 72 insertions(+), 22 deletions(-) diff --git a/.maestro/tests/settings/settings.yaml b/.maestro/tests/settings/settings.yaml index 53fcf0f017..d5f1e110e5 100644 --- a/.maestro/tests/settings/settings.yaml +++ b/.maestro/tests/settings/settings.yaml @@ -16,7 +16,7 @@ appId: ${APP_ID} - tapOn: text: "Report a problem" -- assertVisible: "Report a bug" +- assertVisible: "Report a problem" - back - tapOn: diff --git a/features/messages/impl/src/main/res/values-ru/translations.xml b/features/messages/impl/src/main/res/values-ru/translations.xml index a1ce610568..77f7e1d0e5 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -45,6 +45,7 @@ "Произошла ошибка при загрузке настроек уведомлений." "Не удалось восстановить режим по умолчанию, попробуйте еще раз." "Не удалось настроить режим, попробуйте еще раз." + "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате." "Все сообщения" "В этой комнате уведомить меня о" "Показать меньше" diff --git a/features/messages/impl/src/main/res/values-sk/translations.xml b/features/messages/impl/src/main/res/values-sk/translations.xml index fd7efc1200..0bdf008fc2 100644 --- a/features/messages/impl/src/main/res/values-sk/translations.xml +++ b/features/messages/impl/src/main/res/values-sk/translations.xml @@ -45,6 +45,7 @@ "Pri načítavaní nastavení oznámení došlo k chybe." "Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova." "Nepodarilo sa nastaviť režim, skúste to prosím znova." + "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v tejto miestnosti nedostanete upozornenie." "Všetky správy" "V tejto miestnosti ma upozorniť na" "Zobraziť menej" diff --git a/features/preferences/impl/src/main/res/values-ru/translations.xml b/features/preferences/impl/src/main/res/values-ru/translations.xml index 9afb388a9a..3432902dea 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -28,6 +28,7 @@ "Включить уведомления на данном устройстве" "Конфигурация не была исправлена, попробуйте еще раз." "Групповые чаты" + "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, в некоторых комнатах вы можете не получать уведомления." "Упоминания" "Все" "Упоминания" diff --git a/features/preferences/impl/src/main/res/values-sk/translations.xml b/features/preferences/impl/src/main/res/values-sk/translations.xml index b1de800205..2b8f866255 100644 --- a/features/preferences/impl/src/main/res/values-sk/translations.xml +++ b/features/preferences/impl/src/main/res/values-sk/translations.xml @@ -30,6 +30,7 @@ Ak budete pokračovať, niektoré z vašich nastavení sa môžu zmeniť.""Povoliť oznámenia na tomto zariadení" "Konfigurácia nebola opravená, skúste to prosím znova." "Skupinové rozhovory" + "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v niektorých miestnostiach nemusíte dostať upozornenie." "Zmienky" "Všetky" "Zmienky" diff --git a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt index 2cbb54cdad..7833c21764 100644 --- a/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt +++ b/features/rageshake/impl/src/main/kotlin/io/element/android/features/rageshake/impl/bugreport/BugReportView.kt @@ -64,7 +64,7 @@ fun BugReportView( Box(modifier = modifier) { PreferencePage( - title = stringResource(id = CommonStrings.common_report_a_bug), + title = stringResource(id = CommonStrings.common_report_a_problem), onBackPressed = onBackPressed ) { val isFormEnabled = state.sending !is Async.Loading diff --git a/features/rageshake/impl/src/main/res/values-ru/translations.xml b/features/rageshake/impl/src/main/res/values-ru/translations.xml index 8f05a3148d..c22a34bf30 100644 --- a/features/rageshake/impl/src/main/res/values-ru/translations.xml +++ b/features/rageshake/impl/src/main/res/values-ru/translations.xml @@ -4,8 +4,8 @@ "Вы можете связаться со мной, если у Вас возникнут какие-либо дополнительные вопросы." "Связаться со мной" "Редактировать снимок экрана" - "Пожалуйста, опишите ошибку. Что вы сделали? Что вы ожидали, что произойдет? Что произошло на самом деле. Пожалуйста, опишите все как можно подробнее." - "Опишите ошибку…" + "Пожалуйста, опишите ошибку. Что вы сделали? Какое поведение вы ожидали? Что произошло на самом деле. Пожалуйста, опишите все как можно подробнее." + "Опишите проблему…" "Если возможно, пожалуйста, напишите описание на английском языке." "Отправка журналов сбоев" "Разрешить ведение журналов" diff --git a/features/rageshake/impl/src/main/res/values/localazy.xml b/features/rageshake/impl/src/main/res/values/localazy.xml index fb02c93780..34ba8b5b30 100644 --- a/features/rageshake/impl/src/main/res/values/localazy.xml +++ b/features/rageshake/impl/src/main/res/values/localazy.xml @@ -4,8 +4,8 @@ "You may contact me if you have any follow up questions." "Contact me" "Edit screenshot" - "Please describe the bug. What did you do? What did you expect to happen? What actually happened. Please go into as much detail as you can." - "Describe the bug…" + "Please describe the problem. What did you do? What did you expect to happen? What actually happened. Please go into as much detail as you can." + "Describe the problem…" "If possible, please write the description in English." "Send crash logs" "Allow logs" diff --git a/features/roomdetails/impl/src/main/res/values-ru/translations.xml b/features/roomdetails/impl/src/main/res/values-ru/translations.xml index 38a8835251..7d3eae6fff 100644 --- a/features/roomdetails/impl/src/main/res/values-ru/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ru/translations.xml @@ -36,6 +36,7 @@ "Произошла ошибка при загрузке настроек уведомлений." "Не удалось восстановить режим по умолчанию, попробуйте еще раз." "Не удалось настроить режим, попробуйте еще раз." + "Ваш домашний сервер не поддерживает эту опцию в зашифрованных комнатах, вы не будете получать уведомления в этой комнате." "Все сообщения" "В этой комнате уведомить меня о" "Заблокировать" diff --git a/features/roomdetails/impl/src/main/res/values-sk/translations.xml b/features/roomdetails/impl/src/main/res/values-sk/translations.xml index edaf50d915..c7e422b920 100644 --- a/features/roomdetails/impl/src/main/res/values-sk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-sk/translations.xml @@ -36,6 +36,7 @@ "Pri načítavaní nastavení oznámení došlo k chybe." "Nepodarilo sa obnoviť predvolený režim, skúste to prosím znova." "Nepodarilo sa nastaviť režim, skúste to prosím znova." + "Váš domovský server nepodporuje túto možnosť v šifrovaných miestnostiach, v tejto miestnosti nedostanete upozornenie." "Všetky správy" "V tejto miestnosti ma upozorniť na" "Zablokovať" diff --git a/libraries/ui-strings/src/main/res/values-ru/translations.xml b/libraries/ui-strings/src/main/res/values-ru/translations.xml index 309e463faf..0b4700235b 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -9,12 +9,14 @@ "Воспроизвести" "Опрос" "Опрос завершен" + "Прочитано %1$s" "Отправить файлы" "Показать пароль" "Начать звонок" "Меню пользователя" "Записать голосовое сообщение." "Остановить запись" + "Прочитано %1$s и %2$s" "Разрешить" "Добавить в хронологию" "Назад" @@ -81,6 +83,7 @@ "Начать подтверждение" "Нажмите, чтобы загрузить карту" "Сделать фото" + "Нажмите для просмотра вариантов" "Повторить попытку" "Показать источник" "Да" @@ -89,12 +92,14 @@ "Политика допустимого использования" "Дополнительные параметры" "Аналитика" + "Оформление" "Аудио" "Пузыри" "Резервная копия чатов" "Авторское право" "Создание комнаты…" "Покинул комнату" + "Темная" "Ошибка расшифровки" "Для разработчика" "(изменено)" @@ -113,6 +118,7 @@ "Установить APK" "Идентификатор Matrix ID не найден, приглашение может быть не получено." "Покинуть комнату" + "Светлая" "Ссылка скопирована в буфер обмена" "Загрузка…" "Сообщение" @@ -146,7 +152,10 @@ "Поиск человека" "Результаты поиска" "Безопасность" + "Просмотрено" "Отправка…" + "Сбой отправки" + "Отправлено" "Сервер не поддерживается" "Адрес сервера" "Настройки" @@ -157,6 +166,7 @@ "Успешно" "Рекомендации" "Синхронизация" + "Системная" "Текст" "Уведомление о третьей стороне" "Обсуждение" @@ -198,6 +208,11 @@ "Ведено %1$d цифр" "Введено много цифр" + + "Прочитано %1$s и %2$d другим" + "Прочитано %1$s и %2$d другими" + "Прочитано %1$s и %2$d другими" + "%1$d участник" "%1$d участников" diff --git a/libraries/ui-strings/src/main/res/values-sk/translations.xml b/libraries/ui-strings/src/main/res/values-sk/translations.xml index 0ccbab9cb8..7fe0ffd9d1 100644 --- a/libraries/ui-strings/src/main/res/values-sk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-sk/translations.xml @@ -9,12 +9,14 @@ "Prehrať" "Anketa" "Ukončená anketa" + "Prečítal/a %1$s" "Odoslať súbory" "Zobraziť heslo" "Začať hovor" "Používateľské menu" "Nahrať hlasovú správu." "Zastaviť nahrávanie" + "Prečítal/a %1$s a %2$s" "Prijať" "Pridať na časovú os" "Späť" @@ -81,6 +83,7 @@ "Spustiť overovanie" "Ťuknutím načítate mapu" "Urobiť fotku" + "Klepnutím získate možnosti" "Skúste to znova" "Zobraziť zdroj" "Áno" @@ -89,12 +92,14 @@ "Zásady prijateľného používania" "Pokročilé nastavenia" "Analytika" + "Vzhľad" "Zvuk" "Bubliny" "Záloha konverzácie" "Autorské práva" "Vytváranie miestnosti…" "Opustil/a miestnosť" + "Tmavý" "Chyba dešifrovania" "Možnosti pre vývojárov" "(upravené)" @@ -113,6 +118,7 @@ "Inštalovať APK" "Toto Matrix ID sa nedá nájsť, takže pozvánka nemusí byť prijatá." "Opustenie miestnosti" + "Svetlý" "Odkaz bol skopírovaný do schránky" "Načítava sa…" "Správa" @@ -146,7 +152,10 @@ "Vyhľadať niekoho" "Výsledky hľadania" "Bezpečnosť" + "Videné" "Odosiela sa…" + "Odoslanie zlyhalo" + "Odoslané" "Server nie je podporovaný" "URL adresa servera" "Nastavenia" @@ -157,6 +166,7 @@ "Úspech" "Návrhy" "Synchronizuje sa" + "Systém" "Text" "Oznámenia tretích strán" "Vlákno" @@ -198,6 +208,11 @@ "%1$d zadané číslice" "%1$d zadaných číslic" + + "Prečítal/a %1$s a %2$d ďalší" + "Prečítal/a %1$s a %2$d ďalší" + "Prečítal/a %1$s a %2$d ďalších" + "%1$d člen" "%1$d členovia" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 6eb51a87c8..c2f969551c 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -9,12 +9,14 @@ "Play" "Poll" "Ended poll" + "Read by %1$s" "Send files" "Show password" "Start a call" "User menu" "Record voice message." "Stop recording" + "Read by %1$s and %2$s" "Accept" "Add to timeline" "Back" @@ -81,6 +83,7 @@ "Start verification" "Tap to load map" "Take photo" + "Tap for options" "Try again" "View source" "Yes" @@ -89,12 +92,14 @@ "Acceptable use policy" "Advanced settings" "Analytics" + "Appearance" "Audio" "Bubbles" "Chat backup" "Copyright" "Creating room…" "Left room" + "Dark" "Decryption error" "Developer options" "(edited)" @@ -113,6 +118,7 @@ "Install APK" "This Matrix ID can\'t be found, so the invite might not be received." "Leaving room" + "Light" "Link copied to clipboard" "Loading…" "Message" @@ -146,7 +152,10 @@ "Search for someone" "Search results" "Security" + "Seen by" "Sending…" + "Sending failed" + "Sent" "Server not supported" "Server URL" "Settings" @@ -157,6 +166,7 @@ "Success" "Suggestions" "Syncing" + "System" "Text" "Third-party notices" "Thread" @@ -197,6 +207,10 @@ "%1$d digit entered" "%1$d digits entered" + + "Read by %1$s and %2$d other" + "Read by %1$s and %2$d others" + "%1$d member" "%1$d members" diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png index 5f5be1fdc5..b7bb6bcf4c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf178a51be45cfd5fee676628ae3192374c1bc873e6eef726053ae593974aafe -size 68108 +oid sha256:3928ea1a60bf2b5c1cc94ef566ca67ce03679994430f826f50636c781976e8e2 +size 70068 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png index 15e4e18e8e..7a638cc9f8 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:922ffb470cc64a29cd2d9a65840d267cb742efa523f3d93676ec148f818850e1 -size 204829 +oid sha256:62ddb0f9d6764b639b1a1438535c19138d1f81afdaaebfe03b021fc7f64a5291 +size 206670 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png index aa132627e5..10d0f335e2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:332e7a7baa12fb54fed4ad99f53470a96182e6059f3f24631ef9c5a7ed9c9f54 -size 59420 +oid sha256:6d546a6a3a5e517e99758f05396a72cc7bf802f3e207f5c7d881e5805f103b67 +size 61356 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png index 5f5be1fdc5..b7bb6bcf4c 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Day-0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf178a51be45cfd5fee676628ae3192374c1bc873e6eef726053ae593974aafe -size 68108 +oid sha256:3928ea1a60bf2b5c1cc94ef566ca67ce03679994430f826f50636c781976e8e2 +size 70068 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png index adc80daf5f..d8461b8165 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:310bd5d524d61cbc3b8237811c8d6623a6fb1046bb8a9823ff5173c2f2b3c862 -size 65218 +oid sha256:53cb4046ffb2391e880bb9f4567534483f9c3e8bdbea7d7706d3fb93ec0acdec +size 67105 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png index 618c296fbf..8e8c828451 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bd787f27931a5f71da9ed4248ff7c310acce37168b36e2575995ea1a1dc2cac -size 200484 +oid sha256:22c01def033c5ce1e7bc12dc7b5ec6fae11bb827f29818d3921f156a4cf04386 +size 202460 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png index cda48a2eff..29f1413708 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e93d40807105a64a933180a71a73c661c4b792f5a008af7ada5fc8fd80a1446b -size 54960 +oid sha256:8960e097e94be82ab519bbc1ed3679955dd200349ae32e55d11fad1cbaf586ae +size 56261 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png index adc80daf5f..d8461b8165 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.rageshake.impl.bugreport_BugReportView_null_BugReportView-Night-0_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:310bd5d524d61cbc3b8237811c8d6623a6fb1046bb8a9823ff5173c2f2b3c862 -size 65218 +oid sha256:53cb4046ffb2391e880bb9f4567534483f9c3e8bdbea7d7706d3fb93ec0acdec +size 67105 From f9d621b6e83ba468ffa59ee6bcd020e8b46c702a Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 20 Nov 2023 09:08:16 +0000 Subject: [PATCH 085/102] Update screenshots --- ...ll_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...ll_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png | 3 +++ ...ll_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png | 3 +++ ..._SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png | 4 ++-- ..._SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png | 3 +++ ..._SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png | 3 +++ 14 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png index d5ae1fe7c3..a6ce94832e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aef9057653b917425621448d0ef62975b02b6cae0e572758a2749ac9cc71189 -size 39364 +oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 +size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png index d5ae1fe7c3..c6db44bf6f 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aef9057653b917425621448d0ef62975b02b6cae0e572758a2749ac9cc71189 -size 39364 +oid sha256:7f395fed6dfabb7f26a10f851e0fca7ea475722e1def7159f36d05cc9e47e09a +size 47990 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png index 471a6b98c6..0abdbfd76d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b159c6b39ad05b3d0dc10ae0da1826bcaae45c94e0150b09c2cf6ddf2353d305 -size 24831 +oid sha256:92ce51c885eb9c5efb80b747ef107e185114401d235bfd261ec06a1bb9f53f85 +size 42518 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png index d5ae1fe7c3..a6ce94832e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1aef9057653b917425621448d0ef62975b02b6cae0e572758a2749ac9cc71189 -size 39364 +oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 +size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png index f75b484e1f..8e822c7ef2 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb83490002e584be9f268eadc014345740d0026c929e79f2bb4c407fdf2ba926 -size 29777 +oid sha256:3747da81312e3f34da13e6bd530301550c7307856cc7a31aa009be2d60002ecc +size 26338 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a6ce94832e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fafe906b308c604f4ef98dab36b55b84fd235598aea738913301940bf4222a93 +size 41473 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..557ce7e1ce --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Day-3_3_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:083440e0fa8699c8fd370bb13351d04c01fa2c78c273f8e2b07bff0b09e4f2f1 +size 31938 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png index 35f90db1d9..be5ffbb21e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:870c36e4f7a64e258af75e22845d05dd8c8217d6dc65826b6405537bed9bc848 -size 37439 +oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 +size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png index 35f90db1d9..cbfa69161d 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:870c36e4f7a64e258af75e22845d05dd8c8217d6dc65826b6405537bed9bc848 -size 37439 +oid sha256:0a8411a6c7df7bdc79a526755745fd2b2fed1c32676acd9c9b1135b2391e5abe +size 45326 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png index a8dd202ac2..3a7029466e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:837be69c85fb1a7f08ac0780ca34a56fa5d4d1f2134b4255efc68a78b33cab65 -size 23591 +oid sha256:19965ae446d383e69f8f05dc3e3cb1d3956a9962c472d9246b765ae467ef8d56 +size 40318 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png index 35f90db1d9..be5ffbb21e 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:870c36e4f7a64e258af75e22845d05dd8c8217d6dc65826b6405537bed9bc848 -size 37439 +oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 +size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png index ffc413c52d..4673753cdc 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eda4aab1ded791ad60e417818ad49070c1c53b9c1353da6dd843fae0934459a5 -size 28387 +oid sha256:14cb5e199172c0b325175e913708fb88111d479af06c1ac2c380a8cd68a80021 +size 25090 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..be5ffbb21e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94869b0aaccbbc1e82fb96de5f71fa7e9a59ba36317213c0f9a6bb4f9448e2b1 +size 39451 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f9da40e05d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.securebackup.impl.root_SecureBackupRootView_null_SecureBackupRootView-Night-3_4_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59912e13e6cfec021b7c12d689029f159a0b69e91905323f710d47124e67a7ea +size 30051 From 12bdaf9727196d032348e5d0b4ab2aa6059d3a2c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 20 Nov 2023 10:36:29 +0100 Subject: [PATCH 086/102] Remove wrong comment. --- .../features/securebackup/impl/root/SecureBackupRootView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt index e4ea588df0..3a27ae553e 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootView.kt @@ -84,7 +84,6 @@ fun SecureBackupRootView( when (state.doesBackupExistOnServer) { is Async.Success -> when (state.doesBackupExistOnServer.data) { true -> { - // Should not happen, we will have the state BackupState.ENABLED PreferenceText( title = stringResource(id = R.string.screen_chat_backup_key_backup_action_disable), tintColor = ElementTheme.colors.textCriticalPrimary, From 28f4ccdf9fe560d3b4a01d7e5c0cb867e74cd93d Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Mon, 20 Nov 2023 10:48:11 +0100 Subject: [PATCH 087/102] Delete media caches on startup (#1807) Clear media caches on app startup --- .../element/android/x/ElementXApplication.kt | 2 + features/cachecleaner/api/build.gradle.kts | 29 ++++++++ .../features/cachecleaner/api/CacheCleaner.kt | 26 +++++++ .../cachecleaner/api/CacheCleanerBindings.kt | 25 +++++++ .../api/CacheCleanerInitializer.kt | 29 ++++++++ features/cachecleaner/impl/build.gradle.kts | 41 +++++++++++ .../cachecleaner/impl/DefaultCacheCleaner.kt | 59 +++++++++++++++ .../impl/DefaultCacheCleanerTest.kt | 71 +++++++++++++++++++ .../timeline/VoiceMessageMediaRepo.kt | 1 - 9 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 features/cachecleaner/api/build.gradle.kts create mode 100644 features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt create mode 100644 features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerBindings.kt create mode 100644 features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerInitializer.kt create mode 100644 features/cachecleaner/impl/build.gradle.kts create mode 100644 features/cachecleaner/impl/src/main/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleaner.kt create mode 100644 features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt index c1bed67d80..03f0b20429 100644 --- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt @@ -18,6 +18,7 @@ package io.element.android.x import android.app.Application import androidx.startup.AppInitializer +import io.element.android.features.cachecleaner.api.CacheCleanerInitializer import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.x.di.AppComponent import io.element.android.x.di.DaggerAppComponent @@ -34,6 +35,7 @@ class ElementXApplication : Application(), DaggerComponentOwner { AppInitializer.getInstance(this).apply { initializeComponent(CrashInitializer::class.java) initializeComponent(TracingInitializer::class.java) + initializeComponent(CacheCleanerInitializer::class.java) } logApplicationInfo() } diff --git a/features/cachecleaner/api/build.gradle.kts b/features/cachecleaner/api/build.gradle.kts new file mode 100644 index 0000000000..38788af301 --- /dev/null +++ b/features/cachecleaner/api/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-library") + alias(libs.plugins.anvil) +} + +android { + namespace = "io.element.android.features.cachecleaner.api" +} + +dependencies { + implementation(projects.libraries.architecture) + implementation(libs.androidx.startup) +} diff --git a/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt new file mode 100644 index 0000000000..cd26d87bf9 --- /dev/null +++ b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleaner.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.cachecleaner.api + +interface CacheCleaner { + /** + * Clear the cache subdirs holding temporarily decrypted content (such as media and voice messages). + * + * Will fail silently in case of errors while deleting the files. + */ + fun clearCache() +} diff --git a/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerBindings.kt b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerBindings.kt new file mode 100644 index 0000000000..6492f9e62d --- /dev/null +++ b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerBindings.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.cachecleaner.api + +import com.squareup.anvil.annotations.ContributesTo +import io.element.android.libraries.di.AppScope + +@ContributesTo(AppScope::class) +interface CacheCleanerBindings { + fun cacheCleaner(): CacheCleaner +} diff --git a/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerInitializer.kt b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerInitializer.kt new file mode 100644 index 0000000000..5cd17c8715 --- /dev/null +++ b/features/cachecleaner/api/src/main/kotlin/io/element/android/features/cachecleaner/api/CacheCleanerInitializer.kt @@ -0,0 +1,29 @@ +/* + * 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 io.element.android.features.cachecleaner.api + +import android.content.Context +import androidx.startup.Initializer +import io.element.android.libraries.architecture.bindings + +class CacheCleanerInitializer : Initializer { + override fun create(context: Context) { + context.bindings().cacheCleaner().clearCache() + } + + override fun dependencies(): List>> = emptyList() +} diff --git a/features/cachecleaner/impl/build.gradle.kts b/features/cachecleaner/impl/build.gradle.kts new file mode 100644 index 0000000000..c95619419b --- /dev/null +++ b/features/cachecleaner/impl/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 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. + */ + +plugins { + id("io.element.android-compose-library") + alias(libs.plugins.anvil) +} + +android { + namespace = "io.element.android.features.cachecleaner.impl" +} + +anvil { + generateDaggerFactories.set(true) +} + +dependencies { + implementation(projects.anvilannotations) + anvil(projects.anvilcodegen) + api(projects.features.cachecleaner.api) + implementation(projects.libraries.core) + implementation(projects.libraries.architecture) + + testImplementation(libs.test.junit) + testImplementation(libs.coroutines.test) + testImplementation(libs.test.truth) + testImplementation(projects.tests.testutils) +} diff --git a/features/cachecleaner/impl/src/main/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleaner.kt b/features/cachecleaner/impl/src/main/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleaner.kt new file mode 100644 index 0000000000..fc77a5934a --- /dev/null +++ b/features/cachecleaner/impl/src/main/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleaner.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.cachecleaner.impl + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.cachecleaner.api.CacheCleaner +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.CacheDirectory +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import timber.log.Timber +import java.io.File +import javax.inject.Inject + +/** + * Default implementation of [CacheCleaner]. + */ +@ContributesBinding(AppScope::class) +class DefaultCacheCleaner @Inject constructor( + private val scope: CoroutineScope, + private val dispatchers: CoroutineDispatchers, + @CacheDirectory private val cacheDir: File, +) : CacheCleaner { + companion object { + val SUBDIRS_TO_CLEANUP = listOf("temp/media", "temp/voice") + } + + override fun clearCache() { + scope.launch(dispatchers.io) { + runCatching { + SUBDIRS_TO_CLEANUP.forEach { + File(cacheDir.path, it).apply { + if (exists()) { + if (!deleteRecursively()) error("Failed to delete recursively cache directory $this") + } + if (!mkdirs()) error("Failed to create cache directory $this") + } + } + }.onFailure { + Timber.e(it, "Failed to clear cache") + } + } + } +} diff --git a/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt b/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt new file mode 100644 index 0000000000..7fe70028ac --- /dev/null +++ b/features/cachecleaner/impl/src/test/kotlin/io/element/android/features/cachecleaner/impl/DefaultCacheCleanerTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.cachecleaner.impl + +import com.google.common.truth.Truth +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File + +class DefaultCacheCleanerTest { + @get:Rule + val temporaryFolder = TemporaryFolder() + + @Test + fun `calling clearCache actually removes file in the SUBDIRS_TO_CLEANUP list`() = runTest { + // Create temp subdirs and fill with 2 files each + DefaultCacheCleaner.SUBDIRS_TO_CLEANUP.forEach { + File(temporaryFolder.root, it).apply { + mkdirs() + File(this, "temp1").createNewFile() + File(this, "temp2").createNewFile() + } + } + + // Clear cache + aCacheCleaner().clearCache() + + // Check the files are gone but the sub dirs are not. + DefaultCacheCleaner.SUBDIRS_TO_CLEANUP.forEach { + File(temporaryFolder.root, it).apply { + Truth.assertThat(exists()).isTrue() + Truth.assertThat(isDirectory).isTrue() + Truth.assertThat(listFiles()).isEmpty() + } + } + } + + @Test + fun `clear cache fails silently`() = runTest { + // Set cache dir as unreadable, unwritable and unexecutable so that the deletion fails. + check(temporaryFolder.root.setReadable(false)) + check(temporaryFolder.root.setWritable(false)) + check(temporaryFolder.root.setExecutable(false)) + + aCacheCleaner().clearCache() + } + + private fun TestScope.aCacheCleaner() = DefaultCacheCleaner( + scope = this, + dispatchers = this.testCoroutineDispatchers(true), + cacheDir = temporaryFolder.root, + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt index a36eccc1d2..cc8bd1945f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageMediaRepo.kt @@ -89,7 +89,6 @@ class DefaultVoiceMessageMediaRepo @AssistedInject constructor( source = mediaSource, mimeType = mimeType, body = body, - useCache = false, ).mapCatching { it.use { mediaFile -> val dest = cachedFile.apply { parentFile?.mkdirs() } From 95ae3eaaf228a3dd5053a2cb347867b1526c912a Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Mon, 20 Nov 2023 10:48:25 +0100 Subject: [PATCH 088/102] Stop voice message on redaction (#1826) As per product spec: Voice messages must stop playing when redacted. --- .../impl/timeline/TimelinePresenter.kt | 3 + .../timeline/RedactedVoiceMessageManager.kt | 53 +++++++++ .../messages/MessagesPresenterTest.kt | 2 + .../timeline/TimelinePresenterTest.kt | 31 +++++- .../FakeRedactedVoiceMessageManager.kt | 31 ++++++ .../RedactedVoiceMessageManagerTest.kt | 104 ++++++++++++++++++ 6 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManager.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 0a0feedf65..c9dcbbc8ab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -32,6 +32,7 @@ import im.vector.app.features.analytics.plan.PollVote import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.session.SessionState +import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.core.EventId @@ -63,6 +64,7 @@ class TimelinePresenter @Inject constructor( private val analyticsService: AnalyticsService, private val verificationService: SessionVerificationService, private val encryptionService: EncryptionService, + private val redactedVoiceMessageManager: RedactedVoiceMessageManager, ) : Presenter { private val timeline = room.timeline @@ -142,6 +144,7 @@ class TimelinePresenter @Inject constructor( paginateBackwards() } } + .onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem) .launchIn(this) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManager.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManager.kt new file mode 100644 index 0000000000..fc9e98f0ed --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/RedactedVoiceMessageManager.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.voicemessages.timeline + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.mediaplayer.api.MediaPlayer +import kotlinx.coroutines.withContext +import javax.inject.Inject + +interface RedactedVoiceMessageManager { + suspend fun onEachMatrixTimelineItem(timelineItems: List) +} + +@ContributesBinding(RoomScope::class) +class DefaultRedactedVoiceMessageManager @Inject constructor( + private val dispatchers: CoroutineDispatchers, + private val mediaPlayer: MediaPlayer, +) : RedactedVoiceMessageManager { + override suspend fun onEachMatrixTimelineItem(timelineItems: List) { + withContext(dispatchers.computation) { + mediaPlayer.state.value.let { playerState -> + if (playerState.isPlaying && playerState.mediaId != null) { + val needsToPausePlayer = timelineItems.any { + it is MatrixTimelineItem.Event && + playerState.mediaId == it.eventId?.value && + it.event.content is RedactedContent + } + if (needsToPausePlayer) { + withContext(dispatchers.main) { mediaPlayer.pause() } + } + } + } + } + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 2969f26580..88468e6857 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -47,6 +47,7 @@ import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter +import io.element.android.features.messages.voicemessages.timeline.FakeRedactedVoiceMessageManager import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.architecture.Async @@ -653,6 +654,7 @@ class MessagesPresenterTest { analyticsService = analyticsService, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + redactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), ) val preferencesStore = InMemoryPreferencesStore(isRichTextEditorEnabled = true) val actionListPresenter = ActionListPresenter(preferencesStore = preferencesStore) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index eeca4026bf..fc60fbf759 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -24,11 +24,14 @@ import im.vector.app.features.analytics.plan.PollEnd import im.vector.app.features.analytics.plan.PollVote import io.element.android.features.messages.fixtures.aMessageEvent import io.element.android.features.messages.fixtures.aTimelineItemsFactory -import io.element.android.features.messages.impl.timeline.session.SessionState import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.session.SessionState +import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager +import io.element.android.features.messages.voicemessages.timeline.FakeRedactedVoiceMessageManager +import io.element.android.features.messages.voicemessages.timeline.aRedactedMatrixTimeline import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem @@ -310,9 +313,31 @@ class TimelinePresenterTest { } } + @Test + fun `present - side effect on redacted items is invoked`() = runTest { + val redactedVoiceMessageManager = FakeRedactedVoiceMessageManager() + val presenter = createTimelinePresenter( + timeline = FakeMatrixTimeline( + initialTimelineItems = aRedactedMatrixTimeline(AN_EVENT_ID), + ), + redactedVoiceMessageManager = redactedVoiceMessageManager, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) // skip initial state + assertThat(redactedVoiceMessageManager.invocations.size).isEqualTo(0) + awaitItem().let { + assertThat(it.timelineItems).isNotEmpty() + assertThat(redactedVoiceMessageManager.invocations.size).isEqualTo(1) + } + } + } + private fun TestScope.createTimelinePresenter( timeline: MatrixTimeline = FakeMatrixTimeline(), - timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory() + timelineItemsFactory: TimelineItemsFactory = aTimelineItemsFactory(), + redactedVoiceMessageManager: RedactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), ): TimelinePresenter { return TimelinePresenter( timelineItemsFactory = timelineItemsFactory, @@ -322,6 +347,7 @@ class TimelinePresenterTest { analyticsService = FakeAnalyticsService(), encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + redactedVoiceMessageManager = redactedVoiceMessageManager, ) } @@ -337,6 +363,7 @@ class TimelinePresenterTest { analyticsService = analyticsService, encryptionService = FakeEncryptionService(), verificationService = FakeSessionVerificationService(), + redactedVoiceMessageManager = FakeRedactedVoiceMessageManager(), ) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt new file mode 100644 index 0000000000..0ff58d7728 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/FakeRedactedVoiceMessageManager.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.voicemessages.timeline + +import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem + +class FakeRedactedVoiceMessageManager : RedactedVoiceMessageManager { + + private val _invocations: MutableList> = mutableListOf() + val invocations: List> + get() = _invocations + + override suspend fun onEachMatrixTimelineItem(timelineItems: List) { + _invocations.add(timelineItems) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt new file mode 100644 index 0000000000..b8be4d4d50 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.voicemessages.timeline + +import com.google.common.truth.Truth +import io.element.android.features.messages.impl.voicemessages.timeline.DefaultRedactedVoiceMessageManager +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.mediaplayer.api.MediaPlayer +import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class RedactedVoiceMessageManagerTest { + @Test + fun `redacted event - no playing related media`() = runTest { + val mediaPlayer = FakeMediaPlayer().apply { + setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg") + play() + } + val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer) + + Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + + manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID_2)) + + Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + } + + @Test + fun `redacted event - playing related media is paused`() = runTest { + val mediaPlayer = FakeMediaPlayer().apply { + setMedia(uri = "someUri", mediaId = AN_EVENT_ID.value, mimeType = "audio/ogg") + play() + } + val manager = aDefaultRedactedVoiceMessageManager(mediaPlayer = mediaPlayer) + + Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + Truth.assertThat(mediaPlayer.state.value.isPlaying).isTrue() + + manager.onEachMatrixTimelineItem(aRedactedMatrixTimeline(AN_EVENT_ID)) + + Truth.assertThat(mediaPlayer.state.value.mediaId).isEqualTo(AN_EVENT_ID.value) + Truth.assertThat(mediaPlayer.state.value.isPlaying).isFalse() + } +} + +fun TestScope.aDefaultRedactedVoiceMessageManager( + mediaPlayer: MediaPlayer = FakeMediaPlayer(), +) = DefaultRedactedVoiceMessageManager( + dispatchers = this.testCoroutineDispatchers(true), + mediaPlayer = mediaPlayer, +) + +fun aRedactedMatrixTimeline(eventId: EventId) = listOf( + MatrixTimelineItem.Event( + uniqueId = 0, + event = EventTimelineItem( + eventId = eventId, + transactionId = null, + isEditable = false, + isLocal = false, + isOwn = false, + isRemote = false, + localSendState = null, + reactions = listOf(), + sender = A_USER_ID, + senderProfile = ProfileTimelineDetails.Unavailable, + timestamp = 9442, + content = RedactedContent, + debugInfo = TimelineItemDebugInfo( + model = "enim", + originalJson = null, + latestEditedJson = null + ), + origin = null + ), + ) +) From e7c864c61a894f4526a94142858177445b509c0e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 20 Nov 2023 11:27:25 +0100 Subject: [PATCH 089/102] Fix test on CI. --- .../securebackup/impl/root/SecureBackupRootPresenterTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt index ba20439e79..6b88e20748 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/root/SecureBackupRootPresenterTest.kt @@ -29,7 +29,6 @@ import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.tests.testutils.WarmUpRule -import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -44,7 +43,8 @@ class SecureBackupRootPresenterTest { moleculeFlow(RecompositionMode.Immediate) { presenter.present() }.test { - val initialState = awaitLastSequentialItem() + skipItems(2) + val initialState = awaitItem() assertThat(initialState.backupState).isEqualTo(BackupState.UNKNOWN) assertThat(initialState.doesBackupExistOnServer.dataOrNull()).isTrue() assertThat(initialState.recoveryState).isEqualTo(RecoveryState.UNKNOWN) From 5397ecdf51dd4ed96d704bf9a8570d597c888314 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 20 Nov 2023 14:13:03 +0100 Subject: [PATCH 090/102] Fix compilation issue after merge. --- .../voicemessages/timeline/RedactedVoiceMessageManagerTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt index b8be4d4d50..ccf3d4aa92 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/RedactedVoiceMessageManagerTest.kt @@ -89,6 +89,7 @@ fun aRedactedMatrixTimeline(eventId: EventId) = listOf( isRemote = false, localSendState = null, reactions = listOf(), + receipts = listOf(), sender = A_USER_ID, senderProfile = ProfileTimelineDetails.Unavailable, timestamp = 9442, From 86a3ec6732ca11919690ae5a0eeac9f57483dc1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:44:46 +0000 Subject: [PATCH 091/102] Update dependency io.sentry:sentry-android to v6.34.0 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e5fc26d8fc..5761a13741 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -165,7 +165,7 @@ kotlinpoet = "com.squareup:kotlinpoet:1.14.2" # Analytics posthog = "com.posthog.android:posthog:2.0.3" -sentry = "io.sentry:sentry-android:6.33.1" +sentry = "io.sentry:sentry-android:6.34.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:aa14cbcdf81af2746d20a71779ec751f971e1d7f" # Emojibase From a8fbb882f28cd5033f3f5ca8853ee6c49d396e4c Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 20 Nov 2023 18:14:02 +0100 Subject: [PATCH 092/102] Integrate mentions in the composer (#1799) * Integrate mentions in the composer: - Add `MentionSpanProvider`. - Add custom colors needed for mentions. - Use the span provider to render mentions in the composer. - Allow selecting users from the mentions suggestions to insert a mention. --------- Co-authored-by: ElementBot --- changelog.d/1453.feature | 1 + .../features/messages/impl/MessagesView.kt | 13 +- .../impl/mentions/MentionSuggestion.kt | 26 ++++ .../mentions/MentionSuggestionsPickerView.kt | 31 +++-- .../mentions/MentionSuggestionsProcessor.kt | 18 ++- .../messagecomposer/MessageComposerEvents.kt | 2 + .../MessageComposerPresenter.kt | 37 ++++-- .../messagecomposer/MessageComposerState.kt | 5 +- .../MessageComposerStateProvider.kt | 5 +- .../messagecomposer/MessageComposerView.kt | 6 +- .../MessageComposerPresenterTest.kt | 31 +++-- .../android/libraries/core/data/FilterUpTo.kt | 39 ++++++ .../designsystem/theme/ColorAliases.kt | 30 +++++ .../matrix/api/permalink/PermalinkParser.kt | 18 +-- libraries/textcomposer/impl/build.gradle.kts | 3 + .../libraries/textcomposer/TextComposer.kt | 30 +++++ .../textcomposer/mentions/MentionSpan.kt | 42 +++++++ .../mentions/MentionSpanProvider.kt | 116 ++++++++++++++++++ .../impl/mentions/MentionSpanProviderTest.kt | 67 ++++++++++ ...ionSpan-Day-18_18_null,NEXUS_5,1.0,en].png | 3 + ...nSpan-Night-18_19_null,NEXUS_5,1.0,en].png | 3 + 21 files changed, 465 insertions(+), 61 deletions(-) create mode 100644 changelog.d/1453.feature create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestion.kt create mode 100644 libraries/core/src/main/kotlin/io/element/android/libraries/core/data/FilterUpTo.kt create mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt create mode 100644 libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt create mode 100644 libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/1453.feature b/changelog.d/1453.feature new file mode 100644 index 0000000000..86cc90ad74 --- /dev/null +++ b/changelog.d/1453.feature @@ -0,0 +1 @@ +Add support for typing mentions in the message composer. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index d07afd43b5..c00fbe09aa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -41,6 +41,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -354,12 +355,12 @@ private fun MessagesViewContent( // This key is used to force the sheet to be remeasured when the content changes. // Any state change that should trigger a height size should be added to the list of remembered values here. - val sheetResizeContentKey = remember( - state.composerState.mode.relatedEventId, + val sheetResizeContentKey = remember { mutableIntStateOf(0) } + LaunchedEffect( state.composerState.richTextEditorState.lineCount, - state.composerState.memberSuggestions.size + state.composerState.showTextFormatting, ) { - Random.nextInt() + sheetResizeContentKey.intValue = Random.nextInt() } ExpandableBottomSheetScaffold( @@ -396,7 +397,7 @@ private fun MessagesViewContent( state = state, ) }, - sheetContentKey = sheetResizeContentKey, + sheetContentKey = sheetResizeContentKey.intValue, sheetTonalElevation = 0.dp, sheetShadowElevation = if (state.composerState.memberSuggestions.isNotEmpty()) 16.dp else 0.dp, ) @@ -425,7 +426,7 @@ private fun MessagesViewComposerBottomSheetContents( roomAvatarData = state.roomAvatar.dataOrNull(), memberSuggestions = state.composerState.memberSuggestions, onSuggestionSelected = { - // TODO pass the selected suggestion to the RTE so it can be inserted as a pill + state.composerState.eventSink(MessageComposerEvents.InsertMention(it)) } ) MessageComposerView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestion.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestion.kt new file mode 100644 index 0000000000..b2977bd508 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestion.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.mentions + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.room.RoomMember + +@Immutable +sealed interface MentionSuggestion { + data object Room : MentionSuggestion + data class Member(val roomMember: RoomMember) : MentionSuggestion +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt index 6b937a872e..779398b020 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsPickerView.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.R -import io.element.android.features.messages.impl.messagecomposer.RoomMemberSuggestion import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -52,8 +51,8 @@ fun MentionSuggestionsPickerView( roomId: RoomId, roomName: String?, roomAvatarData: AvatarData?, - memberSuggestions: ImmutableList, - onSuggestionSelected: (RoomMemberSuggestion) -> Unit, + memberSuggestions: ImmutableList, + onSuggestionSelected: (MentionSuggestion) -> Unit, modifier: Modifier = Modifier, ) { LazyColumn( @@ -63,8 +62,8 @@ fun MentionSuggestionsPickerView( memberSuggestions, key = { suggestion -> when (suggestion) { - is RoomMemberSuggestion.Room -> "@room" - is RoomMemberSuggestion.Member -> suggestion.roomMember.userId.value + is MentionSuggestion.Room -> "@room" + is MentionSuggestion.Member -> suggestion.roomMember.userId.value } } ) { @@ -85,18 +84,18 @@ fun MentionSuggestionsPickerView( @Composable private fun RoomMemberSuggestionItemView( - memberSuggestion: RoomMemberSuggestion, + memberSuggestion: MentionSuggestion, roomId: String, roomName: String?, roomAvatar: AvatarData?, - onSuggestionSelected: (RoomMemberSuggestion) -> Unit, + onSuggestionSelected: (MentionSuggestion) -> Unit, modifier: Modifier = Modifier, ) { Row(modifier = modifier.clickable { onSuggestionSelected(memberSuggestion) }, horizontalArrangement = Arrangement.spacedBy(16.dp)) { val avatarSize = AvatarSize.TimelineRoom val avatarData = when (memberSuggestion) { - is RoomMemberSuggestion.Room -> roomAvatar?.copy(size = avatarSize) ?: AvatarData(roomId, roomName, null, avatarSize) - is RoomMemberSuggestion.Member -> AvatarData( + is MentionSuggestion.Room -> roomAvatar?.copy(size = avatarSize) ?: AvatarData(roomId, roomName, null, avatarSize) + is MentionSuggestion.Member -> AvatarData( memberSuggestion.roomMember.userId.value, memberSuggestion.roomMember.displayName, memberSuggestion.roomMember.avatarUrl, @@ -104,13 +103,13 @@ private fun RoomMemberSuggestionItemView( ) } val title = when (memberSuggestion) { - is RoomMemberSuggestion.Room -> stringResource(R.string.screen_room_mentions_at_room_title) - is RoomMemberSuggestion.Member -> memberSuggestion.roomMember.displayName + is MentionSuggestion.Room -> stringResource(R.string.screen_room_mentions_at_room_title) + is MentionSuggestion.Member -> memberSuggestion.roomMember.displayName } val subtitle = when (memberSuggestion) { - is RoomMemberSuggestion.Room -> "@room" - is RoomMemberSuggestion.Member -> memberSuggestion.roomMember.userId.value + is MentionSuggestion.Room -> "@room" + is MentionSuggestion.Member -> memberSuggestion.roomMember.userId.value } Avatar(avatarData = avatarData, modifier = Modifier.padding(start = 16.dp, top = 12.dp, bottom = 12.dp)) @@ -159,9 +158,9 @@ internal fun MentionSuggestionsPickerView_Preview() { roomName = "Room", roomAvatarData = null, memberSuggestions = persistentListOf( - RoomMemberSuggestion.Room, - RoomMemberSuggestion.Member(roomMember), - RoomMemberSuggestion.Member(roomMember.copy(userId = UserId("@bob:server.org"), displayName = "Bob")), + MentionSuggestion.Room, + MentionSuggestion.Member(roomMember), + MentionSuggestion.Member(roomMember.copy(userId = UserId("@bob:server.org"), displayName = "Bob")), ), onSuggestionSelected = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsProcessor.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsProcessor.kt index 1f4e2b5376..d2c3e6e518 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsProcessor.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/mentions/MentionSuggestionsProcessor.kt @@ -16,7 +16,7 @@ package io.element.android.features.messages.impl.mentions -import io.element.android.features.messages.impl.messagecomposer.RoomMemberSuggestion +import io.element.android.libraries.core.data.filterUpTo import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMember @@ -46,10 +46,8 @@ object MentionSuggestionsProcessor { roomMembersState: MatrixRoomMembersState, currentUserId: UserId, canSendRoomMention: suspend () -> Boolean, - ): List { + ): List { val members = roomMembersState.roomMembers() - // Take the first MAX_BATCH_ITEMS only - ?.take(MAX_BATCH_ITEMS) return when { members.isNullOrEmpty() || suggestion == null -> { // Clear suggestions @@ -61,7 +59,7 @@ object MentionSuggestionsProcessor { // Replace suggestions val matchingMembers = getMemberSuggestions( query = suggestion.text, - roomMembers = roomMembersState.roomMembers(), + roomMembers = members, currentUserId = currentUserId, canSendRoomMention = canSendRoomMention() ) @@ -81,7 +79,7 @@ object MentionSuggestionsProcessor { roomMembers: List?, currentUserId: UserId, canSendRoomMention: Boolean, - ): List { + ): List { return if (roomMembers.isNullOrEmpty()) { emptyList() } else { @@ -95,14 +93,14 @@ object MentionSuggestionsProcessor { } val matchingMembers = roomMembers - // Search only in joined members, exclude the current user - .filter { member -> + // Search only in joined members, up to MAX_BATCH_ITEMS, exclude the current user + .filterUpTo(MAX_BATCH_ITEMS) { member -> isJoinedMemberAndNotSelf(member) && memberMatchesQuery(member, query) } - .map(RoomMemberSuggestion::Member) + .map(MentionSuggestion::Member) if ("room".contains(query) && canSendRoomMention) { - listOf(RoomMemberSuggestion.Room) + matchingMembers + listOf(MentionSuggestion.Room) + matchingMembers } else { matchingMembers } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt index 97c2e7015d..c8ce20b6b9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerEvents.kt @@ -17,6 +17,7 @@ package io.element.android.features.messages.impl.messagecomposer import androidx.compose.runtime.Immutable +import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.libraries.textcomposer.model.Message import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.Suggestion @@ -41,4 +42,5 @@ sealed interface MessageComposerEvents { data object CancelSendAttachment : MessageComposerEvents data class Error(val error: Throwable) : MessageComposerEvents data class SuggestionReceived(val suggestion: Suggestion?) : MessageComposerEvents + data class InsertMention(val mention: MentionSuggestion) : MessageComposerEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index c4b631e4df..3a952efb69 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -20,7 +20,6 @@ import android.Manifest import android.annotation.SuppressLint import android.net.Uri import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue @@ -36,6 +35,7 @@ import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.media.local.LocalMediaFactory +import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -45,8 +45,8 @@ import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.ProgressCallback +import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaSender @@ -67,6 +67,8 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.merge import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import javax.inject.Inject @@ -87,7 +89,7 @@ class MessageComposerPresenter @Inject constructor( private val messageComposerContext: MessageComposerContextImpl, private val richTextEditorStateFactory: RichTextEditorStateFactory, private val currentSessionIdHolder: CurrentSessionIdHolder, - permissionsPresenterFactory: PermissionsPresenter.Factory + permissionsPresenterFactory: PermissionsPresenter.Factory, ) : Presenter { private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA) @@ -173,7 +175,7 @@ class MessageComposerPresenter @Inject constructor( } } - val memberSuggestions = remember { mutableStateListOf() } + val memberSuggestions = remember { mutableStateListOf() } LaunchedEffect(isMentionsEnabled) { if (!isMentionsEnabled) return@LaunchedEffect val currentUserId = currentSessionIdHolder.current @@ -184,8 +186,11 @@ class MessageComposerPresenter @Inject constructor( return !roomIsDm && userCanSendAtRoom } - suggestionSearchTrigger - .debounce(0.5.seconds) + // This will trigger a search immediately when `@` is typed + val mentionStartTrigger = suggestionSearchTrigger.filter { it?.text.isNullOrEmpty() } + // This will start a search when the user changes the text after the `@` with a debounce to prevent too much wasted work + val mentionCompletionTrigger = suggestionSearchTrigger.filter { !it?.text.isNullOrEmpty() }.debounce(0.3.seconds) + merge(mentionStartTrigger, mentionCompletionTrigger) .combine(room.membersStateFlow) { suggestion, roomMembersState -> memberSuggestions.clear() val result = MentionSuggestionsProcessor.process( @@ -284,6 +289,20 @@ class MessageComposerPresenter @Inject constructor( is MessageComposerEvents.SuggestionReceived -> { suggestionSearchTrigger.value = event.suggestion } + is MessageComposerEvents.InsertMention -> { + localCoroutineScope.launch { + when (val mention = event.mention) { + is MentionSuggestion.Room -> { + richTextEditorState.insertAtRoomMentionAtSuggestion() + } + is MentionSuggestion.Member -> { + val text = mention.roomMember.displayName?.prependIndent("@") ?: mention.roomMember.userId.value + val link = PermalinkBuilder.permalinkForUser(mention.roomMember.userId).getOrNull() ?: return@launch + richTextEditorState.insertMentionAtSuggestion(text = text, link = link) + } + } + } + } } } @@ -297,6 +316,7 @@ class MessageComposerPresenter @Inject constructor( canCreatePoll = canCreatePoll.value, attachmentsState = attachmentsState.value, memberSuggestions = memberSuggestions.toPersistentList(), + currentUserId = currentSessionIdHolder.current, eventSink = { handleEvents(it) } ) } @@ -410,8 +430,3 @@ class MessageComposerPresenter @Inject constructor( } } -@Immutable -sealed interface RoomMemberSuggestion { - data object Room : RoomMemberSuggestion - data class Member(val roomMember: RoomMember) : RoomMemberSuggestion -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt index 6a9d963d18..09eb51477f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerState.kt @@ -19,6 +19,8 @@ package io.element.android.features.messages.impl.messagecomposer import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import io.element.android.features.messages.impl.attachments.Attachment +import io.element.android.features.messages.impl.mentions.MentionSuggestion +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.wysiwyg.compose.RichTextEditorState import kotlinx.collections.immutable.ImmutableList @@ -33,7 +35,8 @@ data class MessageComposerState( val canShareLocation: Boolean, val canCreatePoll: Boolean, val attachmentsState: AttachmentsState, - val memberSuggestions: ImmutableList, + val memberSuggestions: ImmutableList, + val currentUserId: UserId, val eventSink: (MessageComposerEvents) -> Unit, ) { val hasFocus: Boolean = richTextEditorState.hasFocus diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt index ac936b2118..a542cb772d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerStateProvider.kt @@ -17,6 +17,8 @@ package io.element.android.features.messages.impl.messagecomposer import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.mentions.MentionSuggestion +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.wysiwyg.compose.RichTextEditorState import kotlinx.collections.immutable.ImmutableList @@ -38,7 +40,7 @@ fun aMessageComposerState( canShareLocation: Boolean = true, canCreatePoll: Boolean = true, attachmentsState: AttachmentsState = AttachmentsState.None, - memberSuggestions: ImmutableList = persistentListOf(), + memberSuggestions: ImmutableList = persistentListOf(), ) = MessageComposerState( richTextEditorState = composerState, isFullScreen = isFullScreen, @@ -49,5 +51,6 @@ fun aMessageComposerState( canCreatePoll = canCreatePoll, attachmentsState = attachmentsState, memberSuggestions = memberSuggestions, + currentUserId = UserId("@alice:localhost"), eventSink = {}, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt index b988d545a6..28e40a910f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents @@ -32,9 +33,9 @@ import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.textcomposer.TextComposer import io.element.android.libraries.textcomposer.model.Message -import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import io.element.android.libraries.textcomposer.model.Suggestion import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent +import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent import kotlinx.coroutines.launch @Composable @@ -46,6 +47,7 @@ internal fun MessageComposerView( enableVoiceMessages: Boolean, modifier: Modifier = Modifier, ) { + val view = LocalView.current fun sendMessage(message: Message) { state.eventSink(MessageComposerEvents.SendMessage(message)) } @@ -59,6 +61,7 @@ internal fun MessageComposerView( } fun onDismissTextFormatting() { + view.clearFocus() state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = false)) } @@ -113,6 +116,7 @@ internal fun MessageComposerView( onDeleteVoiceMessage = onDeleteVoiceMessage, onSuggestionReceived = ::onSuggestionReceived, onError = ::onError, + currentUserId = state.currentUserId, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index f05c209085..ef58792552 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -31,7 +31,7 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.messagecomposer.MessageComposerState -import io.element.android.features.messages.impl.messagecomposer.RoomMemberSuggestion +import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -754,19 +754,19 @@ class MessageComposerPresenterTest { // An empty suggestion returns the room and joined members that are not the current user initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, ""))) assertThat(awaitItem().memberSuggestions) - .containsExactly(RoomMemberSuggestion.Room, RoomMemberSuggestion.Member(bob), RoomMemberSuggestion.Member(david)) + .containsExactly(MentionSuggestion.Room, MentionSuggestion.Member(bob), MentionSuggestion.Member(david)) // A suggestion containing a part of "room" will also return the room mention initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "roo"))) - assertThat(awaitItem().memberSuggestions).containsExactly(RoomMemberSuggestion.Room) + assertThat(awaitItem().memberSuggestions).containsExactly(MentionSuggestion.Room) // A non-empty suggestion will return those joined members whose user id matches it initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "bob"))) - assertThat(awaitItem().memberSuggestions).containsExactly(RoomMemberSuggestion.Member(bob)) + assertThat(awaitItem().memberSuggestions).containsExactly(MentionSuggestion.Member(bob)) // A non-empty suggestion will return those joined members whose display name matches it initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, "dave"))) - assertThat(awaitItem().memberSuggestions).containsExactly(RoomMemberSuggestion.Member(david)) + assertThat(awaitItem().memberSuggestions).containsExactly(MentionSuggestion.Member(david)) // If the suggestion isn't a mention, no suggestions are returned initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Command, ""))) @@ -776,7 +776,7 @@ class MessageComposerPresenterTest { room.givenCanTriggerRoomNotification(Result.success(false)) initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, ""))) assertThat(awaitItem().memberSuggestions) - .containsExactly(RoomMemberSuggestion.Member(bob), RoomMemberSuggestion.Member(david)) + .containsExactly(MentionSuggestion.Member(bob), MentionSuggestion.Member(david)) // If room is a DM, `RoomMemberSuggestion.Room` is not returned room.givenCanTriggerRoomNotification(Result.success(true)) @@ -813,8 +813,25 @@ class MessageComposerPresenterTest { // An empty suggestion returns the joined members that are not the current user, but not the room initialState.eventSink(MessageComposerEvents.SuggestionReceived(Suggestion(0, 0, SuggestionType.Mention, ""))) + skipItems(1) assertThat(awaitItem().memberSuggestions) - .containsExactly(RoomMemberSuggestion.Member(bob), RoomMemberSuggestion.Member(david)) + .containsExactly(MentionSuggestion.Member(bob), MentionSuggestion.Member(david)) + } + } + + @Test + fun `present - insertMention`() = runTest { + val presenter = createPresenter(this) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + initialState.richTextEditorState.setHtml("Hey @bo") + initialState.eventSink(MessageComposerEvents.InsertMention(MentionSuggestion.Member(aRoomMember(userId = A_USER_ID_2)))) + + assertThat(initialState.richTextEditorState.messageHtml) + .isEqualTo("Hey ${A_USER_ID_2.value}") } } diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/FilterUpTo.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/FilterUpTo.kt new file mode 100644 index 0000000000..d3e2cc3e78 --- /dev/null +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/data/FilterUpTo.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.core.data + +/** + * Returns a list containing first [count] elements matching the given [predicate]. + * If the list contains less elements matching the [predicate], then all of them are returned. + * + * @param T the type of elements contained in the list. + * @param count the maximum number of elements to take. + * @param predicate the predicate used to match elements. + * @return a list containing first [count] elements matching the given [predicate]. + */ +inline fun Iterable.filterUpTo(count: Int, predicate: (T) -> Boolean): List { + val result = mutableListOf() + for (element in this) { + if (predicate(element)) { + result.add(element) + if (result.size == count) { + break + } + } + } + return result +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt index e85abb396b..c860239d6a 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorAliases.kt @@ -108,6 +108,36 @@ val SemanticColors.pinDigitBg Color(0xFF26282D) } +val SemanticColors.currentUserMentionPillText + get() = if (isLight) { + // We want LightDesignTokens.colorGreen1100 + Color(0xff005c45) + } else { + // We want DarkDesignTokens.colorGreen1100 + Color(0xff1fc090) + } + +val SemanticColors.currentUserMentionPillBackground + get() = if (isLight) { + // We want LightDesignTokens.colorGreenAlpha400 + Color(0x3b07b661) + } else { + // We want DarkDesignTokens.colorGreenAlpha500 + Color(0xff003d29) + } + +val SemanticColors.mentionPillText + get() = textPrimary + +val SemanticColors.mentionPillBackground + get() = if (isLight) { + // We want LightDesignTokens.colorGray400 + Color(0x1f052e61) + } else { + // We want DarkDesignTokens.colorGray500 + Color(0x26f4f7fa) + } + @PreviewsDayNight @Composable internal fun ColorAliasesPreview() = ElementPreview { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt index ba9cdc1e80..4a89d05276 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/permalink/PermalinkParser.kt @@ -133,13 +133,15 @@ object PermalinkParser { } private fun String.getViaParameters(): List { - return UrlQuerySanitizer(this) - .parameterList - .filter { - it.mParameter == "via" - } - .map { - URLDecoder.decode(it.mValue, "UTF-8") - } + return runCatching { + UrlQuerySanitizer(this) + .parameterList + .filter { + it.mParameter == "via" + } + .map { + URLDecoder.decode(it.mValue, "UTF-8") + } + }.getOrDefault(emptyList()) } } diff --git a/libraries/textcomposer/impl/build.gradle.kts b/libraries/textcomposer/impl/build.gradle.kts index db97a96787..99f910d317 100644 --- a/libraries/textcomposer/impl/build.gradle.kts +++ b/libraries/textcomposer/impl/build.gradle.kts @@ -42,4 +42,7 @@ dependencies { testImplementation(libs.test.junit) testImplementation(libs.test.truth) testImplementation(libs.coroutines.test) + testImplementation(libs.test.robolectric) + testImplementation(projects.libraries.matrix.test) + testImplementation(projects.tests.testutils) } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index 93e552768a..75dbc6c1c0 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -44,6 +44,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -57,6 +58,7 @@ import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.CommonDrawables import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.TransactionId +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.ui.components.A_BLUR_HASH import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail @@ -73,6 +75,7 @@ import io.element.android.libraries.textcomposer.components.VoiceMessageDeleteBu import io.element.android.libraries.textcomposer.components.VoiceMessagePreview import io.element.android.libraries.textcomposer.components.VoiceMessageRecording import io.element.android.libraries.textcomposer.components.textInputRoundedCornerShape +import io.element.android.libraries.textcomposer.mentions.rememberMentionSpanProvider import io.element.android.libraries.textcomposer.model.Message import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.libraries.textcomposer.model.VoiceMessageRecorderEvent @@ -81,8 +84,10 @@ import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.theme.ElementTheme import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.wysiwyg.compose.PillStyle import io.element.android.wysiwyg.compose.RichTextEditor import io.element.android.wysiwyg.compose.RichTextEditorState +import io.element.android.wysiwyg.display.TextDisplay import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import uniffi.wysiwyg_composer.MenuAction @@ -95,6 +100,7 @@ fun TextComposer( composerMode: MessageComposerMode, enableTextFormatting: Boolean, enableVoiceMessages: Boolean, + currentUserId: UserId, modifier: Modifier = Modifier, showTextFormatting: Boolean = false, subcomposing: Boolean = false, @@ -143,6 +149,7 @@ fun TextComposer( val textInput: @Composable () -> Unit = remember(state, subcomposing, composerMode, onResetComposerMode, onError) { @Composable { + val mentionSpanProvider = rememberMentionSpanProvider(currentUserId) TextInput( state = state, subcomposing = subcomposing, @@ -153,6 +160,8 @@ fun TextComposer( }, composerMode = composerMode, onResetComposerMode = onResetComposerMode, + resolveMentionDisplay = { text, url -> TextDisplay.Custom(mentionSpanProvider.getMentionSpanFor(text, url)) }, + resolveRoomMentionDisplay = { TextDisplay.Custom(mentionSpanProvider.getMentionSpanFor("@room", "#")) }, onError = onError, ) } @@ -385,6 +394,8 @@ private fun TextInput( placeholder: String, composerMode: MessageComposerMode, onResetComposerMode: () -> Unit, + resolveRoomMentionDisplay: () -> TextDisplay, + resolveMentionDisplay: (text: String, url: String) -> TextDisplay, modifier: Modifier = Modifier, onError: (Throwable) -> Unit = {}, ) { @@ -432,7 +443,11 @@ private fun TextInput( .fillMaxWidth(), style = ElementRichTextEditorStyle.create( hasFocus = state.hasFocus + ).copy( + pill = PillStyle(Color.Red) ), + resolveMentionDisplay = resolveMentionDisplay, + resolveRoomMentionDisplay = resolveRoomMentionDisplay, onError = onError ) } @@ -584,6 +599,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost"), ) }, { TextComposer( @@ -594,6 +610,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -607,6 +624,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -617,6 +635,7 @@ internal fun TextComposerSimplePreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }) ) @@ -633,6 +652,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { composerMode = MessageComposerMode.Normal, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -642,6 +662,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { composerMode = MessageComposerMode.Normal, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -651,6 +672,7 @@ internal fun TextComposerFormattingPreview() = ElementPreview { composerMode = MessageComposerMode.Normal, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) })) } @@ -667,6 +689,7 @@ internal fun TextComposerEditPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) })) } @@ -691,6 +714,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { @@ -710,6 +734,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -731,6 +756,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -752,6 +778,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -773,6 +800,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }, { TextComposer( @@ -794,6 +822,7 @@ internal fun TextComposerReplyPreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) }) ) @@ -813,6 +842,7 @@ internal fun TextComposerVoicePreview() = ElementPreview { onResetComposerMode = {}, enableTextFormatting = true, enableVoiceMessages = true, + currentUserId = UserId("@alice:localhost") ) PreviewColumn(items = persistentListOf({ VoicePreview(voiceMessageState = VoiceMessageState.Recording(61.seconds, createFakeWaveform())) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt new file mode 100644 index 0000000000..609a050abf --- /dev/null +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpan.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.mentions + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import android.text.style.ReplacementSpan +import kotlin.math.roundToInt + +class MentionSpan( + val backgroundColor: Int, + val textColor: Int, +) : ReplacementSpan() { + + override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int { + return paint.measureText(text, start, end).roundToInt() + 40 + } + + override fun draw(canvas: Canvas, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { + val textSize = paint.measureText(text, start, end) + val rect = RectF(x, top.toFloat(), x + textSize + 40, bottom.toFloat()) + paint.color = backgroundColor + canvas.drawRoundRect(rect, rect.height() / 2, rect.height() / 2, paint) + paint.color = textColor + canvas.drawText(text!!, start, end, x + 20, y.toFloat(), paint) + } +} diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt new file mode 100644 index 0000000000..77674e1254 --- /dev/null +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/mentions/MentionSpanProvider.kt @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.mentions + +import android.graphics.Color +import android.view.ViewGroup +import android.widget.TextView +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.text.buildSpannedString +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.currentUserMentionPillBackground +import io.element.android.libraries.designsystem.theme.currentUserMentionPillText +import io.element.android.libraries.designsystem.theme.mentionPillBackground +import io.element.android.libraries.designsystem.theme.mentionPillText +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.permalink.PermalinkData +import io.element.android.libraries.matrix.api.permalink.PermalinkParser +import io.element.android.libraries.theme.ElementTheme + +@Stable +class MentionSpanProvider( + private val currentSessionId: SessionId, + private var currentUserTextColor: Int = 0, + private var currentUserBackgroundColor: Int = Color.WHITE, + private var otherTextColor: Int = 0, + private var otherBackgroundColor: Int = Color.WHITE, +) { + + @Suppress("ComposableNaming") + @Composable + internal fun setup() { + currentUserTextColor = ElementTheme.colors.currentUserMentionPillText.toArgb() + currentUserBackgroundColor = ElementTheme.colors.currentUserMentionPillBackground.toArgb() + otherTextColor = ElementTheme.colors.mentionPillText.toArgb() + otherBackgroundColor = ElementTheme.colors.mentionPillBackground.toArgb() + } + + fun getMentionSpanFor(text: String, url: String): MentionSpan { + val permalinkData = PermalinkParser.parse(url) + return when { + permalinkData is PermalinkData.UserLink -> { + val isCurrentUser = permalinkData.userId == currentSessionId.value + MentionSpan( + backgroundColor = if (isCurrentUser) currentUserBackgroundColor else otherBackgroundColor, + textColor = if (isCurrentUser) currentUserTextColor else otherTextColor, + ) + } + text == "@room" && permalinkData is PermalinkData.FallbackLink -> { + MentionSpan( + backgroundColor = otherBackgroundColor, + textColor = otherTextColor, + ) + } + else -> { + MentionSpan( + backgroundColor = otherBackgroundColor, + textColor = otherTextColor, + ) + } + } + } +} + +@Composable +fun rememberMentionSpanProvider(currentUserId: SessionId): MentionSpanProvider { + val provider = remember(currentUserId) { + MentionSpanProvider(currentUserId) + } + provider.setup() + return provider +} + +@PreviewsDayNight +@Composable +internal fun MentionSpanPreview() { + val provider = rememberMentionSpanProvider(SessionId("@me:matrix.org")) + ElementPreview { + provider.setup() + + val textColor = ElementTheme.colors.textPrimary.toArgb() + val mentionSpan = provider.getMentionSpanFor("me", "https://matrix.to/#/@me:matrix.org") + val mentionSpan2 = provider.getMentionSpanFor("other", "https://matrix.to/#/@other:matrix.org") + AndroidView(factory = { context -> + TextView(context).apply { + layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + text = buildSpannedString { + append("This is a ") + append("@mention", mentionSpan, 0) + append(" to the current user and this is a ") + append("@mention", mentionSpan2, 0) + append(" to other user") + } + setTextColor(textColor) + } + }) + } +} diff --git a/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt new file mode 100644 index 0000000000..748a06bb2c --- /dev/null +++ b/libraries/textcomposer/impl/src/test/kotlin/io/element/android/libraries/textcomposer/impl/mentions/MentionSpanProviderTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.textcomposer.impl.mentions + +import android.graphics.Color +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider +import io.element.android.tests.testutils.WarmUpRule +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class MentionSpanProviderTest { + + @JvmField @Rule + val warmUpRule = WarmUpRule() + + private val myUserColor = Color.RED + private val otherColor = Color.BLUE + private val currentUserId = A_SESSION_ID + + private val mentionSpanProvider = MentionSpanProvider( + currentSessionId = currentUserId, + currentUserBackgroundColor = myUserColor, + currentUserTextColor = myUserColor, + otherBackgroundColor = otherColor, + otherTextColor = otherColor, + ) + + @Test + fun `getting mention span for current user should return a MentionSpan with custom colors`() { + val mentionSpan = mentionSpanProvider.getMentionSpanFor("me", "https://matrix.to/#/${currentUserId.value}") + assertThat(mentionSpan.backgroundColor).isEqualTo(myUserColor) + assertThat(mentionSpan.textColor).isEqualTo(myUserColor) + } + + @Test + fun `getting mention span for other user should return a MentionSpan with normal colors`() { + val mentionSpan = mentionSpanProvider.getMentionSpanFor("other", "https://matrix.to/#/@other:matrix.org") + assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) + assertThat(mentionSpan.textColor).isEqualTo(otherColor) + } + + @Test + fun `getting mention span for @room should return a MentionSpan with normal colors`() { + val mentionSpan = mentionSpanProvider.getMentionSpanFor("@room", "#") + assertThat(mentionSpan.backgroundColor).isEqualTo(otherColor) + assertThat(mentionSpan.textColor).isEqualTo(otherColor) + } +} diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..87bfdadf60 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Day-18_18_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a70e858c4935324773d885cdc80268550af9ded8bfa2c7e5e6b916f2d4b85f4b +size 18110 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2b93b20917 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer.mentions_MentionSpan_null_MentionSpan-Night-18_19_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c0a05c8f8245af9d46da170ce3062f20c10b77f7b64be1432747a14b98c5c0e +size 16198 From 68f9c816283cb43ec050bab59eecb322cd69b4bc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 21 Nov 2023 11:55:16 +0100 Subject: [PATCH 093/102] Let the user choose theme (#1499) --- .../io/element/android/x/MainActivity.kt | 14 ++++- .../io/element/android/x/di/AppBindings.kt | 2 + .../features/call/ui/ElementCallActivity.kt | 16 +++++- .../impl/advanced/AdvancedSettingsEvents.kt | 5 ++ .../advanced/AdvancedSettingsPresenter.kt | 19 ++++++- .../impl/advanced/AdvancedSettingsState.kt | 4 ++ .../advanced/AdvancedSettingsStateProvider.kt | 5 ++ .../impl/advanced/AdvancedSettingsView.kt | 55 +++++++++++++++++++ .../advanced/AdvancedSettingsPresenterTest.kt | 27 +++++++++ .../preferences/api/store/PreferencesStore.kt | 3 + .../impl/store/DefaultPreferencesStore.kt | 13 +++++ .../test/InMemoryPreferencesStore.kt | 10 ++++ .../android/libraries/theme/theme/Theme.kt | 46 ++++++++++++++++ 13 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index 29f2d76527..c8e16f708e 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -25,6 +25,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen @@ -38,6 +41,9 @@ import io.element.android.libraries.architecture.bindings import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.theme.theme.Theme +import io.element.android.libraries.theme.theme.isDark +import io.element.android.libraries.theme.theme.mapToTheme import io.element.android.x.di.AppBindings import io.element.android.x.intent.SafeUriHandler import timber.log.Timber @@ -77,7 +83,13 @@ class MainActivity : NodeActivity() { @Composable private fun MainContent(appBindings: AppBindings) { - ElementTheme { + val theme by remember { + appBindings.preferencesStore().getThemeFlow().mapToTheme() + } + .collectAsState(initial = Theme.System) + ElementTheme( + darkTheme = theme.isDark() + ) { CompositionLocalProvider( LocalSnackbarDispatcher provides appBindings.snackbarDispatcher(), LocalUriHandler provides SafeUriHandler(this), diff --git a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt index 512b5093d0..96439ff973 100644 --- a/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt +++ b/app/src/main/kotlin/io/element/android/x/di/AppBindings.kt @@ -18,6 +18,7 @@ package io.element.android.x.di import com.squareup.anvil.annotations.ContributesTo import io.element.android.features.lockscreen.api.LockScreenService +import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.di.AppScope @@ -29,4 +30,5 @@ interface AppBindings { fun tracingService(): TracingService fun bugReporter(): BugReporter fun lockScreenService(): LockScreenService + fun preferencesStore(): PreferencesStore } diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt b/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt index 651a2176f3..54a41ea31d 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/ui/ElementCallActivity.kt @@ -31,15 +31,22 @@ import android.webkit.PermissionRequest import androidx.activity.compose.setContent import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.core.content.IntentCompat import com.bumble.appyx.core.integrationpoint.NodeComponentActivity import io.element.android.features.call.CallForegroundService import io.element.android.features.call.CallType import io.element.android.features.call.di.CallBindings import io.element.android.features.call.utils.CallIntentDataParser +import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.libraries.architecture.bindings import io.element.android.libraries.theme.ElementTheme +import io.element.android.libraries.theme.theme.Theme +import io.element.android.libraries.theme.theme.isDark +import io.element.android.libraries.theme.theme.mapToTheme import javax.inject.Inject class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator { @@ -60,6 +67,7 @@ class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator { @Inject lateinit var callIntentDataParser: CallIntentDataParser @Inject lateinit var presenterFactory: CallScreenPresenter.Factory + @Inject lateinit var preferencesStore: PreferencesStore private lateinit var presenter: CallScreenPresenter @@ -92,8 +100,14 @@ class ElementCallActivity : NodeComponentActivity(), CallScreenNavigator { requestAudioFocus() setContent { + val theme by remember { + preferencesStore.getThemeFlow().mapToTheme() + } + .collectAsState(initial = Theme.System) val state = presenter.present() - ElementTheme { + ElementTheme( + darkTheme = theme.isDark() + ) { CallScreenView( state = state, requestPermissions = { permissions, callback -> diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt index 37641d684c..f79c53335c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsEvents.kt @@ -16,7 +16,12 @@ package io.element.android.features.preferences.impl.advanced +import io.element.android.libraries.theme.theme.Theme + sealed interface AdvancedSettingsEvents { data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents + data object ChangeTheme : AdvancedSettingsEvents + data object CancelChangeTheme : AdvancedSettingsEvents + data class SetTheme(val theme: Theme) : AdvancedSettingsEvents } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt index 4b6b0354b6..bf438db4ef 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenter.kt @@ -19,9 +19,14 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import io.element.android.features.preferences.api.store.PreferencesStore import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.theme.theme.Theme +import io.element.android.libraries.theme.theme.mapToTheme import kotlinx.coroutines.launch import javax.inject.Inject @@ -38,7 +43,11 @@ class AdvancedSettingsPresenter @Inject constructor( val isDeveloperModeEnabled by preferencesStore .isDeveloperModeEnabledFlow() .collectAsState(initial = false) - + val theme by remember { + preferencesStore.getThemeFlow().mapToTheme() + } + .collectAsState(initial = Theme.System) + var showChangeThemeDialog by remember { mutableStateOf(false) } fun handleEvents(event: AdvancedSettingsEvents) { when (event) { is AdvancedSettingsEvents.SetRichTextEditorEnabled -> localCoroutineScope.launch { @@ -47,12 +56,20 @@ class AdvancedSettingsPresenter @Inject constructor( is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch { preferencesStore.setDeveloperModeEnabled(event.enabled) } + AdvancedSettingsEvents.CancelChangeTheme -> showChangeThemeDialog = false + AdvancedSettingsEvents.ChangeTheme -> showChangeThemeDialog = true + is AdvancedSettingsEvents.SetTheme -> localCoroutineScope.launch { + preferencesStore.setTheme(event.theme.name) + showChangeThemeDialog = false + } } } return AdvancedSettingsState( isRichTextEditorEnabled = isRichTextEditorEnabled, isDeveloperModeEnabled = isDeveloperModeEnabled, + theme = theme, + showChangeThemeDialog = showChangeThemeDialog, eventSink = { handleEvents(it) } ) } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt index 899eac2744..8258684750 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsState.kt @@ -16,8 +16,12 @@ package io.element.android.features.preferences.impl.advanced +import io.element.android.libraries.theme.theme.Theme + data class AdvancedSettingsState( val isRichTextEditorEnabled: Boolean, val isDeveloperModeEnabled: Boolean, + val theme: Theme, + val showChangeThemeDialog: Boolean, val eventSink: (AdvancedSettingsEvents) -> Unit ) diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt index 5ab50c8a16..012a39896c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.preferences.impl.advanced import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.theme.theme.Theme open class AdvancedSettingsStateProvider : PreviewParameterProvider { override val values: Sequence @@ -24,14 +25,18 @@ open class AdvancedSettingsStateProvider : PreviewParameterProvider { + return themes.map { + ListOption(title = it.toHumanReadable()) + }.toImmutableList() +} + +@Composable +private fun Theme.toHumanReadable(): String { + return stringResource( + id = when (this) { + Theme.System -> CommonStrings.common_system + Theme.Dark -> CommonStrings.common_dark + Theme.Light -> CommonStrings.common_light + } + ) } @PreviewsDayNight diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt index 1ea04c11dd..00ea3bbf14 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/advanced/AdvancedSettingsPresenterTest.kt @@ -21,6 +21,7 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.featureflag.test.InMemoryPreferencesStore +import io.element.android.libraries.theme.theme.Theme import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem import kotlinx.coroutines.test.runTest @@ -42,6 +43,8 @@ class AdvancedSettingsPresenterTest { val initialState = awaitLastSequentialItem() assertThat(initialState.isDeveloperModeEnabled).isFalse() assertThat(initialState.isRichTextEditorEnabled).isFalse() + assertThat(initialState.showChangeThemeDialog).isFalse() + assertThat(initialState.theme).isEqualTo(Theme.System) } } @@ -76,4 +79,28 @@ class AdvancedSettingsPresenterTest { assertThat(awaitItem().isRichTextEditorEnabled).isFalse() } } + + @Test + fun `present - change theme`() = runTest { + val store = InMemoryPreferencesStore() + val presenter = AdvancedSettingsPresenter(store) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + val initialState = awaitLastSequentialItem() + initialState.eventSink.invoke(AdvancedSettingsEvents.ChangeTheme) + val withDialog = awaitItem() + assertThat(withDialog.showChangeThemeDialog).isTrue() + // Cancel + withDialog.eventSink(AdvancedSettingsEvents.CancelChangeTheme) + val withoutDialog = awaitItem() + assertThat(withoutDialog.showChangeThemeDialog).isFalse() + withDialog.eventSink.invoke(AdvancedSettingsEvents.ChangeTheme) + assertThat(awaitItem().showChangeThemeDialog).isTrue() + withDialog.eventSink(AdvancedSettingsEvents.SetTheme(Theme.Light)) + val withNewTheme = awaitItem() + assertThat(withNewTheme.showChangeThemeDialog).isFalse() + assertThat(withNewTheme.theme).isEqualTo(Theme.Light) + } + } } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/PreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/PreferencesStore.kt index d62fb7e6cf..2bd1fc6064 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/PreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/features/preferences/api/store/PreferencesStore.kt @@ -28,5 +28,8 @@ interface PreferencesStore { suspend fun setCustomElementCallBaseUrl(string: String?) fun getCustomElementCallBaseUrlFlow(): Flow + suspend fun setTheme(theme: String) + fun getThemeFlow(): Flow + suspend fun reset() } diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultPreferencesStore.kt index 66a46d1ca3..d00b7505d7 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultPreferencesStore.kt @@ -39,6 +39,7 @@ private val Context.dataStore: DataStore by preferencesDataStore(na private val richTextEditorKey = booleanPreferencesKey("richTextEditor") private val developerModeKey = booleanPreferencesKey("developerMode") private val customElementCallBaseUrlKey = stringPreferencesKey("elementCallBaseUrl") +private val themeKey = stringPreferencesKey("theme") @ContributesBinding(AppScope::class) class DefaultPreferencesStore @Inject constructor( @@ -89,6 +90,18 @@ class DefaultPreferencesStore @Inject constructor( } } + override suspend fun setTheme(theme: String) { + store.edit { prefs -> + prefs[themeKey] = theme + } + } + + override fun getThemeFlow(): Flow { + return store.data.map { prefs -> + prefs[themeKey] + } + } + override suspend fun reset() { store.edit { it.clear() } } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemoryPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemoryPreferencesStore.kt index 6dea8910ed..c4a26215c2 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemoryPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/featureflag/test/InMemoryPreferencesStore.kt @@ -24,10 +24,12 @@ class InMemoryPreferencesStore( isRichTextEditorEnabled: Boolean = false, isDeveloperModeEnabled: Boolean = false, customElementCallBaseUrl: String? = null, + theme: String? = null, ) : PreferencesStore { private var _isRichTextEditorEnabled = MutableStateFlow(isRichTextEditorEnabled) private var _isDeveloperModeEnabled = MutableStateFlow(isDeveloperModeEnabled) private var _customElementCallBaseUrl = MutableStateFlow(customElementCallBaseUrl) + private var _theme = MutableStateFlow(theme) override suspend fun setRichTextEditorEnabled(enabled: Boolean) { _isRichTextEditorEnabled.value = enabled @@ -53,6 +55,14 @@ class InMemoryPreferencesStore( return _customElementCallBaseUrl } + override suspend fun setTheme(theme: String) { + _theme.value = theme + } + + override fun getThemeFlow(): Flow { + return _theme + } + override suspend fun reset() { // No op } diff --git a/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt new file mode 100644 index 0000000000..2d77a66c98 --- /dev/null +++ b/libraries/theme/src/main/kotlin/io/element/android/libraries/theme/theme/Theme.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.theme.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +enum class Theme { + System, + Dark, + Light; +} + +val themes = listOf(Theme.System, Theme.Dark, Theme.Light) + +@Composable +fun Theme.isDark(): Boolean { + return when (this) { + Theme.System -> isSystemInDarkTheme() + Theme.Dark -> true + Theme.Light -> false + } +} + +fun Flow.mapToTheme(): Flow = map { + when (it) { + null -> Theme.System + else -> Theme.valueOf(it) + } +} From 5e95da991137db2d6a49028fad38470f81c3fea1 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 21 Nov 2023 11:21:43 +0000 Subject: [PATCH 094/102] Update screenshots --- ...ll_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ll_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png | 3 +++ ..._AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png | 4 ++-- ..._AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png | 3 +++ 8 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png index 87469e8d76..a8b6a93bdd 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20ed65388204495106f428d0445c3091bac5b953e46931d21d5ee73c5ae876cc -size 37889 +oid sha256:d6bc45093c8a96a5446c18b42590d6b866a40ea34268e4764a29505dcf97965c +size 42565 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png index 4588e60de3..05647b9c50 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7bd91af47b7b336b5bd1abf52ebfcd588d29cb8b0bbb67115fbeaac8ba5bb469 -size 37473 +oid sha256:290bb5d6134171c6d3f343ee3bad95d244175fd18696c5c27a526b9d61948d5f +size 42232 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png index d9d2d0a9f5..e8a91dae18 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40bddbf21b6ca1d3f2647ff5168bd7b620b36d5cf3c88f15bab148963c3426b7 -size 37530 +oid sha256:a9ee0f94d9067eda01bc4b93bd5bf5c7493d0f6790bd5c1d1d542ed029e0a07d +size 42149 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..33ebf89295 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Day-1_1_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd3770364cdfb8804008c0cd86129e689f239a4685ec4690b2d8f65f73b57ac4 +size 36621 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png index 11451941bf..ffd6bc821b 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21e918bf1bbc6f024099d7bb8a1f38eaa356a50a244a9348411489a678579afa -size 35444 +oid sha256:917073ea5c55d001279b622a268a61ce2b56b94d396c99802f92013b6ddbb71c +size 39924 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png index d9300615dc..bf66ea4700 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c051bfc9f9eafd112ccc60eb77b7ffbc9d91c493f2b588fbf51476c2be39ab1a -size 35069 +oid sha256:e6e41d8672db7f6b89f3db5e174569c9ccec381e0f3c34ab410a85768234764f +size 39683 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png index fe0e7dfff1..0fee0836a4 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:943800b5c6fbdda0c39569ab44375ec2e4487c54644663fc21417b077ce5b8e3 -size 35181 +oid sha256:577f6db9a8a987a685b3596644b0cae992694662aee10b5e64b4c748e21e1a5c +size 39647 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f194912300 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[f.preferences.impl.advanced_AdvancedSettingsView_null_AdvancedSettingsView-Night-1_2_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d14440500ffac475aa57cedbc3de507f182cb1e79b0b93b4f4b9b90c3c5c13a5 +size 32136 From bb51862fc4b72672457075beff2850506fe90738 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:14:21 +0000 Subject: [PATCH 095/102] Update mobile-dev-inc/action-maestro-cloud action to v1.8.0 --- .github/workflows/maestro.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maestro.yml b/.github/workflows/maestro.yml index 1b5d43a13e..ad13d5ecc7 100644 --- a/.github/workflows/maestro.yml +++ b/.github/workflows/maestro.yml @@ -40,7 +40,7 @@ jobs: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }} ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }} - - uses: mobile-dev-inc/action-maestro-cloud@v1.7.0 + - uses: mobile-dev-inc/action-maestro-cloud@v1.8.0 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} # Doc says (https://github.com/mobile-dev-inc/action-maestro-cloud#android): From a5fd082a3b65817d75fde7b1cdbb5ff9ba013516 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:45:34 +0000 Subject: [PATCH 096/102] Update dependency androidx.compose.material3:material3 to v1.2.0-alpha11 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 79dc0222ac..7f13c696e8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -89,7 +89,7 @@ androidx_preference = "androidx.preference:preference:1.2.1" androidx_webkit = "androidx.webkit:webkit:1.8.0" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } -androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha10" +androidx_compose_material3 = "androidx.compose.material3:material3:1.2.0-alpha11" androidx_compose_ui = { module = "androidx.compose.ui:ui" } androidx_compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } androidx_compose_ui_tooling_preview = { module = "androidx.compose.ui:ui-tooling-preview" } From 830bad2bcf0422c8a92dc3e6ac9e28f66966a3b9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 21 Nov 2023 16:13:07 +0100 Subject: [PATCH 097/102] Fix compilation warning, add opt in for ExperimentalMaterial3Api --- .../impl/timeline/components/customreaction/EmojiPicker.kt | 3 ++- .../libraries/designsystem/components/tooltip/TooltipBox.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt index 29d6b14e59..02ddfec026 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/customreaction/EmojiPicker.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SecondaryTabRow import androidx.compose.material3.Tab import androidx.compose.runtime.Composable @@ -47,7 +48,7 @@ import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.launch -@OptIn(ExperimentalFoundationApi::class) +@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun EmojiPicker( onEmojiSelected: (Emoji) -> Unit, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/TooltipBox.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/TooltipBox.kt index 589f1fddcd..fc11758104 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/TooltipBox.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/tooltip/TooltipBox.kt @@ -16,12 +16,14 @@ package io.element.android.libraries.designsystem.components.tooltip +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.TooltipState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.window.PopupPositionProvider import androidx.compose.material3.TooltipBox as M3TooltipBox +@OptIn(ExperimentalMaterial3Api::class) @Composable fun TooltipBox( positionProvider: PopupPositionProvider, From 637f788d40c9a0a16123b1acbeda85e1569f56d2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 21 Nov 2023 17:22:53 +0100 Subject: [PATCH 098/102] Replace Alertdialog (deprecated) by BasicAlertDialog. --- .../designsystem/components/dialogs/ConfirmationDialog.kt | 4 ++-- .../libraries/designsystem/components/dialogs/ErrorDialog.kt | 4 ++-- .../libraries/designsystem/components/dialogs/ListDialog.kt | 4 ++-- .../components/dialogs/MultipleSelectionDialog.kt | 4 ++-- .../libraries/designsystem/components/dialogs/RetryDialog.kt | 4 ++-- .../designsystem/components/dialogs/SingleSelectionDialog.kt | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt index 801575fe56..aed618cd3b 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ConfirmationDialog.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.designsystem.components.dialogs -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -43,7 +43,7 @@ fun ConfirmationDialog( onCancelClicked: () -> Unit = onDismiss, onThirdButtonClicked: () -> Unit = {}, ) { - AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { ConfirmationDialogContent( title = title, content = content, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt index 9b080f5403..52779052a6 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ErrorDialog.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.designsystem.components.dialogs -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -37,7 +37,7 @@ fun ErrorDialog( title: String = ErrorDialogDefaults.title, submitText: String = ErrorDialogDefaults.submitText, ) { - AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { ErrorDialogContent( title = title, content = content, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt index 5ad13d6a13..83079584c3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/ListDialog.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -56,7 +56,7 @@ fun ListDialog( ) } } - AlertDialog( + BasicAlertDialog( modifier = modifier, onDismissRequest = onDismissRequest, ) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt index 5ed79f2c35..055843edae 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/MultipleSelectionDialog.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -60,7 +60,7 @@ fun MultipleSelectionDialog( ) } } - AlertDialog( + BasicAlertDialog( modifier = modifier, onDismissRequest = onDismissRequest, ) { diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt index 85447b940b..1837041e64 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/RetryDialog.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.designsystem.components.dialogs -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -39,7 +39,7 @@ fun RetryDialog( onRetry: () -> Unit = {}, onDismiss: () -> Unit = {}, ) { - AlertDialog(modifier = modifier, onDismissRequest = onDismiss) { + BasicAlertDialog(modifier = modifier, onDismissRequest = onDismiss) { RetryDialogContent( title = title, content = content, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt index 82b88bc0a1..36869bf270 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/dialogs/SingleSelectionDialog.kt @@ -19,7 +19,7 @@ package io.element.android.libraries.designsystem.components.dialogs import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.AlertDialog +import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -57,7 +57,7 @@ fun SingleSelectionDialog( ) } } - AlertDialog( + BasicAlertDialog( modifier = modifier, onDismissRequest = onDismissRequest, ) { From 97e9528e13986f037d3cd759f55a9a4fd75623b2 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Tue, 21 Nov 2023 17:34:00 +0100 Subject: [PATCH 099/102] Add intentional mentions (#1843) * Add intentional mentions --- changelog.d/1591.feature | 1 + .../MessageComposerPresenter.kt | 16 ++++- .../MessageComposerPresenterTest.kt | 66 ++++++++++++++++++- .../libraries/matrix/api/room/MatrixRoom.kt | 6 +- .../libraries/matrix/api/room/Mention.kt | 22 +++++++ .../libraries/matrix/impl/room/Mention.kt | 26 ++++++++ .../matrix/impl/room/RustMatrixRoom.kt | 19 ++++-- .../matrix/test/room/FakeMatrixRoom.kt | 17 ++++- libraries/textcomposer/impl/build.gradle.kts | 2 +- 9 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 changelog.d/1591.feature create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt diff --git a/changelog.d/1591.feature b/changelog.d/1591.feature new file mode 100644 index 0000000000..54f1402cf8 --- /dev/null +++ b/changelog.d/1591.feature @@ -0,0 +1 @@ +Add intentional mentions to messages. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 3a952efb69..dd2537ff18 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -47,6 +47,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaSender @@ -327,15 +328,25 @@ class MessageComposerPresenter @Inject constructor( richTextEditorState: RichTextEditorState, ) = launch { val capturedMode = messageComposerContext.composerMode + val mentions = richTextEditorState.mentionsState?.let { state -> + buildList { + if (state.hasAtRoomMention) { + add(Mention.AtRoom) + } + for (userId in state.userIds) { + add(Mention.User(userId)) + } + } + }.orEmpty() // Reset composer right away richTextEditorState.setHtml("") updateComposerMode(MessageComposerMode.Normal) when (capturedMode) { - is MessageComposerMode.Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html) + is MessageComposerMode.Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html, mentions = mentions) is MessageComposerMode.Edit -> { val eventId = capturedMode.eventId val transactionId = capturedMode.transactionId - room.editMessage(eventId, transactionId, message.markdown, message.html) + room.editMessage(eventId, transactionId, message.markdown, message.html, mentions) } is MessageComposerMode.Quote -> TODO() @@ -343,6 +354,7 @@ class MessageComposerPresenter @Inject constructor( capturedMode.eventId, message.markdown, message.html, + mentions ) } analyticsService.capture( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index ef58792552..a48f55d120 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -26,12 +26,12 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer +import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.impl.messagecomposer.AttachmentsState import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.messagecomposer.MessageComposerState -import io.element.android.features.messages.impl.mentions.MentionSuggestion import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher @@ -44,6 +44,7 @@ import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE @@ -79,10 +80,12 @@ import io.element.android.tests.testutils.waitForPredicate import io.mockk.mockk import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import okhttp3.internal.immutableListOf import org.junit.Rule import org.junit.Test +import uniffi.wysiwyg_composer.MentionsState import java.io.File @Suppress("LargeClass") @@ -835,6 +838,67 @@ class MessageComposerPresenterTest { } } + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `present - send messages with intentional mentions`() = runTest { + val room = FakeMatrixRoom() + val presenter = createPresenter(room = room, coroutineScope = this) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(1) + val initialState = awaitItem() + + // Check intentional mentions on message sent + val mentionUser1 = listOf(A_USER_ID.value) + initialState.richTextEditorState.mentionsState = MentionsState( + userIds = mentionUser1, + roomIds = emptyList(), + roomAliases = emptyList(), + hasAtRoomMention = false + ) + initialState.richTextEditorState.setHtml(A_MESSAGE) + initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage())) + + advanceUntilIdle() + + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID.value))) + + // Check intentional mentions on reply sent + initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode())) + val mentionUser2 = listOf(A_USER_ID_2.value) + awaitItem().richTextEditorState.mentionsState = MentionsState( + userIds = mentionUser2, + roomIds = emptyList(), + roomAliases = emptyList(), + hasAtRoomMention = false + ) + + initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage())) + advanceUntilIdle() + + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2.value))) + + // Check intentional mentions on edit message + skipItems(1) + initialState.eventSink(MessageComposerEvents.SetMode(anEditMode())) + val mentionUser3 = listOf(A_USER_ID_3.value) + awaitItem().richTextEditorState.mentionsState = MentionsState( + userIds = mentionUser3, + roomIds = emptyList(), + roomAliases = emptyList(), + hasAtRoomMention = false + ) + + initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage())) + advanceUntilIdle() + + assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3.value))) + + skipItems(1) + } + } + private suspend fun ReceiveTurbine.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState { state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode) skipItems(skipCount) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 1f2de7720f..e9ad35dafa 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -90,13 +90,13 @@ interface MatrixRoom : Closeable { suspend fun userAvatarUrl(userId: UserId): Result - suspend fun sendMessage(body: String, htmlBody: String?): Result + suspend fun sendMessage(body: String, htmlBody: String?, mentions: List): Result - suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result + suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List): Result suspend fun enterSpecialMode(eventId: EventId?): Result - suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result + suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List): Result suspend fun redactEvent(eventId: EventId, reason: String? = null): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt new file mode 100644 index 0000000000..30aba3c3c0 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/Mention.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.api.room + +sealed interface Mention { + data class User(val userId: String): Mention + data object AtRoom: Mention +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt new file mode 100644 index 0000000000..463496ffe7 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/Mention.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 io.element.android.libraries.matrix.impl.room + +import io.element.android.libraries.matrix.api.room.Mention +import org.matrix.rustcomponents.sdk.Mentions + +fun List.map(): Mentions { + val hasAtRoom = any { it is Mention.AtRoom } + val userIds = filterIsInstance().map { it.userId } + return Mentions(userIds, hasAtRoom) +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index ab7317590e..35a61c1a1d 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState +import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.location.AssetType @@ -250,22 +251,28 @@ class RustMatrixRoom( } } - override suspend fun sendMessage(body: String, htmlBody: String?): Result = withContext(roomDispatcher) { - messageEventContentFromParts(body, htmlBody).use { content -> + override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List): Result = withContext(roomDispatcher) { + messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content -> runCatching { innerRoom.send(content) } } } - override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result = + override suspend fun editMessage( + originalEventId: EventId?, + transactionId: TransactionId?, + body: String, + htmlBody: String?, + mentions: List, + ): Result = withContext(roomDispatcher) { if (originalEventId != null) { runCatching { val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value) editedEvent.use { innerRoom.edit( - newContent = messageEventContentFromParts(body, htmlBody), + newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), editItem = it, ) } @@ -289,11 +296,11 @@ class RustMatrixRoom( } } - override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result = withContext(roomDispatcher) { + override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List): Result = withContext(roomDispatcher) { runCatching { val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value) inReplyTo.use { eventTimelineItem -> - innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventTimelineItem) + innerRoom.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem) } specialModeEventTimelineItem = null } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index 244a769895..a041263e2c 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState +import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.room.MessageEventType import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomNotificationMode @@ -108,6 +109,7 @@ class FakeMatrixRoom( private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io") private var getWidgetDriverResult: Result = Result.success(FakeWidgetDriver()) private var canUserTriggerRoomNotificationResult: Result = Result.success(true) + var sendMessageMentions = emptyList() val editMessageCalls = mutableListOf>() var sendMediaCount = 0 @@ -190,7 +192,8 @@ class FakeMatrixRoom( userAvatarUrlResult } - override suspend fun sendMessage(body: String, htmlBody: String?) = simulateLongTask { + override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List) = simulateLongTask { + sendMessageMentions = mentions Result.success(Unit) } @@ -219,7 +222,14 @@ class FakeMatrixRoom( return cancelSendResult } - override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result { + override suspend fun editMessage( + originalEventId: EventId?, + transactionId: TransactionId?, + body: String, + htmlBody: String?, + mentions: List + ): Result { + sendMessageMentions = mentions editMessageCalls += body to htmlBody return Result.success(Unit) } @@ -231,7 +241,8 @@ class FakeMatrixRoom( return Result.success(Unit) } - override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result { + override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List): Result { + sendMessageMentions = mentions replyMessageParameter = body to htmlBody return Result.success(Unit) } diff --git a/libraries/textcomposer/impl/build.gradle.kts b/libraries/textcomposer/impl/build.gradle.kts index 99f910d317..6cce2b855d 100644 --- a/libraries/textcomposer/impl/build.gradle.kts +++ b/libraries/textcomposer/impl/build.gradle.kts @@ -34,7 +34,7 @@ dependencies { implementation(projects.libraries.testtags) implementation(projects.libraries.uiUtils) - implementation(libs.matrix.richtexteditor) + api(libs.matrix.richtexteditor) api(libs.matrix.richtexteditor.compose) ksp(libs.showkase.processor) From de646e4e5af57b9a326ae03908eee129a08785cb Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Tue, 21 Nov 2023 20:48:08 +0100 Subject: [PATCH 100/102] Voice message scrubbing improvements (#1847) - Voice messages can be scrubbed (i.e. seeked to) even when they have not been played yet.. - The progress bar is displayed also when paused. - Multiple voice messages can keep their state when paused. - Tries to adhere as much as possible at the detailed "green cursor" behavior in the story (but might not be 100% compliant). Story: https://github.com/vector-im/element-meta/issues/2113 --- .../components/event/TimelineItemVoiceView.kt | 2 +- .../timeline/VoiceMessagePlayer.kt | 138 ++++++--- .../timeline/VoiceMessagePresenter.kt | 51 ++-- .../timeline/VoiceMessageState.kt | 1 + .../timeline/VoiceMessageStateProvider.kt | 4 + .../timeline/DefaultVoiceMessagePlayerTest.kt | 262 +++++++++++++----- .../timeline/VoiceMessagePresenterTest.kt | 64 +---- .../libraries/mediaplayer/api/MediaPlayer.kt | 5 + .../mediaplayer/impl/MediaPlayerImpl.kt | 16 +- .../mediaplayer/impl/SimplePlayer.kt | 4 +- .../mediaplayer/test/FakeMediaPlayer.kt | 42 ++- 11 files changed, 381 insertions(+), 208 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt index e1494b9da0..6d3dd57994 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVoiceView.kt @@ -96,7 +96,7 @@ fun TimelineItemVoiceView( Spacer(Modifier.width(8.dp)) val context = LocalContext.current WaveformPlaybackView( - showCursor = state.button == VoiceMessageState.Button.Pause, + showCursor = state.showCursor, playbackProgress = state.progress, waveform = content.waveform, modifier = Modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt index 57607507c9..a6f002c38a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePlayer.kt @@ -22,8 +22,11 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.mediaplayer.api.MediaPlayer import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update +import java.io.File import javax.inject.Inject /** @@ -60,14 +63,18 @@ interface VoiceMessagePlayer { val state: Flow /** - * Starts playing from the beginning - * acquiring control of the underlying [MediaPlayer]. - * If already in control of the underlying [MediaPlayer], starts playing from the - * current position. + * Acquires control of the underlying [MediaPlayer] and prepares it + * to play the media file. * - * Will suspend whilst the media file is being downloaded. + * Will suspend whilst the media file is being downloaded and/or + * the underlying [MediaPlayer] is loading the media file. */ - suspend fun play(): Result + suspend fun prepare(): Result + + /** + * Play the media. + */ + fun play() /** * Pause playback. @@ -75,28 +82,33 @@ interface VoiceMessagePlayer { fun pause() /** - * Seek to a specific position acquiring control of the - * underlying [MediaPlayer] if needed. - * - * Will suspend whilst the media file is being downloaded. + * Seek to a specific position. * * @param positionMs The position in milliseconds. */ - suspend fun seekTo(positionMs: Long): Result + fun seekTo(positionMs: Long) data class State( + /** + * Whether the player is ready to play. + */ + val isReady: Boolean, /** * Whether this player is currently playing. */ val isPlaying: Boolean, /** - * Whether this player has control of the underlying [MediaPlayer]. + * Whether the player has reached the end of the media. */ - val isMyMedia: Boolean, + val isEnded: Boolean, /** * The elapsed time of this player in milliseconds. */ val currentPosition: Long, + /** + * The duration of the current content, if available. + */ + val duration: Long?, ) } @@ -140,50 +152,84 @@ class DefaultVoiceMessagePlayer( body = body ) - override val state: Flow = mediaPlayer.state.map { state -> + private var internalState = MutableStateFlow( VoiceMessagePlayer.State( - isPlaying = state.mediaId.isMyTrack() && state.isPlaying, - isMyMedia = state.mediaId.isMyTrack(), - currentPosition = if (state.mediaId.isMyTrack()) state.currentPosition else 0L + isReady = false, + isPlaying = false, + isEnded = false, + currentPosition = 0L, + duration = null + ) + ) + + override val state: Flow = combine(mediaPlayer.state, internalState) { mediaPlayerState, internalState -> + if (mediaPlayerState.isMyTrack) { + this.internalState.update { + it.copy( + isReady = mediaPlayerState.isReady, + isPlaying = mediaPlayerState.isPlaying, + isEnded = mediaPlayerState.isEnded, + currentPosition = mediaPlayerState.currentPosition, + duration = mediaPlayerState.duration, + ) + } + } else { + this.internalState.update { + it.copy( + isReady = false, + isPlaying = false, + ) + } + } + VoiceMessagePlayer.State( + isReady = internalState.isReady, + isPlaying = internalState.isPlaying, + isEnded = internalState.isEnded, + currentPosition = internalState.currentPosition, + duration = internalState.duration, ) }.distinctUntilChanged() - override suspend fun play(): Result = acquireControl { - mediaPlayer.play() + override suspend fun prepare(): Result = if (eventId != null) { + repo.getMediaFile().mapCatching { mediaFile -> + val state = internalState.value + mediaPlayer.setMedia( + uri = mediaFile.path, + mediaId = eventId.value, + mimeType = "audio/ogg", // Files in the voice cache have no extension so we need to set the mime type manually. + startPositionMs = if (state.isEnded) 0L else state.currentPosition, + ) + } + } else { + Result.failure(IllegalStateException("Cannot acquireControl on a voice message with no eventId")) + } + + override fun play() { + if (inControl()) { + mediaPlayer.play() + } } override fun pause() { - ifInControl { + if (inControl()) { mediaPlayer.pause() } } - override suspend fun seekTo(positionMs: Long): Result = acquireControl { - mediaPlayer.seekTo(positionMs) - } - - private fun String?.isMyTrack(): Boolean = if (eventId == null) false else this == eventId.value - - private inline fun ifInControl(block: () -> Unit) { - if (inControl()) block() - } - - private fun inControl(): Boolean = mediaPlayer.state.value.mediaId.isMyTrack() - - private suspend inline fun acquireControl(onReady: (state: MediaPlayer.State) -> Unit): Result = if (inControl()) { - onReady(mediaPlayer.state.value) - Result.success(Unit) - } else { - if (eventId != null) { - repo.getMediaFile().mapCatching { mediaFile -> - mediaPlayer.setMedia( - uri = mediaFile.path, - mediaId = eventId.value, - mimeType = "audio/ogg" // Files in the voice cache have no extension so we need to set the mime type manually. - ).let(onReady) - } + override fun seekTo(positionMs: Long) { + if (inControl()) { + mediaPlayer.seekTo(positionMs) } else { - Result.failure(IllegalStateException("Cannot acquireControl on a voice message with no eventId")) + internalState.update { + it.copy(currentPosition = positionMs) + } } } + + private val MediaPlayer.State.isMyTrack: Boolean + get() = if (eventId == null) false else this.mediaId == eventId.value + + private fun inControl(): Boolean = mediaPlayer.state.value.let { + it.isMyTrack && (it.isReady || it.isEnded) + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index d0f7f3b2c1..aebed9dd57 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -72,12 +72,19 @@ class VoiceMessagePresenter @AssistedInject constructor( ) private val play = mutableStateOf>(Async.Uninitialized) - private var progressCache: Float = 0f @Composable override fun present(): VoiceMessageState { - val playerState by player.state.collectAsState(VoiceMessagePlayer.State(isPlaying = false, isMyMedia = false, currentPosition = 0L)) + val playerState by player.state.collectAsState( + VoiceMessagePlayer.State( + isReady = false, + isPlaying = false, + isEnded = false, + currentPosition = 0L, + duration = null + ) + ) val button by remember { derivedStateOf { @@ -90,18 +97,26 @@ class VoiceMessagePresenter @AssistedInject constructor( } } } + val duration by remember { + derivedStateOf { playerState.duration ?: content.duration.toMillis() } + } val progress by remember { derivedStateOf { - if (playerState.isMyMedia) { - progressCache = playerState.currentPosition / content.duration.toMillis().toFloat() - } - progressCache + playerState.currentPosition / duration.toFloat() } } val time by remember { derivedStateOf { - val time = if (playerState.isMyMedia) playerState.currentPosition else content.duration.toMillis() - time.milliseconds.formatShort() + when { + playerState.isReady && !playerState.isEnded -> playerState.currentPosition + playerState.currentPosition > 0 -> playerState.currentPosition + else -> duration + }.milliseconds.formatShort() + } + } + val showCursor by remember { + derivedStateOf { + !play.value.isUninitialized() && !playerState.isEnded } } @@ -110,6 +125,8 @@ class VoiceMessagePresenter @AssistedInject constructor( is VoiceMessageEvents.PlayPause -> { if (playerState.isPlaying) { player.pause() + } else if (playerState.isReady) { + player.play() } else { scope.launch { play.runUpdatingState( @@ -120,24 +137,15 @@ class VoiceMessagePresenter @AssistedInject constructor( it }, ) { - player.play() + player.prepare().apply { + player.play() + } } } } } is VoiceMessageEvents.Seek -> { - scope.launch { - play.runUpdatingState( - errorTransform = { - analyticsService.trackError( - VoiceMessageException.PlayMessageError("Error while trying to seek voice message", it) - ) - it - }, - ) { - player.seekTo((event.percentage * content.duration.toMillis()).toLong()) - } - } + player.seekTo((event.percentage * duration).toLong()) } } } @@ -146,6 +154,7 @@ class VoiceMessagePresenter @AssistedInject constructor( button = button, progress = progress, time = time, + showCursor = showCursor, eventSink = { eventSink(it) }, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageState.kt index 093d5336fd..8940374cc7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageState.kt @@ -20,6 +20,7 @@ data class VoiceMessageState( val button: Button, val progress: Float, val time: String, + val showCursor: Boolean, val eventSink: (event: VoiceMessageEvents) -> Unit, ) { enum class Button { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageStateProvider.kt index 83d2f1141f..188ff6656d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessageStateProvider.kt @@ -35,11 +35,13 @@ open class VoiceMessageStateProvider : PreviewParameterProvider.matchInitialState() { + awaitItem().let { + Truth.assertThat(it.isReady).isEqualTo(false) + Truth.assertThat(it.isPlaying).isEqualTo(false) + Truth.assertThat(it.isEnded).isEqualTo(false) + Truth.assertThat(it.currentPosition).isEqualTo(0) + Truth.assertThat(it.duration).isEqualTo(null) + } +} + +private suspend fun TurbineTestContext.matchReadyState( + fakeTotalDurationMs: Long = FAKE_TOTAL_DURATION_MS, +) { + awaitItem().let { + Truth.assertThat(it.isReady).isEqualTo(true) + Truth.assertThat(it.isPlaying).isEqualTo(false) + Truth.assertThat(it.isEnded).isEqualTo(false) + Truth.assertThat(it.currentPosition).isEqualTo(0) + Truth.assertThat(it.duration).isEqualTo(fakeTotalDurationMs) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt index af8dbfc1d4..00c8cd1f70 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -53,6 +53,7 @@ class VoiceMessagePresenterTest { @Test fun `pressing play downloads and plays`() = runTest { val presenter = createVoiceMessagePresenter( + mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), content = aTimelineItemVoiceContent(durationMs = 2_000), ) moleculeFlow(RecompositionMode.Immediate) { @@ -88,6 +89,7 @@ class VoiceMessagePresenterTest { fun `pressing play downloads and fails`() = runTest { val analyticsService = FakeAnalyticsService() val presenter = createVoiceMessagePresenter( + mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true }, analyticsService = analyticsService, content = aTimelineItemVoiceContent(durationMs = 2_000), @@ -125,6 +127,7 @@ class VoiceMessagePresenterTest { @Test fun `pressing pause while playing pauses`() = runTest { val presenter = createVoiceMessagePresenter( + mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), content = aTimelineItemVoiceContent(durationMs = 2_000), ) moleculeFlow(RecompositionMode.Immediate) { @@ -171,8 +174,9 @@ class VoiceMessagePresenterTest { } @Test - fun `seeking downloads and seeks`() = runTest { + fun `seeking before play`() = runTest { val presenter = createVoiceMessagePresenter( + mediaPlayer = FakeMediaPlayer(fakeTotalDurationMs = 2_000), content = aTimelineItemVoiceContent(durationMs = 10_000), ) moleculeFlow(RecompositionMode.Immediate) { @@ -186,21 +190,6 @@ class VoiceMessagePresenterTest { initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) - awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") - } - awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:00") - } - awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0.5f) - Truth.assertThat(it.time).isEqualTo("0:05") - } awaitItem().also { Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) Truth.assertThat(it.progress).isEqualTo(0.5f) @@ -210,45 +199,7 @@ class VoiceMessagePresenterTest { } @Test - fun `seeking downloads and fails`() = runTest { - val analyticsService = FakeAnalyticsService() - val presenter = createVoiceMessagePresenter( - voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true }, - analyticsService = analyticsService, - content = aTimelineItemVoiceContent(durationMs = 10_000), - ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { - val initialState = awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Play) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") - } - - initialState.eventSink(VoiceMessageEvents.Seek(0.5f)) - - awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") - } - awaitItem().also { - Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Retry) - Truth.assertThat(it.progress).isEqualTo(0f) - Truth.assertThat(it.time).isEqualTo("0:10") - } - analyticsService.trackedErrors.first().also { - Truth.assertThat(it).apply { - isInstanceOf(VoiceMessageException.PlayMessageError::class.java) - hasMessageThat().isEqualTo("Error while trying to seek voice message") - } - } - } - } - - @Test - fun `seeking seeks`() = runTest { + fun `seeking after play`() = runTest { val presenter = createVoiceMessagePresenter( content = aTimelineItemVoiceContent(durationMs = 10_000), ) @@ -283,13 +234,14 @@ class VoiceMessagePresenterTest { } fun TestScope.createVoiceMessagePresenter( + mediaPlayer: FakeMediaPlayer = FakeMediaPlayer(), voiceMessageMediaRepo: VoiceMessageMediaRepo = FakeVoiceMessageMediaRepo(), analyticsService: AnalyticsService = FakeAnalyticsService(), content: TimelineItemVoiceContent = aTimelineItemVoiceContent(), ) = VoiceMessagePresenter( voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, body -> DefaultVoiceMessagePlayer( - mediaPlayer = FakeMediaPlayer(), + mediaPlayer = mediaPlayer, voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo }, eventId = eventId, mediaSource = mediaSource, diff --git a/libraries/mediaplayer/api/src/main/kotlin/io/element/android/libraries/mediaplayer/api/MediaPlayer.kt b/libraries/mediaplayer/api/src/main/kotlin/io/element/android/libraries/mediaplayer/api/MediaPlayer.kt index 46c5b40ead..3598edccf3 100644 --- a/libraries/mediaplayer/api/src/main/kotlin/io/element/android/libraries/mediaplayer/api/MediaPlayer.kt +++ b/libraries/mediaplayer/api/src/main/kotlin/io/element/android/libraries/mediaplayer/api/MediaPlayer.kt @@ -38,6 +38,7 @@ interface MediaPlayer : AutoCloseable { uri: String, mediaId: String, mimeType: String, + startPositionMs: Long = 0, ): State /** @@ -69,6 +70,10 @@ interface MediaPlayer : AutoCloseable { * Whether the player is currently playing. */ val isPlaying: Boolean, + /** + * Whether the player has reached the end of the current media. + */ + val isEnded: Boolean, /** * The id of the media which is currently playing. * diff --git a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt index 66e4e748a5..04919b0e56 100644 --- a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt +++ b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/MediaPlayerImpl.kt @@ -77,6 +77,7 @@ class MediaPlayerImpl @Inject constructor( _state.update { it.copy( isReady = playbackState == Player.STATE_READY, + isEnded = playbackState == Player.STATE_ENDED, currentPosition = player.currentPosition, duration = duration, ) @@ -95,16 +96,22 @@ class MediaPlayerImpl @Inject constructor( MediaPlayer.State( isReady = false, isPlaying = false, + isEnded = false, mediaId = null, currentPosition = 0L, - duration = 0L + duration = null, ) ) override val state: StateFlow = _state.asStateFlow() @OptIn(FlowPreview::class) - override suspend fun setMedia(uri: String, mediaId: String, mimeType: String): MediaPlayer.State { + override suspend fun setMedia( + uri: String, + mediaId: String, + mimeType: String, + startPositionMs: Long, + ): MediaPlayer.State { player.pause() // Must pause here otherwise if the player was playing it would keep on playing the new media item. player.clearMediaItems() player.setMediaItem( @@ -112,7 +119,8 @@ class MediaPlayerImpl @Inject constructor( .setUri(uri) .setMediaId(mediaId) .setMimeType(mimeType) - .build() + .build(), + startPositionMs, ) player.prepare() // Will throw TimeoutCancellationException if the player is not ready after 1 second. @@ -129,7 +137,7 @@ class MediaPlayerImpl @Inject constructor( // playing no sound. // This is a workaround which will reload the media file. player.getCurrentMediaItem()?.let { - player.setMediaItem(it) + player.setMediaItem(it, 0) player.prepare() player.play() } diff --git a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/SimplePlayer.kt b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/SimplePlayer.kt index 1395c69e57..ff8ff786ec 100644 --- a/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/SimplePlayer.kt +++ b/libraries/mediaplayer/impl/src/main/kotlin/io/element/android/libraries/mediaplayer/impl/SimplePlayer.kt @@ -35,7 +35,7 @@ interface SimplePlayer { val playbackState: Int val duration: Long fun clearMediaItems() - fun setMediaItem(mediaItem: MediaItem) + fun setMediaItem(mediaItem: MediaItem, startPositionMs: Long) fun getCurrentMediaItem(): MediaItem? fun prepare() fun play() @@ -81,7 +81,7 @@ class SimplePlayerImpl( override fun clearMediaItems() = p.clearMediaItems() - override fun setMediaItem(mediaItem: MediaItem) = p.setMediaItem(mediaItem) + override fun setMediaItem(mediaItem: MediaItem, startPositionMs: Long) = p.setMediaItem(mediaItem, startPositionMs) override fun getCurrentMediaItem(): MediaItem? = p.currentMediaItem diff --git a/libraries/mediaplayer/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeMediaPlayer.kt b/libraries/mediaplayer/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeMediaPlayer.kt index 4f7d19df47..9bfb974622 100644 --- a/libraries/mediaplayer/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeMediaPlayer.kt +++ b/libraries/mediaplayer/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeMediaPlayer.kt @@ -26,31 +26,37 @@ import kotlinx.coroutines.flow.update /** * Fake implementation of [MediaPlayer] for testing purposes. */ -class FakeMediaPlayer : MediaPlayer { - companion object { - private const val FAKE_TOTAL_DURATION_MS = 10_000L - private const val FAKE_PLAYED_DURATION_MS = 1000L - } +class FakeMediaPlayer( + private val fakeTotalDurationMs: Long = 10_000L, + private val fakePlayedDurationMs: Long = 1000L, +) : MediaPlayer { private val _state = MutableStateFlow( MediaPlayer.State( isReady = false, isPlaying = false, + isEnded = false, mediaId = null, currentPosition = 0L, - duration = 0L + duration = null ) ) override val state: StateFlow = _state.asStateFlow() - override suspend fun setMedia(uri: String, mediaId: String, mimeType: String): MediaPlayer.State { + override suspend fun setMedia( + uri: String, + mediaId: String, + mimeType: String, + startPositionMs: Long, + ): MediaPlayer.State { _state.update { it.copy( isReady = false, isPlaying = false, + isEnded = false, mediaId = mediaId, - currentPosition = 0, + currentPosition = startPositionMs, duration = null, ) } @@ -58,7 +64,7 @@ class FakeMediaPlayer : MediaPlayer { _state.update { it.copy( isReady = true, - duration = FAKE_TOTAL_DURATION_MS, + duration = fakeTotalDurationMs, ) } return _state.value @@ -66,10 +72,20 @@ class FakeMediaPlayer : MediaPlayer { override fun play() { _state.update { - it.copy( - isPlaying = true, - currentPosition = it.currentPosition + FAKE_PLAYED_DURATION_MS, - ) + val newPosition = it.currentPosition + fakePlayedDurationMs + if (newPosition < fakeTotalDurationMs) { + it.copy( + isPlaying = true, + currentPosition = newPosition, + ) + } else { + it.copy( + isReady = false, + isPlaying = false, + isEnded = true, + currentPosition = fakeTotalDurationMs, + ) + } } } From ed37d1c64757ad43de668f11057f7c269be3f3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 22 Nov 2023 10:30:15 +0100 Subject: [PATCH 101/102] Changelog for version 0.3.2 --- CHANGES.md | 25 ++++++++++++++++++++++ changelog.d/+remove-element-call-flag.misc | 4 ---- changelog.d/1158.feature | 1 - changelog.d/1453.feature | 1 - changelog.d/1591.feature | 1 - changelog.d/1718.misc | 1 - changelog.d/1784.feature | 1 - changelog.d/1790.bugfix | 1 - changelog.d/1801.misc | 1 - changelog.d/1806.misc | 1 - changelog.d/1824.misc | 1 - 11 files changed, 25 insertions(+), 13 deletions(-) delete mode 100644 changelog.d/+remove-element-call-flag.misc delete mode 100644 changelog.d/1158.feature delete mode 100644 changelog.d/1453.feature delete mode 100644 changelog.d/1591.feature delete mode 100644 changelog.d/1718.misc delete mode 100644 changelog.d/1784.feature delete mode 100644 changelog.d/1790.bugfix delete mode 100644 changelog.d/1801.misc delete mode 100644 changelog.d/1806.misc delete mode 100644 changelog.d/1824.misc diff --git a/CHANGES.md b/CHANGES.md index 665bca158f..3b4b5ca350 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,28 @@ +Changes in Element X v0.3.2 (2023-11-22) +======================================== + +Features ✨ +---------- + - Add ongoing call indicator to rooms lists items. ([#1158](https://github.com/vector-im/element-x-android/issues/1158)) + - Add support for typing mentions in the message composer. ([#1453](https://github.com/vector-im/element-x-android/issues/1453)) + - Add intentional mentions to messages. This needs to be enabled in developer options since it's disabled by default. ([#1591](https://github.com/vector-im/element-x-android/issues/1591)) + - Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. ([#1784](https://github.com/vector-im/element-x-android/issues/1784)) + +Bugfixes 🐛 +---------- + - Always ensure media temp dir exists ([#1790](https://github.com/vector-im/element-x-android/issues/1790)) + +Other changes +------------- + - Update icons and move away from `PreferenceText` components. ([#1718](https://github.com/vector-im/element-x-android/issues/1718)) + - Add item "This is the beginning of..." at the beginning of the timeline. ([#1801](https://github.com/vector-im/element-x-android/issues/1801)) + - LockScreen : rework LoggedInFlowNode and back management when locked. ([#1806](https://github.com/vector-im/element-x-android/issues/1806)) + - Suppress usage of removeTimeline method. ([#1824](https://github.com/vector-im/element-x-android/issues/1824)) + - Remove Element Call feature flag, it's now always enabled. + - Reverted the EC base URL to `https://call.element.io`. + - Moved the option to override this URL to developer settings from advanced settings. + + Changes in Element X v0.3.1 (2023-11-09) ======================================== diff --git a/changelog.d/+remove-element-call-flag.misc b/changelog.d/+remove-element-call-flag.misc deleted file mode 100644 index a5f67489ed..0000000000 --- a/changelog.d/+remove-element-call-flag.misc +++ /dev/null @@ -1,4 +0,0 @@ -Remove Element Call feature flag, it's now always enabled. - -- Reverted the EC base URL to `https://call.element.io`. -- Moved the option to override this URL to developer settings from advanced settings. diff --git a/changelog.d/1158.feature b/changelog.d/1158.feature deleted file mode 100644 index a3ce14a877..0000000000 --- a/changelog.d/1158.feature +++ /dev/null @@ -1 +0,0 @@ -Add ongoing call indicator to rooms lists items. diff --git a/changelog.d/1453.feature b/changelog.d/1453.feature deleted file mode 100644 index 86cc90ad74..0000000000 --- a/changelog.d/1453.feature +++ /dev/null @@ -1 +0,0 @@ -Add support for typing mentions in the message composer. diff --git a/changelog.d/1591.feature b/changelog.d/1591.feature deleted file mode 100644 index 54f1402cf8..0000000000 --- a/changelog.d/1591.feature +++ /dev/null @@ -1 +0,0 @@ -Add intentional mentions to messages. diff --git a/changelog.d/1718.misc b/changelog.d/1718.misc deleted file mode 100644 index 1b021767c3..0000000000 --- a/changelog.d/1718.misc +++ /dev/null @@ -1 +0,0 @@ -Update icons and move away from `PreferenceText` components. diff --git a/changelog.d/1784.feature b/changelog.d/1784.feature deleted file mode 100644 index 8613d7c798..0000000000 --- a/changelog.d/1784.feature +++ /dev/null @@ -1 +0,0 @@ -Update voice message recording behaviour. Instead of holding the record button, users can now tap the record button to start recording and tap again to stop recording. diff --git a/changelog.d/1790.bugfix b/changelog.d/1790.bugfix deleted file mode 100644 index db8ea93406..0000000000 --- a/changelog.d/1790.bugfix +++ /dev/null @@ -1 +0,0 @@ -Always ensure media temp dir exists diff --git a/changelog.d/1801.misc b/changelog.d/1801.misc deleted file mode 100644 index 798f2ae60f..0000000000 --- a/changelog.d/1801.misc +++ /dev/null @@ -1 +0,0 @@ -Add item "This is the beginning of..." at the beginning of the timeline. diff --git a/changelog.d/1806.misc b/changelog.d/1806.misc deleted file mode 100644 index fe3ed3f693..0000000000 --- a/changelog.d/1806.misc +++ /dev/null @@ -1 +0,0 @@ -LockScreen : rework LoggedInFlowNode and back management when locked. diff --git a/changelog.d/1824.misc b/changelog.d/1824.misc deleted file mode 100644 index 16cdc522ed..0000000000 --- a/changelog.d/1824.misc +++ /dev/null @@ -1 +0,0 @@ -Suppress usage of removeTimeline method. From 6c83cb5bb8903e6021f9c4ccaf1122597f3ccc76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 22 Nov 2023 10:34:26 +0100 Subject: [PATCH 102/102] Adding fastlane file for version 0.3.2 --- fastlane/metadata/android/en-US/changelogs/40003020.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 fastlane/metadata/android/en-US/changelogs/40003020.txt diff --git a/fastlane/metadata/android/en-US/changelogs/40003020.txt b/fastlane/metadata/android/en-US/changelogs/40003020.txt new file mode 100644 index 0000000000..415e29b2bf --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40003020.txt @@ -0,0 +1,7 @@ +Main changes in this version: +- Element Call is now enabled by default. +- There is an 'ongoing call' indicator in the room list. +- Adding mentions to a message is now possible, but it's disabled by default as it's a work in progress. They can be enabled in the developer options. +- Voice messages behavior changed: there is no need to keep pressing to record, to start recording a message just tap on the mic button, then tap again to stop recording. +- Added a marker in the timeline to indicate the starting point of the room messages. +Full changelog: https://github.com/vector-im/element-x-android/releases