From 51c06b4becae8df102aa919d6e0c1f8baa855e94 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 17 Sep 2026 14:49:03 +0200 Subject: [PATCH 01/92] Timeline : initial multi-select state --- .../messages/impl/MessagesPresenter.kt | 4 + .../features/messages/impl/MessagesView.kt | 80 ++++++---- .../impl/actionlist/ActionListPresenter.kt | 9 ++ .../actionlist/model/TimelineItemAction.kt | 1 + .../model/TimelineItemActionComparator.kt | 1 + .../pinned/list/PinnedMessagesListView.kt | 2 + .../messages/impl/timeline/SelectionState.kt | 24 +++ .../messages/impl/timeline/TimelineEvent.kt | 4 + .../impl/timeline/TimelinePresenter.kt | 59 ++++++++ .../messages/impl/timeline/TimelineState.kt | 12 ++ .../impl/timeline/TimelineStateProvider.kt | 2 + .../messages/impl/timeline/TimelineView.kt | 60 +++++++- .../timeline/components/SelectionIndicator.kt | 54 +++++++ .../components/TimelineItemEventRow.kt | 10 +- .../TimelineItemGroupedEventsRow.kt | 1 + .../timeline/components/TimelineItemRow.kt | 142 ++++++++++++++++-- .../components/TimelineItemSelectionData.kt | 24 +++ .../timeline/model/TimelineItemSelection.kt | 34 +++++ .../impl/topbars/SelectionModeTopBar.kt | 59 ++++++++ .../impl/src/main/res/values/temporary.xml | 15 ++ .../actionlist/ActionListPresenterTest.kt | 38 +++++ .../impl/timeline/TimelinePresenterTest.kt | 77 ++++++++++ .../model/TimelineItemSelectionTest.kt | 47 ++++++ .../libraries/featureflag/api/FeatureFlags.kt | 7 + 24 files changed, 726 insertions(+), 40 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt create mode 100644 features/messages/impl/src/main/res/values/temporary.xml create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.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 6a019d865b..bcbfc35255 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 @@ -402,6 +402,10 @@ class MessagesPresenter( TimelineItemAction.Unpin -> handleUnpinAction(targetEvent) TimelineItemAction.ViewInTimeline -> Unit TimelineItemAction.RetrySending -> handleRetrySending(targetEvent) + TimelineItemAction.Select -> { + val eventId = targetEvent.eventId ?: return@launch + timelineState.eventSink(TimelineEvent.EnterSelectionMode(eventId)) + } } } 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 16ca023d50..c4bcf552b5 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 @@ -10,6 +10,8 @@ package io.element.android.features.messages.impl import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -89,12 +91,15 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheet import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvent +import io.element.android.features.messages.impl.timeline.isSelectionModeActive 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.event.aTimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.features.messages.impl.timeline.selectedCount import io.element.android.features.messages.impl.timeline.sendfailure.SendFailureDialogView import io.element.android.features.messages.impl.topbars.MessagesViewTopBar +import io.element.android.features.messages.impl.topbars.SelectionModeTopBar import io.element.android.features.messages.impl.topbars.ThreadTopBar import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessagePermissionRationaleDialog import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageSendingFailedDialog @@ -177,6 +182,14 @@ fun MessagesView( block() } + // Entering selection mode collapses the composer, so dismiss the keyboard too. hideKeyboard + // triggers the IME hide animation, keeping it in sync with the composer collapse. + LaunchedEffect(state.timelineState.isSelectionModeActive) { + if (state.timelineState.isSelectionModeActive) { + localView.hideKeyboard() + } + } + fun onContentClick(event: TimelineItem.Event) { Timber.v("onMessageClick= ${event.id}") val eventId = event.eventId @@ -233,34 +246,45 @@ fun MessagesView( Scaffold( contentWindowInsets = scaffoldScrollableContentInsets, topBar = { - if (state.timelineState.timelineMode is Timeline.Mode.Thread) { - ThreadTopBar( - roomName = state.roomName, - roomAvatarData = state.roomAvatar, - heroes = state.heroes, - isTombstoned = state.isTombstoned, - onBackClick = onBackClick, - ) - } else { - MessagesViewTopBar( - roomName = state.roomName, - roomAvatar = state.roomAvatar, - isTombstoned = state.isTombstoned, - heroes = state.heroes, - dmUserIdentityState = state.dmUserVerificationState, - sharedHistoryIcon = state.topBarSharedHistoryIcon, - dmUserStatus = state.dmUserStatus, - onBackClick = { hidingKeyboard { onBackClick() } }, - onRoomDetailsClick = { hidingKeyboard { onRoomDetailsClick() } }, - menuActions = { - MessagesMenuActions( - displayThreads = state.timelineState.timelineMode !is Timeline.Mode.Thread && state.threads.hasThreads, - roomCallState = state.roomCallState, - onJoinCallClick = onJoinCallClick, - onThreadsListClick = onThreadsListClick - ) - } - ) + when { + state.timelineState.isSelectionModeActive -> { + SelectionModeTopBar( + selectedCount = state.timelineState.selectedCount, + onCancelClick = { + state.timelineState.eventSink(TimelineEvent.ExitSelectionMode) + }, + ) + } + state.timelineState.timelineMode is Timeline.Mode.Thread -> { + ThreadTopBar( + roomName = state.roomName, + roomAvatarData = state.roomAvatar, + heroes = state.heroes, + isTombstoned = state.isTombstoned, + onBackClick = onBackClick, + ) + } + else -> { + MessagesViewTopBar( + roomName = state.roomName, + roomAvatar = state.roomAvatar, + isTombstoned = state.isTombstoned, + heroes = state.heroes, + dmUserIdentityState = state.dmUserVerificationState, + sharedHistoryIcon = state.topBarSharedHistoryIcon, + dmUserStatus = state.dmUserStatus, + onBackClick = { hidingKeyboard { onBackClick() } }, + onRoomDetailsClick = { hidingKeyboard { onRoomDetailsClick() } }, + menuActions = { + MessagesMenuActions( + displayThreads = state.timelineState.timelineMode !is Timeline.Mode.Thread && state.threads.hasThreads, + roomCallState = state.roomCallState, + onJoinCallClick = onJoinCallClick, + onThreadsListClick = onThreadsListClick + ) + } + ) + } } }, content = { padding -> diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 8caa95f31d..66ed7dd757 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo +import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAttachmentsContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentWithAttachment @@ -109,6 +110,7 @@ class DefaultActionListPresenter( }.collectAsState(initial = persistentListOf()) val isThreadsEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Threads).collectAsState(false) + val isMultiSelectEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.MessageMultiSelect).collectAsState(false) fun handleEvent(event: ActionListEvent) { when (event) { @@ -120,6 +122,7 @@ class DefaultActionListPresenter( pinnedEventIds = pinnedEventIds, target = target, isThreadsEnabled = isThreadsEnabled.value, + isMultiSelectEnabled = isMultiSelectEnabled.value, ) } } @@ -137,6 +140,7 @@ class DefaultActionListPresenter( pinnedEventIds: ImmutableList, target: MutableState, isThreadsEnabled: Boolean, + isMultiSelectEnabled: Boolean, ) = launch { target.value = ActionListState.Target.Loading(timelineItem) @@ -146,6 +150,7 @@ class DefaultActionListPresenter( isDeveloperModeEnabled = isDeveloperModeEnabled, isEventPinned = pinnedEventIds.contains(timelineItem.eventId), isThreadsEnabled = isThreadsEnabled, + isMultiSelectEnabled = isMultiSelectEnabled, ) val verifiedUserSendFailure = userSendFailureFactory.create(timelineItem.localSendState) @@ -179,6 +184,7 @@ class DefaultActionListPresenter( isDeveloperModeEnabled: Boolean, isEventPinned: Boolean, isThreadsEnabled: Boolean, + isMultiSelectEnabled: Boolean, ): List { val canRedact = timelineItem.isMine && usersEventPermissions.canRedactOwn || !timelineItem.isMine && usersEventPermissions.canRedactOther return buildSet { @@ -200,6 +206,9 @@ class DefaultActionListPresenter( if (timelineItem.isRemote && timelineItem.content.canBeForwarded()) { add(TimelineItemAction.Forward) } + if (isMultiSelectEnabled && timelineItem.canBeSelected()) { + add(TimelineItemAction.Select) + } if (timelineItem.isEditable && usersEventPermissions.canSendMessage) { if (timelineItem.content is TimelineItemEventContentWithAttachment || timelineItem.content is TimelineItemGalleryContent || 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 3c64223e59..c7ee198ebc 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 @@ -20,6 +20,7 @@ enum class TimelineItemAction( ) { ViewInTimeline(CommonStrings.action_view_in_timeline, CompoundDrawables.ic_compound_visibility_on), Forward(CommonStrings.action_forward, CompoundDrawables.ic_compound_forward), + Select(CommonStrings.action_select, CompoundDrawables.ic_compound_check), CopyText(CommonStrings.action_copy_text, CompoundDrawables.ic_compound_copy), CopyCaption(CommonStrings.action_copy_caption, CompoundDrawables.ic_compound_copy), CopyLink(CommonStrings.action_copy_link_to_message, CompoundDrawables.ic_compound_link), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt index 2f6fa4e4e0..86b62794ba 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt @@ -19,6 +19,7 @@ class TimelineItemActionComparator : Comparator { TimelineItemAction.Reply, TimelineItemAction.ReplyInThread, TimelineItemAction.Forward, + TimelineItemAction.Select, TimelineItemAction.Edit, TimelineItemAction.EditPoll, TimelineItemAction.AddCaption, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index 5daec3e275..f751250c90 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -33,6 +33,7 @@ import io.element.android.features.messages.impl.link.LinkEvent import io.element.android.features.messages.impl.link.LinkView import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.components.TimelineItemRow +import io.element.android.features.messages.impl.timeline.components.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView @@ -291,6 +292,7 @@ private fun PinnedMessagesListLoaded( onContentLayoutChange = onContentLayoutChange ) }, + selectionData = TimelineItemSelectionData(), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt new file mode 100644 index 0000000000..91b0d2e73a --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline + +import androidx.compose.runtime.Immutable +import io.element.android.libraries.matrix.api.core.EventId +import kotlinx.collections.immutable.ImmutableSet + +/** Maximum number of messages that can be selected at once. */ +const val MAX_SELECTION_COUNT = 30 + +@Immutable +sealed interface SelectionState { + /** Selection mode is not active. */ + data object Disabled : SelectionState + + /** Selection mode is active with the given selection. */ + data class Active(val selectedEventIds: ImmutableSet) : SelectionState +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt index 66807b74f2..f21bfe88aa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt @@ -29,6 +29,10 @@ sealed interface TimelineEvent { data object MarkAllAsRead : TimelineEvent + data class EnterSelectionMode(val eventId: EventId) : TimelineEvent + data class ToggleSelection(val eventId: EventId) : TimelineEvent + data object ExitSelectionMode : TimelineEvent + /** * Events coming from a timeline item. */ 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 2fc4351cea..80812264fd 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 @@ -25,6 +25,7 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.messages.impl.MessagesNavigator +import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvent import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState @@ -33,6 +34,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent @@ -46,6 +48,8 @@ import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.roomcall.api.RoomCallState import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags @@ -68,7 +72,9 @@ import io.element.android.services.analytics.api.finishLongRunningTransaction import io.element.android.services.analyticsproviders.api.AnalyticsUserData import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toPersistentSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -100,6 +106,7 @@ class TimelinePresenter( private val typingNotificationPresenter: Presenter, private val roomCallStatePresenter: Presenter, private val featureFlagService: FeatureFlagService, + private val snackbarDispatcher: SnackbarDispatcher, private val analyticsService: AnalyticsService, private val liveLocationShareManager: ActiveLiveLocationShareManager, private val markAsFullyRead: MarkAsFullyRead, @@ -164,6 +171,18 @@ class TimelinePresenter( value = featureFlagService.isFeatureEnabled(FeatureFlags.JumpToUnread) } + val isMultiSelectEnabled by produceState(false) { + featureFlagService.isFeatureEnabledFlow(FeatureFlags.MessageMultiSelect).collect { value = it } + } + var selectionState by remember { mutableStateOf(SelectionState.Disabled) } + + // When the feature flag is turned off, clear any active selection. + LaunchedEffect(isMultiSelectEnabled) { + if (!isMultiSelectEnabled) { + selectionState = SelectionState.Disabled + } + } + val timelineProtectionState = timelineProtectionPresenter.present() fun handleEvent(event: TimelineEvent) { @@ -244,6 +263,28 @@ class TimelinePresenter( is TimelineEvent.JumpToLive -> { timelineController.focusOnLive() } + is TimelineEvent.EnterSelectionMode -> { + if (!isMultiSelectEnabled) return + selectionState = SelectionState.Active(persistentSetOf(event.eventId)) + } + is TimelineEvent.ToggleSelection -> { + val current = selectionState as? SelectionState.Active ?: return + val selected = current.selectedEventIds + selectionState = when { + event.eventId in selected -> { + val next = (selected - event.eventId).toPersistentSet() + if (next.isEmpty()) SelectionState.Disabled else SelectionState.Active(next) + } + selected.size >= MAX_SELECTION_COUNT -> { + snackbarDispatcher.post(SnackbarMessage(R.string.screen_room_timeline_selection_limit_reached)) + current + } + else -> SelectionState.Active((selected + event.eventId).toPersistentSet()) + } + } + is TimelineEvent.ExitSelectionMode -> { + selectionState = SelectionState.Disabled + } TimelineEvent.HideShieldDialog -> messageShieldDialogData.value = null TimelineEvent.MarkAllAsRead -> sessionCoroutineScope.launch { val latestEventId = room.liveTimeline.getLatestEventId().getOrElse { @@ -353,6 +394,23 @@ class TimelinePresenter( computeNewItemState(timelineItems, prevMostRecentItemId, newEventState) } + // Reconcile the selection when the items change: drop any selected event that is no longer + // present or no longer selectable, and exit selection mode if nothing remains selected. + LaunchedEffect(timelineItems.map { it.identifier() }) { + val active = selectionState as? SelectionState.Active ?: return@LaunchedEffect + val stillSelectable = timelineItems + .filterIsInstance() + .filter { it.canBeSelected() } + .mapNotNull { it.eventId } + .toSet() + val reconciled = active.selectedEventIds.filter { it in stillSelectable }.toPersistentSet() + selectionState = when { + reconciled.isEmpty() -> SelectionState.Disabled + reconciled.size != active.selectedEventIds.size -> SelectionState.Active(reconciled) + else -> active + } + } + // Keyed on the full [timelineItems] reference (not just .size) so we re-scan when the // read marker advances in place — the SDK swaps the marker virtual item to a new position // without changing the list length, e.g. when [markRoomAsFullyRead] is sent while at the @@ -463,6 +521,7 @@ class TimelinePresenter( displayThreadSummaries = displayThreadSummaries, displayJumpToUnread = displayJumpToUnread, jumpToUnread = jumpToUnread.value, + selectionState = selectionState, eventSink = ::handleEvent, ) } 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 1ebc96f15f..6bd5aa0895 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 @@ -37,6 +37,7 @@ data class TimelineState( val displayThreadSummaries: Boolean, val displayJumpToUnread: Boolean, val jumpToUnread: JumpToUnreadState, + val selectionState: SelectionState, val eventSink: (TimelineEvent) -> Unit, ) { private val lastTimelineEvent = timelineItems.firstOrNull { it is TimelineItem.Event } as? TimelineItem.Event @@ -100,3 +101,14 @@ sealed interface JumpToUnreadState { /** The read marker event is older than the loaded window — load it via focused-event navigation. */ data class OutOfWindow(val eventId: EventId) : JumpToUnreadState } + +val TimelineState.isSelectionModeActive: Boolean + get() = selectionState is SelectionState.Active + +val TimelineState.selectedCount: Int + get() = (selectionState as? SelectionState.Active)?.selectedEventIds?.size ?: 0 + +fun TimelineState.isSelected(eventId: EventId?): Boolean { + if (eventId == null) return false + return (selectionState as? SelectionState.Active)?.selectedEventIds?.contains(eventId) == true +} 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 89545b5d0b..5faea3de12 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 @@ -63,6 +63,7 @@ fun aTimelineState( displayJumpToUnread: Boolean = false, jumpToUnread: JumpToUnreadState = JumpToUnreadState.Hidden, newEventState: NewEventState = NewEventState.None, + selectionState: SelectionState = SelectionState.Disabled, eventSink: (TimelineEvent) -> Unit = {}, ): TimelineState { val focusedEventId = timelineItems.filterIsInstance().getOrNull(focusedEventIndex)?.eventId @@ -84,6 +85,7 @@ fun aTimelineState( displayThreadSummaries = displayThreadSummaries, displayJumpToUnread = displayJumpToUnread, jumpToUnread = jumpToUnread, + selectionState = selectionState, eventSink = eventSink, ) } 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 5e7fd34723..0df9be3ca0 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 @@ -11,6 +11,7 @@ package io.element.android.features.messages.impl.timeline import android.view.HapticFeedbackConstants import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -83,14 +84,17 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureView import io.element.android.features.messages.impl.timeline.components.FloatingDateBadgeOverlay import io.element.android.features.messages.impl.timeline.components.TimelineItemRow +import io.element.android.features.messages.impl.timeline.components.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.components.toText import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.focus.FocusRequestStateView import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentPreviewParam +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.androidutils.system.copyToClipboard @@ -111,6 +115,7 @@ import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.link.Link import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine @@ -189,6 +194,18 @@ fun TimelineView( state.eventSink(TimelineEvent.LoadMore(Timeline.PaginationDirection.BACKWARDS)) } + val isSelectionModeActive = state.isSelectionModeActive + // Animate once here (shared by every row) rather than per-row: the value is identical for all + // rows since it only depends on the timeline-wide selection mode. + val selectionProgress by animateFloatAsState( + targetValue = if (isSelectionModeActive) 1f else 0f, + label = "selectionProgress", + ) + fun onToggleSelection(event: TimelineItem.Event) { + val eventId = event.eventId ?: return + state.eventSink(TimelineEvent.ToggleSelection(eventId)) + } + // Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms AnimatedVisibility(visible = true, enter = fadeIn()) { Box(modifier) { @@ -214,12 +231,21 @@ fun TimelineView( isLastOutgoingMessage = state.isLastOutgoingMessage(timelineItem.identifier()), focusedEventId = state.focusedEventId, displayThreadSummaries = state.displayThreadSummaries, + selectionData = TimelineItemSelectionData( + isSelectionModeActive = isSelectionModeActive, + progress = selectionProgress, + isSelected = state.isSelected((timelineItem as? TimelineItem.Event)?.eventId), + ), onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, onLinkLongClick = ::onLinkLongClick, - onContentClick = onContentClick, + onContentClick = { event -> + if (isSelectionModeActive && event.canBeSelected()) onToggleSelection(event) else onContentClick(event) + }, onGalleryItemClick = onGalleryItemClick, - onLongClick = onMessageLongClick, + onLongClick = { event -> + if (isSelectionModeActive && event.canBeSelected()) onToggleSelection(event) else onMessageLongClick(event) + }, inReplyToClick = ::inReplyToClick, onReactionClick = onReactionClick, onReactionLongClick = onReactionLongClick, @@ -616,6 +642,36 @@ internal fun TimelineViewPreview( } } +@PreviewsDayNight +@Composable +internal fun TimelineViewSelectionModePreview() = ElementPreview { + val timelineItems = aTimelineItemList(aTimelineItemTextContent()) + val selectedEventId = timelineItems.filterIsInstance().first { it.canBeSelected() }.eventId!! + CompositionLocalProvider( + LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(), + ) { + TimelineView( + state = aTimelineState( + timelineItems = timelineItems, + selectionState = SelectionState.Active(persistentSetOf(selectedEventId)), + ), + timelineProtectionState = aTimelineProtectionState(), + onUserDataClick = {}, + onLinkClick = {}, + onContentClick = {}, + onMessageLongClick = {}, + onSwipeToReply = {}, + onReactionClick = { _, _ -> }, + onReactionLongClick = { _, _ -> }, + onJoinCallClick = {}, + onMoreReactionsClick = {}, + onReadReceiptClick = {}, + onGalleryItemClick = { _, _ -> }, + forceJumpToBottomVisibility = true, + ) + } +} + // The jump-to-unread FAB's indicator is always rendered when the FAB is visible — in // production the FAB only appears when unread content exists above. So `hasUnreadAbove` // here only varies the scroll-target state, not the upper indicator's visibility. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt new file mode 100644 index 0000000000..e980b47155 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.components + +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +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 + +/** + * Compact selection indicator used in the timeline selection mode, in place of a Material + * [androidx.compose.material3.Checkbox] (which reserves a 48dp interactive area). It is purely + * visual: the tap is handled by the whole-row click target, so this only reflects [selected]. + */ +@Composable +internal fun SelectionIndicator( + selected: Boolean, + modifier: Modifier = Modifier, +) { + Icon( + modifier = modifier, + imageVector = if (selected) CompoundIcons.CheckCircleSolid() else CompoundIcons.Circle(), + contentDescription = null, + tint = if (selected) ElementTheme.colors.iconAccentPrimary else ElementTheme.colors.iconTertiary, + ) +} + +@PreviewsDayNight +@Composable +internal fun SelectionIndicatorPreview() = ElementPreview { + Icon( + modifier = Modifier.size(24.dp), + imageVector = CompoundIcons.Circle(), + contentDescription = null, + tint = ElementTheme.colors.iconTertiary, + ) +} + +@PreviewsDayNight +@Composable +internal fun SelectionIndicatorSelectedPreview() = ElementPreview { + SelectionIndicator(selected = true) +} 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 99e72f4973..d8a8473891 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 @@ -24,6 +24,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.absoluteOffset import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -59,6 +60,7 @@ import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDirection import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.LayoutDirection @@ -180,6 +182,9 @@ fun TimelineItemEventRow( onSwipeToReply: () -> Unit, eventSink: (TimelineEvent.TimelineItemEvent) -> Unit, modifier: Modifier = Modifier, + // Horizontal offset applied to this row's content by selection mode. The read receipts are + // anchored to the end edge and should stay pinned, so they are counter-offset by this amount. + selectionContentOffset: Dp = 0.dp, eventContentView: @Composable (Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit = { contentModifier, onContentLayoutChange -> // Only pass down a custom clickable lambda if the content can be clicked separately val onContentClick = onEventClick.takeUnless { event.isWholeContentClickable } @@ -326,7 +331,10 @@ fun TimelineItemEventRow( receipts = event.readReceiptState.receipts, ), onReadReceiptsClick = { onReadReceiptClick(event) }, - modifier = Modifier.padding(top = 4.dp) + // Counter the selection-mode content slide so the end-anchored read receipts stay pinned. + modifier = Modifier + .padding(top = 4.dp) + .offset(x = -selectionContentOffset) ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 5b620a7dc3..7094e302f8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -190,6 +190,7 @@ private fun TimelineItemGroupedEventsRowContent( onSwipeToReply = {}, eventSink = eventSink, eventContentView = eventContentView, + selectionData = TimelineItemSelectionData(), ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index fb9659ff70..7f08aa465e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -8,22 +8,30 @@ package io.element.android.features.messages.impl.timeline.components +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.isTraversalGroup import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.messages.impl.timeline.TimelineEvent @@ -31,6 +39,7 @@ import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent @@ -60,6 +69,7 @@ internal fun TimelineItemRow( timelineProtectionState: TimelineProtectionState, focusedEventId: EventId?, displayThreadSummaries: Boolean, + selectionData: TimelineItemSelectionData, onUserDataClick: (MatrixUser) -> Unit, onLinkClick: (Link) -> Unit, onLinkLongClick: (Link) -> Unit, @@ -92,17 +102,130 @@ internal fun TimelineItemRow( ) }, ) { - val backgroundModifier = if (timelineItem.isEvent(focusedEventId)) { - val focusedEventOffset = if ((timelineItem as? TimelineItem.Event)?.showSenderInformation == true) { - 14.dp - } else { - 2.dp + val isSelectionModeActive = selectionData.isSelectionModeActive + val selectionProgress = selectionData.progress + val isSelected = selectionData.isSelected + val backgroundModifier = when { + isSelected -> { + Modifier.background(ElementTheme.colors.bgAccentSelected) + } + timelineItem.isEvent(focusedEventId) -> { + val focusedEventOffset = + if ((timelineItem as? TimelineItem.Event)?.showSenderInformation == true) { + 14.dp + } else { + 2.dp + } + Modifier.focusedEvent(focusedEventOffset) + } + else -> { + Modifier } - Modifier.focusedEvent(focusedEventOffset) - } else { - Modifier } - Box(modifier = modifier.then(backgroundModifier)) { + val selectableEvent = (timelineItem as? TimelineItem.Event)?.takeIf { it.canBeSelected() } + // Only start-aligned message bubbles slide to make room for the selection indicator: + // - Outgoing (own) messages are end-aligned, opposite the indicator, so they already have room. + // - Centered tiles (state events, call tiles, day dividers, grouped events) must stay put. + val isOutgoing = (timelineItem as? TimelineItem.Event)?.isMine == true + val shouldSlideContent = timelineItem.rendersAsBubble() && !isOutgoing + val layoutDirection = LocalLayoutDirection.current + val contentSlide = if (shouldSlideContent) SELECTION_COLUMN_WIDTH * selectionProgress else 0.dp + Box( + modifier = modifier.then(backgroundModifier) + ) { + TimelineItemRowContent( + modifier = Modifier + .fillMaxWidth() + .offset(x = contentSlide), + selectionContentOffset = contentSlide, + timelineItem = timelineItem, + timelineMode = timelineMode, + timelineRoomInfo = timelineRoomInfo, + timelineProtectionState = timelineProtectionState, + isLastOutgoingMessage = isLastOutgoingMessage, + focusedEventId = focusedEventId, + displayThreadSummaries = displayThreadSummaries, + onUserDataClick = onUserDataClick, + onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, + onContentClick = onContentClick, + onGalleryItemClick = onGalleryItemClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + onJoinCallClick = onJoinCallClick, + onSwipeToReply = onSwipeToReply, + eventSink = eventSink, + eventContentView = eventContentView, + ) + if (selectableEvent != null && selectionProgress > 0f) { + val slideSign = if (layoutDirection == LayoutDirection.Ltr) -1f else 1f + SelectionIndicator( + selected = isSelected, + modifier = Modifier + .align(Alignment.CenterStart) + .graphicsLayer { translationX = slideSign * SELECTION_COLUMN_WIDTH.toPx() * (1f - selectionProgress) } + .padding(start = 12.dp) + .size(24.dp), + ) + } + if (isSelectionModeActive && selectableEvent != null) { + // In selection mode the whole row toggles selection. + Box( + modifier = Modifier + .matchParentSize() + .clickable { onContentClick(selectableEvent) } + ) + } + } +} + +/** Width reserved for the selection indicator; the row content slides by this amount. */ +private val SELECTION_COLUMN_WIDTH = 36.dp + +/** + * Whether this item is rendered as a start/end-aligned message bubble, as opposed to a centered + * tile (state event, legacy call invite, call/RTC notification, day divider, grouped events). + * Mirrors the dispatch in [TimelineItemRowContent]: only bubbles shift to reveal the selection + * indicator; centered tiles stay put. + */ +private fun TimelineItem.rendersAsBubble(): Boolean = this is TimelineItem.Event && + content !is TimelineItemStateContent && + content !is TimelineItemLegacyCallInviteContent && + content !is TimelineItemRtcNotificationContent + +@Suppress("LongMethod") +@Composable +private fun TimelineItemRowContent( + timelineItem: TimelineItem, + timelineMode: Timeline.Mode, + timelineRoomInfo: TimelineRoomInfo, + isLastOutgoingMessage: Boolean, + timelineProtectionState: TimelineProtectionState, + focusedEventId: EventId?, + displayThreadSummaries: Boolean, + onUserDataClick: (MatrixUser) -> Unit, + onLinkClick: (Link) -> Unit, + onLinkLongClick: (Link) -> Unit, + onContentClick: (TimelineItem.Event) -> Unit, + onGalleryItemClick: (TimelineItem.Event, Int) -> Unit, + onLongClick: (TimelineItem.Event) -> Unit, + inReplyToClick: (EventId) -> Unit, + onReactionClick: (key: String, TimelineItem.Event) -> Unit, + onReactionLongClick: (key: String, TimelineItem.Event) -> Unit, + onMoreReactionsClick: (TimelineItem.Event) -> Unit, + onReadReceiptClick: (TimelineItem.Event) -> Unit, + onJoinCallClick: (isAudioCall: Boolean) -> Unit, + onSwipeToReply: (TimelineItem.Event) -> Unit, + eventSink: (TimelineEvent.TimelineItemEvent) -> Unit, + eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit, + modifier: Modifier = Modifier, + selectionContentOffset: Dp = 0.dp, +) { + Box(modifier) { when (timelineItem) { is TimelineItem.Virtual -> { TimelineItemVirtualRow( @@ -197,6 +320,7 @@ internal fun TimelineItemRow( onSwipeToReply = { onSwipeToReply(timelineItem) }, onGalleryItemClick = { index -> onGalleryItemClick(timelineItem, index) }, eventSink = eventSink, + selectionContentOffset = selectionContentOffset, eventContentView = { contentModifier, onContentLayoutChange -> eventContentView(timelineItem, contentModifier, onContentLayoutChange) }, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt new file mode 100644 index 0000000000..f8de354aac --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.components + +/** + * Row-scoped selection state passed to [TimelineItemRow]. + * + * @param isSelectionModeActive whether the timeline is currently in selection mode. Drives + * behaviour (e.g. the whole-row click target) and must reflect the real mode, not [progress]. + * @param progress animated 0f..1f value driving the visuals (content slide + indicator slide-in). + * Animated once in TimelineView and shared by every row. Differs from [isSelectionModeActive] + * during the enter/exit animation. + * @param isSelected whether this specific row's event is part of the current selection. + */ +data class TimelineItemSelectionData( + val isSelectionModeActive: Boolean = false, + val isSelected: Boolean = false, + val progress: Float = 0f, +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt new file mode 100644 index 0000000000..9f11e86213 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.model + +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent + +/** + * Whether this timeline event can be selected in multi-select ("selection") mode. + */ +fun TimelineItem.Event.canBeSelected(): Boolean { + // Local echoes (no remote eventId) and failed sends are never selectable. + if (!isRemote || failedToSend) return false + return when (content) { + is TimelineItemStateContent, + is TimelineItemRedactedContent, + is TimelineItemEncryptedContent, + is TimelineItemLegacyCallInviteContent, + is TimelineItemRtcNotificationContent, + is TimelineItemLocationContent, + is TimelineItemUnknownContent -> false + else -> true + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt new file mode 100644 index 0000000000..55563d1f1b --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.topbars + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.compound.tokens.generated.CompoundIcons +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.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.theme.components.TopAppBar +import io.element.android.libraries.ui.strings.CommonStrings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SelectionModeTopBar( + selectedCount: Int, + onCancelClick: () -> Unit, + modifier: Modifier = Modifier, +) { + TopAppBar( + modifier = modifier, + title = { + Text(text = pluralStringResource(R.plurals.screen_room_timeline_selection_title, selectedCount, selectedCount)) + }, + navigationIcon = { + IconButton( + onClick = onCancelClick, + ) { + Icon(imageVector = CompoundIcons.Close(), contentDescription = stringResource(CommonStrings.action_close)) + } + }, + ) +} + +@PreviewsDayNight +@Composable +internal fun SelectionModeTopBarPreview( + @PreviewParameter(SelectionCountProvider::class) count: Int, +) = ElementPreview { + SelectionModeTopBar(selectedCount = count, onCancelClick = {}) +} + +internal class SelectionCountProvider : PreviewParameterProvider { + override val values = sequenceOf(0, 1, 5) +} diff --git a/features/messages/impl/src/main/res/values/temporary.xml b/features/messages/impl/src/main/res/values/temporary.xml new file mode 100644 index 0000000000..5dfeaa7b85 --- /dev/null +++ b/features/messages/impl/src/main/res/values/temporary.xml @@ -0,0 +1,15 @@ + + + + + + %1$d selected + %1$d selected + + You can select up to 30 messages + diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index e987a32480..2988fbe894 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -70,6 +70,44 @@ class ActionListPresenterTest { } } + @Test + fun `present - Select action is present for a remote message when multi-select is enabled`() = runTest { + val presenter = createActionListPresenter( + isDeveloperModeEnabled = false, + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink.invoke( + ActionListEvent.ComputeForMessage( + event = aMessageEvent(), + userEventPermissions = aUserEventPermissions(canSendMessage = true), + ) + ) + val successState = awaitItem() + assertThat((successState.target as ActionListState.Target.Success).actions).contains(TimelineItemAction.Select) + } + } + + @Test + fun `present - Select action is absent when multi-select is disabled`() = runTest { + val presenter = createActionListPresenter( + isDeveloperModeEnabled = false, + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to false)), + ) + presenter.test { + val initialState = awaitItem() + initialState.eventSink.invoke( + ActionListEvent.ComputeForMessage( + event = aMessageEvent(), + userEventPermissions = aUserEventPermissions(canSendMessage = true), + ) + ) + val successState = awaitItem() + assertThat((successState.target as ActionListState.Target.Success).actions).doesNotContain(TimelineItemAction.Select) + } + } + @Test fun `present - compute for message from me redacted`() = runTest { val presenter = createActionListPresenter(isDeveloperModeEnabled = true) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 290246ffb9..6dcf107e0b 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -34,6 +34,7 @@ import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction import io.element.android.features.roomcall.api.aStandByCallState import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.EventId @@ -86,6 +87,7 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.delay @@ -1821,6 +1823,80 @@ class TimelinePresenterTest { } } + @Test + fun `present - entering selection mode selects the target event`() = runTest { + val timelineItems = MutableStateFlow( + listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) + ) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + assertThat(initialState.selectionState).isEqualTo(SelectionState.Disabled) + initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + val state = consumeItemsUntilPredicate { it.isSelectionModeActive }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Active(persistentSetOf(AN_EVENT_ID))) + assertThat(state.selectedCount).isEqualTo(1) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - entering selection mode is ignored when the flag is off`() = runTest { + val featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to false)) + val presenter = createTimelinePresenter(featureFlagService = featureFlagService) + presenter.test { + val initialState = awaitFirstItem() + initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + expectNoEvents() + assertThat(initialState.selectionState).isEqualTo(SelectionState.Disabled) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - toggling the last selected event exits selection mode`() = runTest { + val timelineItems = MutableStateFlow( + listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) + ) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + consumeItemsUntilPredicate { it.isSelectionModeActive } + initialState.eventSink(TimelineEvent.ToggleSelection(AN_EVENT_ID)) + val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Disabled) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - turning the flag off clears the active selection`() = runTest { + val timelineItems = MutableStateFlow( + listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) + ) + val featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = featureFlagService, + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + consumeItemsUntilPredicate { it.isSelectionModeActive } + featureFlagService.setFeatureEnabled(FeatureFlags.MessageMultiSelect, false) + val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Disabled) + cancelAndIgnoreRemainingEvents() + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { return awaitItem() } @@ -1911,6 +1987,7 @@ class TimelinePresenterTest { typingNotificationPresenter = { aTypingNotificationState() }, roomCallStatePresenter = { aStandByCallState() }, featureFlagService = featureFlagService, + snackbarDispatcher = SnackbarDispatcher(), analyticsService = FakeAnalyticsService(), liveLocationShareManager = liveLocationShareManager, markAsFullyRead = markAsFullyRead, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt new file mode 100644 index 0000000000..e293246900 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline.model + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import org.junit.Test + +class TimelineItemSelectionTest { + @Test + fun `a remote text message can be selected`() { + val item = aTimelineItemEvent() + assertThat(item.canBeSelected()).isTrue() + } + + @Test + fun `a local echo cannot be selected`() { + val item = aTimelineItemEvent().copy(eventId = null) + assertThat(item.canBeSelected()).isFalse() + } + + @Test + fun `a failed to send message cannot be selected`() { + val item = aTimelineItemEvent(sendState = LocalEventSendState.Failed.Unknown("failed")) + assertThat(item.canBeSelected()).isFalse() + } + + @Test + fun `a state event cannot be selected`() { + val item = aTimelineItemEvent(content = aTimelineItemStateEventContent()) + assertThat(item.canBeSelected()).isFalse() + } + + @Test + fun `a redacted event cannot be selected`() { + val item = aTimelineItemEvent(content = aTimelineItemRedactedContent()) + assertThat(item.canBeSelected()).isFalse() + } +} 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 af7ccef97f..a50ebce5d7 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 @@ -161,4 +161,11 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), + MessageMultiSelect( + key = "feature.message_multi_select", + title = "Multi-select messages", + description = "Adds a Select action to the message menu to pick several messages at once.", + defaultValue = { false }, + isFinished = false, + ), } From 93831ffcbce22831fbd191f62b5014db615f7e66 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:47:17 +0200 Subject: [PATCH 02/92] Forward several events at once --- .../features/forward/api/ForwardEntryPoint.kt | 2 +- .../forward/impl/DefaultForwardEntryPoint.kt | 2 +- .../forward/impl/ForwardMessagesNode.kt | 4 +-- .../forward/impl/ForwardMessagesPresenter.kt | 16 ++++----- .../impl/DefaultForwardEntryPointTest.kt | 4 +-- .../impl/ForwardMessagesPresenterTest.kt | 16 ++++----- .../libraries/matrix/api/timeline/Timeline.kt | 9 ++--- .../matrix/impl/room/RoomContentForwarder.kt | 36 ++++++++++--------- .../impl/timeline/RoomTimelineExtensions.kt | 13 ------- .../matrix/impl/timeline/RustTimeline.kt | 4 +-- .../matrix/test/timeline/FakeTimeline.kt | 6 ++-- 11 files changed, 50 insertions(+), 62 deletions(-) diff --git a/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt b/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt index 95b8c43808..bbc3915c86 100644 --- a/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt +++ b/features/forward/api/src/main/kotlin/io/element/android/features/forward/api/ForwardEntryPoint.kt @@ -23,7 +23,7 @@ interface ForwardEntryPoint : FeatureEntryPoint { } data class Params( - val eventId: EventId, + val eventIds: List, val timelineProvider: TimelineProvider, ) : NodeInputs diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt index 97256c6576..e46e268a55 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPoint.kt @@ -27,7 +27,7 @@ class DefaultForwardEntryPoint : ForwardEntryPoint { buildContext = buildContext, plugins = listOf( ForwardMessagesNode.Inputs( - eventId = params.eventId, + eventIds = params.eventIds, timelineProvider = params.timelineProvider, ), callback, diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt index 1c565a390a..30a48c91de 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesNode.kt @@ -52,13 +52,13 @@ class ForwardMessagesNode( object NavTarget : Parcelable data class Inputs( - val eventId: EventId, + val eventIds: List, val timelineProvider: TimelineProvider, ) : NodeInputs private val inputs = inputs() private val callback: ForwardEntryPoint.Callback = callback() - private val presenter = presenterFactory.create(inputs.eventId.value, inputs.timelineProvider) + private val presenter = presenterFactory.create(inputs.eventIds, inputs.timelineProvider) override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { val callback = object : RoomSelectEntryPoint.Callback { diff --git a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt index 6068866653..a36be20ab4 100644 --- a/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt +++ b/features/forward/impl/src/main/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenter.kt @@ -28,22 +28,20 @@ import timber.log.Timber @AssistedInject class ForwardMessagesPresenter( - @Assisted eventId: String, + @Assisted private val eventIds: List, @Assisted private val timelineProvider: TimelineProvider, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, ) : Presenter { - private val eventId: EventId = EventId(eventId) - @AssistedFactory fun interface Factory { - fun create(eventId: String, timelineProvider: TimelineProvider): ForwardMessagesPresenter + fun create(eventIds: List, timelineProvider: TimelineProvider): ForwardMessagesPresenter } private val forwardingActionState: MutableState>> = mutableStateOf(AsyncAction.Uninitialized) fun onRoomSelected(roomIds: List) { - sessionCoroutineScope.forwardEvent(eventId, roomIds) + sessionCoroutineScope.forwardEvents(eventIds, roomIds) } @Composable @@ -60,14 +58,14 @@ class ForwardMessagesPresenter( ) } - private fun CoroutineScope.forwardEvent( - eventId: EventId, + private fun CoroutineScope.forwardEvents( + eventIds: List, roomIds: List, ) = launch { suspend { - timelineProvider.getActiveTimeline().forwardEvent(eventId, roomIds) + timelineProvider.getActiveTimeline().forwardEvents(eventIds, roomIds) .onFailure { - Timber.e(it, "Error while forwarding event") + Timber.e(it, "Error while forwarding events") } .getOrThrow() roomIds diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt index 9ee932a246..c082e4ecd3 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/DefaultForwardEntryPointTest.kt @@ -45,7 +45,7 @@ class DefaultForwardEntryPointTest { override fun onDone(roomIds: List) = lambdaError() } val params = ForwardEntryPoint.Params( - eventId = AN_EVENT_ID, + eventIds = listOf(AN_EVENT_ID), timelineProvider = FakeTimelineProvider(), ) val result = entryPoint.createNode( @@ -57,7 +57,7 @@ class DefaultForwardEntryPointTest { assertThat(result).isInstanceOf(ForwardMessagesNode::class.java) assertThat(result.plugins).contains( ForwardMessagesNode.Inputs( - eventId = params.eventId, + eventIds = params.eventIds, timelineProvider = params.timelineProvider, ) ) diff --git a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt index 0ff3210f2e..5fda2e252c 100644 --- a/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt +++ b/features/forward/impl/src/test/kotlin/io/element/android/features/forward/impl/ForwardMessagesPresenterTest.kt @@ -44,11 +44,11 @@ class ForwardMessagesPresenterTest { @Test fun `present - forward successful`() = runTest { - val forwardEventLambda = lambdaRecorder { _: EventId, _: List -> + val forwardEventsLambda = lambdaRecorder { _: List, _: List -> Result.success(Unit) } val timeline = FakeTimeline().apply { - this.forwardEventLambda = forwardEventLambda + this.forwardEventsLambda = forwardEventsLambda } val room = FakeJoinedRoom(liveTimeline = timeline) val presenter = createForwardMessagesPresenter(fakeRoom = room) @@ -62,17 +62,17 @@ class ForwardMessagesPresenterTest { assertThat(forwardingState.forwardAction.isLoading()).isTrue() val successfulForwardState = awaitItem() assertThat(successfulForwardState.forwardAction).isEqualTo(AsyncAction.Success(listOf(summary.roomId))) - forwardEventLambda.assertions().isCalledOnce() + forwardEventsLambda.assertions().isCalledOnce() } } @Test fun `present - select a room and forward failed, then clear`() = runTest { - val forwardEventLambda = lambdaRecorder { _: EventId, _: List -> + val forwardEventsLambda = lambdaRecorder { _: List, _: List -> Result.failure(IllegalStateException("error")) } val timeline = FakeTimeline().apply { - this.forwardEventLambda = forwardEventLambda + this.forwardEventsLambda = forwardEventsLambda } val room = FakeJoinedRoom(liveTimeline = timeline) val presenter = createForwardMessagesPresenter(fakeRoom = room) @@ -88,16 +88,16 @@ class ForwardMessagesPresenterTest { // Then clear error failedForwardState.eventSink(ForwardMessagesEvent.ClearError) assertThat(awaitItem().forwardAction.isUninitialized()).isTrue() - forwardEventLambda.assertions().isCalledOnce() + forwardEventsLambda.assertions().isCalledOnce() } } } fun TestScope.createForwardMessagesPresenter( - eventId: EventId = AN_EVENT_ID, + eventIds: List = listOf(AN_EVENT_ID), fakeRoom: FakeJoinedRoom = FakeJoinedRoom(), ) = ForwardMessagesPresenter( - eventId = eventId.value, + eventIds = eventIds, timelineProvider = LiveTimelineProvider(fakeRoom), sessionCoroutineScope = this, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 72483e68f1..51a8964aa0 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -316,12 +316,13 @@ interface Timeline : AutoCloseable { suspend fun toggleReaction(emoji: String, eventOrTransactionId: EventOrTransactionId): Result /** - * Forwards the content of an event to other rooms, as new messages sent by the current user. + * Forwards the content of some events to other rooms, as new messages sent by the current user. + * The events are forwarded in the order of [eventIds]. * - * @param eventId the event whose content is forwarded. - * @param roomIds the rooms to forward it to. + * @param eventIds the events whose content is forwarded. + * @param roomIds the rooms to forward them to. */ - suspend fun forwardEvent(eventId: EventId, roomIds: List): Result + suspend fun forwardEvents(eventIds: List, roomIds: List): Result /** * Cancels an event that is still waiting in the send queue, by redacting its local echo. 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 39cde68c31..8191082806 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 @@ -14,7 +14,6 @@ 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.withTimeout import org.matrix.rustcomponents.sdk.MsgLikeKind import org.matrix.rustcomponents.sdk.RoomListService @@ -31,33 +30,36 @@ class RoomContentForwarder( private val roomListService: RoomListService, ) { /** - * Forwards the event with the given [eventId] from the [fromTimeline] to the given [toRoomIds]. - * @param fromTimeline the room to forward the event from - * @param eventId the id of the event to forward - * @param toRoomIds the ids of the rooms to forward the event to - * @param timeoutMs the maximum time in milliseconds to wait for the event to be sent to a room + * Forwards the events with the given [eventIds] from the [fromTimeline] to the given [toRoomIds]. + * The events are sent to each room in the order they are provided in [eventIds], it is up to the caller to sort them. + * @param fromTimeline the room to forward the events from + * @param eventIds the ids of the events to forward, in the order they must be sent + * @param toRoomIds the ids of the rooms to forward the events to + * @param timeoutMs the maximum time in milliseconds to wait for an event to be sent to a room */ suspend fun forward( fromTimeline: Timeline, - eventId: EventId, + eventIds: List, toRoomIds: List, timeoutMs: Long = 5000L ) { - val messageLikeContent = (fromTimeline.getEventTimelineItemByEventId(eventId.value).content as? TimelineItemContent.MsgLike)?.content - ?: throw ForwardEventException(toRoomIds) - - val content = (messageLikeContent.kind as? MsgLikeKind.Message)?.content - ?: throw ForwardEventException(toRoomIds) - + val contents = eventIds.map { eventId -> + val timelineEvent = fromTimeline.getEventTimelineItemByEventId(eventId.value) + val content = timelineEvent.content as? TimelineItemContent.MsgLike ?: throw ForwardEventException(toRoomIds) + val message = content.content.kind as? MsgLikeKind.Message ?: throw ForwardEventException(toRoomIds) + contentWithoutRelationFromMessage(message.content) + } val targetRooms = toRoomIds.toSet().mapNotNull { roomId -> roomListService.roomOrNull(roomId.value) } val failedForwardingTo = mutableSetOf() targetRooms.parallelMap { room -> room.use { targetRoom -> runCatchingExceptions { - // Sending a message requires a registered timeline listener - targetRoom.timeline().runWithTimelineListenerRegistered { - withTimeout(timeoutMs.milliseconds) { - targetRoom.timeline().send(contentWithoutRelationFromMessage(content)) + targetRoom.timeline().use { timeline -> + // Send the contents sequentially, so that they are received in the same order + contents.forEach { content -> + withTimeout(timeoutMs.milliseconds) { + timeline.send(content) + } } } } 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 c932230217..9766da8235 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 @@ -53,16 +53,3 @@ internal fun TimelineInterface.timelineDiffFlow(): Flow> = }.catch { Timber.d(it, "timelineDiffFlow() failed") }.buffer(Channel.UNLIMITED) - -internal suspend fun TimelineInterface.runWithTimelineListenerRegistered(action: suspend () -> Unit) { - val result = addListener(NoOpTimelineListener) - try { - action() - } finally { - result.cancelAndDestroy() - } -} - -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/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index c6a737e156..c7dd260d47 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -489,9 +489,9 @@ class RustTimeline( } } - override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = withContext(dispatcher) { + override suspend fun forwardEvents(eventIds: List, roomIds: List): Result = withContext(dispatcher) { runCatchingExceptions { - roomContentForwarder.forward(fromTimeline = inner, eventId = eventId, toRoomIds = roomIds) + roomContentForwarder.forward(fromTimeline = inner, eventIds = eventIds, toRoomIds = roomIds) }.onFailure { Timber.tag(loggerTag).e(it) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index d13ac06bd3..88dcb13a8c 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -355,10 +355,10 @@ class FakeTimeline( ) } - var forwardEventLambda: (eventId: EventId, roomIds: List) -> Result = { _, _ -> lambdaError() } + var forwardEventsLambda: (eventIds: List, roomIds: List) -> Result = { _, _ -> lambdaError() } - override suspend fun forwardEvent(eventId: EventId, roomIds: List): Result = simulateLongTask { - forwardEventLambda(eventId, roomIds) + override suspend fun forwardEvents(eventIds: List, roomIds: List): Result = simulateLongTask { + forwardEventsLambda(eventIds, roomIds) } var createPollLambda: ( From a0bfdd379c41dcf3868986777afb20faf30b077f Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:47:17 +0200 Subject: [PATCH 03/92] Pass a list of event ids to the forward screen --- .../appnav/room/joined/JoinedRoomLoadedFlowNode.kt | 2 +- .../features/messages/impl/MessagesFlowNode.kt | 14 +++++++------- .../features/messages/impl/MessagesNavigator.kt | 2 +- .../android/features/messages/impl/MessagesNode.kt | 6 +++--- .../messages/impl/threads/ThreadedMessagesNode.kt | 6 +++--- .../messages/impl/FakeMessagesNavigator.kt | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index dbe53b75ac..7205d095b4 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -201,7 +201,7 @@ class JoinedRoomLoadedFlowNode( } else { (graph as TimelineBindings).timelineProvider } - val params = ForwardEntryPoint.Params(navTarget.eventId, timelineProvider) + val params = ForwardEntryPoint.Params(listOf(navTarget.eventId), timelineProvider) val callback = object : ForwardEntryPoint.Callback { override fun onDone(roomIds: List) { backstack.pop() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 88a90f6c7d..4c86c75dd8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -179,7 +179,7 @@ class MessagesFlowNode( @Parcelize data class ForwardEvent( - val eventId: EventId, + val eventIds: List, val fromPinnedEvents: Boolean, ) : NavTarget @@ -303,8 +303,8 @@ class MessagesFlowNode( backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } - override fun forwardEvent(eventId: EventId) { - backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false)) + override fun forwardEvents(eventIds: List) { + backstack.push(NavTarget.ForwardEvent(eventIds, fromPinnedEvents = false)) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { @@ -464,7 +464,7 @@ class MessagesFlowNode( } else { timelineController } - val params = ForwardEntryPoint.Params(navTarget.eventId, timelineProvider) + val params = ForwardEntryPoint.Params(navTarget.eventIds, timelineProvider) val callback = object : ForwardEntryPoint.Callback { override fun onDone(roomIds: List) { backstack.pop() @@ -551,7 +551,7 @@ class MessagesFlowNode( } override fun handleForwardEventClick(eventId: EventId) { - backstack.push(NavTarget.ForwardEvent(eventId = eventId, fromPinnedEvents = true)) + backstack.push(NavTarget.ForwardEvent(eventIds = listOf(eventId), fromPinnedEvents = true)) } override fun navigateToThread(threadRootId: ThreadId) { @@ -617,8 +617,8 @@ class MessagesFlowNode( backstack.push(NavTarget.EventDebugInfo(eventId, debugInfo)) } - override fun handleForwardEventClick(eventId: EventId) { - backstack.push(NavTarget.ForwardEvent(eventId, fromPinnedEvents = false)) + override fun handleForwardEventClick(eventIds: List) { + backstack.push(NavTarget.ForwardEvent(eventIds, fromPinnedEvents = false)) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt index 6113b68aab..b51c516214 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNavigator.kt @@ -18,7 +18,7 @@ import kotlinx.collections.immutable.ImmutableList interface MessagesNavigator { fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun forwardEvent(eventId: EventId) + fun forwardEvents(eventIds: List) fun navigateToReportMessage(eventId: EventId, senderId: UserId) fun navigateToEditPoll(eventId: EventId) fun navigateToPreviewAttachments(attachments: ImmutableList, inReplyToEventId: EventId?) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 97d3465ce7..d95ba0bd45 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -130,7 +130,7 @@ class MessagesNode( fun navigateToRoomMemberDetails(userId: UserId) fun handlePermalinkClick(data: PermalinkData) fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun forwardEvent(eventId: EventId) + fun forwardEvents(eventIds: List) fun navigateToReportMessage(eventId: EventId, senderId: UserId) fun navigateToSendLocation() fun navigateToCreatePoll() @@ -213,8 +213,8 @@ class MessagesNode( callback.navigateToEventDebugInfo(eventId, debugInfo) } - override fun forwardEvent(eventId: EventId) { - callback.forwardEvent(eventId) + override fun forwardEvents(eventIds: List) { + callback.forwardEvents(eventIds) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt index cf3c701015..01d466d83b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/threads/ThreadedMessagesNode.kt @@ -135,7 +135,7 @@ class ThreadedMessagesNode( fun navigateToRoomMemberDetails(userId: UserId) fun handlePermalinkClick(data: PermalinkData) fun navigateToEventDebugInfo(eventId: EventId?, debugInfo: TimelineItemDebugInfo) - fun handleForwardEventClick(eventId: EventId) + fun handleForwardEventClick(eventIds: List) fun navigateToReportMessage(eventId: EventId, senderId: UserId) fun navigateToSendLocation() fun navigateToCreatePoll() @@ -219,8 +219,8 @@ class ThreadedMessagesNode( callback.navigateToEventDebugInfo(eventId, debugInfo) } - override fun forwardEvent(eventId: EventId) { - callback.handleForwardEventClick(eventId) + override fun forwardEvents(eventIds: List) { + callback.handleForwardEventClick(eventIds) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt index 68f9a8d17a..1d9243af3d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/FakeMessagesNavigator.kt @@ -19,7 +19,7 @@ import kotlinx.collections.immutable.ImmutableList class FakeMessagesNavigator( private val onShowEventDebugInfoClickLambda: (eventId: EventId?, debugInfo: TimelineItemDebugInfo) -> Unit = { _, _ -> lambdaError() }, - private val onForwardEventClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, + private val onForwardEventClickLambda: (eventIds: List) -> Unit = { _ -> lambdaError() }, private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() }, private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() }, private val onPreviewAttachmentLambda: (attachments: ImmutableList, inReplyToEventId: EventId?) -> Unit = { _, _ -> lambdaError() }, @@ -34,8 +34,8 @@ class FakeMessagesNavigator( onShowEventDebugInfoClickLambda(eventId, debugInfo) } - override fun forwardEvent(eventId: EventId) { - onForwardEventClickLambda(eventId) + override fun forwardEvents(eventIds: List) { + onForwardEventClickLambda(eventIds) } override fun navigateToReportMessage(eventId: EventId, senderId: UserId) { From cec42ae4e6fff161475d491c57c372c3f621977b Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:47:28 +0200 Subject: [PATCH 04/92] Enter selection mode from the Forward action --- .../features/messages/impl/MessagesEvent.kt | 1 + .../messages/impl/MessagesPresenter.kt | 32 ++++--- .../features/messages/impl/MessagesView.kt | 8 +- .../impl/actionlist/ActionListPresenter.kt | 9 -- .../actionlist/model/TimelineItemAction.kt | 1 - .../model/TimelineItemActionComparator.kt | 1 - .../messages/impl/timeline/SelectionState.kt | 30 ++++++- .../messages/impl/timeline/TimelineEvent.kt | 2 +- .../impl/timeline/TimelinePresenter.kt | 26 +++--- .../messages/impl/timeline/TimelineState.kt | 37 +++++++- .../messages/impl/timeline/TimelineView.kt | 12 +-- .../components/TimelineItemSelectionData.kt | 1 + .../timeline/model/TimelineItemSelection.kt | 34 -------- .../impl/topbars/SelectionModeTopBar.kt | 21 ++++- .../messages/impl/MessagesPresenterTest.kt | 84 +++++++++++++++++-- .../actionlist/ActionListPresenterTest.kt | 38 --------- .../impl/timeline/SelectionActionTest.kt | 60 +++++++++++++ .../impl/timeline/TimelinePresenterTest.kt | 38 +++++++-- .../impl/timeline/TimelineStateTest.kt | 77 +++++++++++++++++ .../model/TimelineItemSelectionTest.kt | 47 ----------- 20 files changed, 379 insertions(+), 180 deletions(-) delete mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt delete mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt index 4d621e417f..adbf1f15ab 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvent.kt @@ -21,6 +21,7 @@ sealed interface MessagesEvent { data object StopLiveLocationShare : MessagesEvent data object ShowLiveLocationShare : MessagesEvent data object MarkAsFullyReadAndExit : MessagesEvent + data object ConfirmSelectionAction : MessagesEvent } enum class InviteDialogAction { 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 bcbfc35255..5a24c64787 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 @@ -39,6 +39,8 @@ import io.element.android.features.messages.impl.messagecomposer.MessageComposer import io.element.android.features.messages.impl.messagecomposer.MessageComposerState import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerState import io.element.android.features.messages.impl.timeline.MarkAsFullyRead +import io.element.android.features.messages.impl.timeline.SelectionAction +import io.element.android.features.messages.impl.timeline.SelectionState import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.TimelineState @@ -53,6 +55,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.captionOrNull import io.element.android.features.messages.impl.timeline.model.event.htmlCaptionOrNull import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState +import io.element.android.features.messages.impl.timeline.selectedEventIdsInTimelineOrder import io.element.android.features.messages.impl.voicemessages.composer.DefaultVoiceMessageComposerPresenter import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvent @@ -253,6 +256,17 @@ class MessagesPresenter( timelineProtectionState = timelineProtectionState, ) } + is MessagesEvent.ConfirmSelectionAction -> { + val selection = timelineState.selectionState as? SelectionState.Active + if (selection != null) { + when (selection.action) { + SelectionAction.Forward -> { + navigator.forwardEvents(timelineState.selectedEventIdsInTimelineOrder()) + } + } + timelineState.eventSink(TimelineEvent.ExitSelectionMode) + } + } is MessagesEvent.ToggleReaction -> { localCoroutineScope.toggleReaction(event.emoji, event.eventOrTransactionId) } @@ -395,17 +409,20 @@ class MessagesPresenter( } } TimelineItemAction.ViewSource -> handleShowDebugInfoAction(targetEvent) - TimelineItemAction.Forward -> handleForwardAction(targetEvent) + TimelineItemAction.Forward -> { + val eventId = targetEvent.eventId ?: return@launch + if (featureFlagService.isFeatureEnabled(FeatureFlags.MessageMultiSelect)) { + timelineState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, eventId)) + } else { + navigator.forwardEvents(listOf(eventId)) + } + } TimelineItemAction.ReportContent -> handleReportAction(targetEvent) TimelineItemAction.EndPoll -> handleEndPollAction(targetEvent, timelineState) TimelineItemAction.Pin -> handlePinAction(targetEvent) TimelineItemAction.Unpin -> handleUnpinAction(targetEvent) TimelineItemAction.ViewInTimeline -> Unit TimelineItemAction.RetrySending -> handleRetrySending(targetEvent) - TimelineItemAction.Select -> { - val eventId = targetEvent.eventId ?: return@launch - timelineState.eventSink(TimelineEvent.EnterSelectionMode(eventId)) - } } } @@ -588,11 +605,6 @@ class MessagesPresenter( navigator.navigateToEventDebugInfo(event.eventId, event.debugInfo) } - private fun handleForwardAction(event: TimelineItem.Event) { - if (event.eventId == null) return - navigator.forwardEvent(event.eventId) - } - private fun handleReportAction(event: TimelineItem.Event) { if (event.eventId == null) return navigator.navigateToReportMessage(event.eventId, event.senderId) 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 c4bcf552b5..0a82e52d08 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 @@ -10,8 +10,6 @@ package io.element.android.features.messages.impl import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -79,6 +77,7 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerView import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerViewDefaults import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS +import io.element.android.features.messages.impl.timeline.SelectionState import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.TimelineView import io.element.android.features.messages.impl.timeline.aGroupedEvents @@ -248,11 +247,16 @@ fun MessagesView( topBar = { when { state.timelineState.isSelectionModeActive -> { + val selection = state.timelineState.selectionState as SelectionState.Active SelectionModeTopBar( selectedCount = state.timelineState.selectedCount, + action = selection.action, onCancelClick = { state.timelineState.eventSink(TimelineEvent.ExitSelectionMode) }, + onConfirmClick = { + state.eventSink(MessagesEvent.ConfirmSelectionAction) + }, ) } state.timelineState.timelineMode is Timeline.Mode.Thread -> { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 66ed7dd757..8caa95f31d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -27,7 +27,6 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUserSendFailureFactory import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo -import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAttachmentsContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentWithAttachment @@ -110,7 +109,6 @@ class DefaultActionListPresenter( }.collectAsState(initial = persistentListOf()) val isThreadsEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.Threads).collectAsState(false) - val isMultiSelectEnabled = featureFlagService.isFeatureEnabledFlow(FeatureFlags.MessageMultiSelect).collectAsState(false) fun handleEvent(event: ActionListEvent) { when (event) { @@ -122,7 +120,6 @@ class DefaultActionListPresenter( pinnedEventIds = pinnedEventIds, target = target, isThreadsEnabled = isThreadsEnabled.value, - isMultiSelectEnabled = isMultiSelectEnabled.value, ) } } @@ -140,7 +137,6 @@ class DefaultActionListPresenter( pinnedEventIds: ImmutableList, target: MutableState, isThreadsEnabled: Boolean, - isMultiSelectEnabled: Boolean, ) = launch { target.value = ActionListState.Target.Loading(timelineItem) @@ -150,7 +146,6 @@ class DefaultActionListPresenter( isDeveloperModeEnabled = isDeveloperModeEnabled, isEventPinned = pinnedEventIds.contains(timelineItem.eventId), isThreadsEnabled = isThreadsEnabled, - isMultiSelectEnabled = isMultiSelectEnabled, ) val verifiedUserSendFailure = userSendFailureFactory.create(timelineItem.localSendState) @@ -184,7 +179,6 @@ class DefaultActionListPresenter( isDeveloperModeEnabled: Boolean, isEventPinned: Boolean, isThreadsEnabled: Boolean, - isMultiSelectEnabled: Boolean, ): List { val canRedact = timelineItem.isMine && usersEventPermissions.canRedactOwn || !timelineItem.isMine && usersEventPermissions.canRedactOther return buildSet { @@ -206,9 +200,6 @@ class DefaultActionListPresenter( if (timelineItem.isRemote && timelineItem.content.canBeForwarded()) { add(TimelineItemAction.Forward) } - if (isMultiSelectEnabled && timelineItem.canBeSelected()) { - add(TimelineItemAction.Select) - } if (timelineItem.isEditable && usersEventPermissions.canSendMessage) { if (timelineItem.content is TimelineItemEventContentWithAttachment || timelineItem.content is TimelineItemGalleryContent || 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 c7ee198ebc..3c64223e59 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 @@ -20,7 +20,6 @@ enum class TimelineItemAction( ) { ViewInTimeline(CommonStrings.action_view_in_timeline, CompoundDrawables.ic_compound_visibility_on), Forward(CommonStrings.action_forward, CompoundDrawables.ic_compound_forward), - Select(CommonStrings.action_select, CompoundDrawables.ic_compound_check), CopyText(CommonStrings.action_copy_text, CompoundDrawables.ic_compound_copy), CopyCaption(CommonStrings.action_copy_caption, CompoundDrawables.ic_compound_copy), CopyLink(CommonStrings.action_copy_link_to_message, CompoundDrawables.ic_compound_link), diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt index 86b62794ba..2f6fa4e4e0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemActionComparator.kt @@ -19,7 +19,6 @@ class TimelineItemActionComparator : Comparator { TimelineItemAction.Reply, TimelineItemAction.ReplyInThread, TimelineItemAction.Forward, - TimelineItemAction.Select, TimelineItemAction.Edit, TimelineItemAction.EditPoll, TimelineItemAction.AddCaption, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt index 91b0d2e73a..f2738480aa 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt @@ -7,18 +7,42 @@ package io.element.android.features.messages.impl.timeline +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.runtime.Immutable +import io.element.android.compound.R +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.canBeForwarded import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableSet /** Maximum number of messages that can be selected at once. */ -const val MAX_SELECTION_COUNT = 30 +const val MAX_SELECTION_COUNT = 10 @Immutable sealed interface SelectionState { /** Selection mode is not active. */ data object Disabled : SelectionState - /** Selection mode is active with the given selection. */ - data class Active(val selectedEventIds: ImmutableSet) : SelectionState + /** Selection mode is active for [action], with the given selection. */ + data class Active( + val action: SelectionAction, + val selectedEventIds: ImmutableSet, + ) : SelectionState +} + +/** + * An action which can be performed on several timeline events at once, and which selection mode can be entered for. + */ +enum class SelectionAction(@field:StringRes val titleRes: Int, @field:DrawableRes val iconRes: Int) { + Forward(CommonStrings.action_forward, R.drawable.ic_compound_forward), +} + +/** + * Whether [event] can be part of a selection made for this action. + * Each action uses the same criteria as the single event action it mirrors. + */ +fun SelectionAction.canApplyTo(event: TimelineItem.Event): Boolean = when (this) { + SelectionAction.Forward -> event.isRemote && event.content.canBeForwarded() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt index f21bfe88aa..ac67d93784 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt @@ -29,7 +29,7 @@ sealed interface TimelineEvent { data object MarkAllAsRead : TimelineEvent - data class EnterSelectionMode(val eventId: EventId) : TimelineEvent + data class EnterSelectionMode(val action: SelectionAction, val eventId: EventId) : TimelineEvent data class ToggleSelection(val eventId: EventId) : TimelineEvent data object ExitSelectionMode : TimelineEvent 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 80812264fd..320e362024 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,7 +34,6 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemReadMarkerModel import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemTypingNotificationModel import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionEvent @@ -265,7 +264,7 @@ class TimelinePresenter( } is TimelineEvent.EnterSelectionMode -> { if (!isMultiSelectEnabled) return - selectionState = SelectionState.Active(persistentSetOf(event.eventId)) + selectionState = SelectionState.Active(event.action, persistentSetOf(event.eventId)) } is TimelineEvent.ToggleSelection -> { val current = selectionState as? SelectionState.Active ?: return @@ -273,13 +272,13 @@ class TimelinePresenter( selectionState = when { event.eventId in selected -> { val next = (selected - event.eventId).toPersistentSet() - if (next.isEmpty()) SelectionState.Disabled else SelectionState.Active(next) + if (next.isEmpty()) SelectionState.Disabled else current.copy(selectedEventIds = next) } selected.size >= MAX_SELECTION_COUNT -> { snackbarDispatcher.post(SnackbarMessage(R.string.screen_room_timeline_selection_limit_reached)) current } - else -> SelectionState.Active((selected + event.eventId).toPersistentSet()) + else -> current.copy(selectedEventIds = (selected + event.eventId).toPersistentSet()) } } is TimelineEvent.ExitSelectionMode -> { @@ -394,19 +393,22 @@ class TimelinePresenter( computeNewItemState(timelineItems, prevMostRecentItemId, newEventState) } - // Reconcile the selection when the items change: drop any selected event that is no longer - // present or no longer selectable, and exit selection mode if nothing remains selected. - LaunchedEffect(timelineItems.map { it.identifier() }) { - val active = selectionState as? SelectionState.Active ?: return@LaunchedEffect - val stillSelectable = timelineItems + val selectableEventIds = (selectionState as? SelectionState.Active)?.let { active -> + timelineItems .filterIsInstance() - .filter { it.canBeSelected() } + .filter { active.action.canApplyTo(it) } .mapNotNull { it.eventId } .toSet() - val reconciled = active.selectedEventIds.filter { it in stillSelectable }.toPersistentSet() + }.orEmpty() + + // Reconcile the selection when the items change: drop any selected event that is no longer + // present or no longer selectable, and exit selection mode if nothing remains selected. + LaunchedEffect(selectableEventIds) { + val active = selectionState as? SelectionState.Active ?: return@LaunchedEffect + val reconciled = active.selectedEventIds.filter { it in selectableEventIds }.toPersistentSet() selectionState = when { reconciled.isEmpty() -> SelectionState.Disabled - reconciled.size != active.selectedEventIds.size -> SelectionState.Active(reconciled) + reconciled.size != active.selectedEventIds.size -> active.copy(selectedEventIds = reconciled) else -> active } } 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 6bd5aa0895..bc571f6634 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 @@ -21,6 +21,8 @@ import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import io.element.android.libraries.matrix.api.timeline.Timeline import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration data class TimelineState( @@ -108,7 +110,36 @@ val TimelineState.isSelectionModeActive: Boolean val TimelineState.selectedCount: Int get() = (selectionState as? SelectionState.Active)?.selectedEventIds?.size ?: 0 -fun TimelineState.isSelected(eventId: EventId?): Boolean { - if (eventId == null) return false - return (selectionState as? SelectionState.Active)?.selectedEventIds?.contains(eventId) == true +fun TimelineState.isSelected(item: TimelineItem): Boolean { + val selection = selectionState as? SelectionState.Active ?: return false + return when(item){ + is TimelineItem.Event -> selection.selectedEventIds.contains(item.eventId) + else -> false + } +} + +/** + * Whether [item] can be selected right now: selection mode must be active, and the action it was entered for must accept this event. + */ +fun TimelineState.canSelect(item: TimelineItem): Boolean { + val selection = selectionState as? SelectionState.Active ?: return false + return when(item) { + is TimelineItem.Event -> selection.action.canApplyTo(item) + else -> false + } +} + +/** + * The selected event ids, sorted from the oldest to the most recent one, as they appear in the timeline. + * The selection itself is stored as a set, in the order the events were tapped, which is not necessarily the order they must be acted on. + */ +fun TimelineState.selectedEventIdsInTimelineOrder(): ImmutableList { + val selectedEventIds = (selectionState as? SelectionState.Active)?.selectedEventIds ?: return persistentListOf() + return timelineItems + .filterIsInstance() + .mapNotNull { it.eventId } + .filter { it in selectedEventIds } + // Most recent item is first in the timeline, so reverse the result. + .reversed() + .toImmutableList() } 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 0df9be3ca0..eb9eef7c8f 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 @@ -91,7 +91,6 @@ import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPr import io.element.android.features.messages.impl.timeline.focus.FocusRequestStateView import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentPreviewParam import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent @@ -234,17 +233,18 @@ fun TimelineView( selectionData = TimelineItemSelectionData( isSelectionModeActive = isSelectionModeActive, progress = selectionProgress, - isSelected = state.isSelected((timelineItem as? TimelineItem.Event)?.eventId), + isSelected = state.isSelected(timelineItem), + canBeSelected = state.canSelect(timelineItem), ), onUserDataClick = onUserDataClick, onLinkClick = onLinkClick, onLinkLongClick = ::onLinkLongClick, onContentClick = { event -> - if (isSelectionModeActive && event.canBeSelected()) onToggleSelection(event) else onContentClick(event) + if (state.canSelect(event)) onToggleSelection(event) else onContentClick(event) }, onGalleryItemClick = onGalleryItemClick, onLongClick = { event -> - if (isSelectionModeActive && event.canBeSelected()) onToggleSelection(event) else onMessageLongClick(event) + if (state.canSelect(event)) onToggleSelection(event) else onMessageLongClick(event) }, inReplyToClick = ::inReplyToClick, onReactionClick = onReactionClick, @@ -646,14 +646,14 @@ internal fun TimelineViewPreview( @Composable internal fun TimelineViewSelectionModePreview() = ElementPreview { val timelineItems = aTimelineItemList(aTimelineItemTextContent()) - val selectedEventId = timelineItems.filterIsInstance().first { it.canBeSelected() }.eventId!! + val selectedEventId = timelineItems.filterIsInstance().first { SelectionAction.Forward.canApplyTo(it) }.eventId!! CompositionLocalProvider( LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(), ) { TimelineView( state = aTimelineState( timelineItems = timelineItems, - selectionState = SelectionState.Active(persistentSetOf(selectedEventId)), + selectionState = SelectionState.Active(SelectionAction.Forward, persistentSetOf(selectedEventId)), ), timelineProtectionState = aTimelineProtectionState(), onUserDataClick = {}, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt index f8de354aac..641be44fe8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt @@ -20,5 +20,6 @@ package io.element.android.features.messages.impl.timeline.components data class TimelineItemSelectionData( val isSelectionModeActive: Boolean = false, val isSelected: Boolean = false, + val canBeSelected: Boolean = false, val progress: Float = 0f, ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt deleted file mode 100644 index 9f11e86213..0000000000 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelection.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2026 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.messages.impl.timeline.model - -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRtcNotificationContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent - -/** - * Whether this timeline event can be selected in multi-select ("selection") mode. - */ -fun TimelineItem.Event.canBeSelected(): Boolean { - // Local echoes (no remote eventId) and failed sends are never selectable. - if (!isRemote || failedToSend) return false - return when (content) { - is TimelineItemStateContent, - is TimelineItemRedactedContent, - is TimelineItemEncryptedContent, - is TimelineItemLegacyCallInviteContent, - is TimelineItemRtcNotificationContent, - is TimelineItemLocationContent, - is TimelineItemUnknownContent -> false - else -> true - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt index 55563d1f1b..1e96d7b1e1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.R +import io.element.android.features.messages.impl.timeline.SelectionAction 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 @@ -28,7 +29,9 @@ import io.element.android.libraries.ui.strings.CommonStrings @Composable fun SelectionModeTopBar( selectedCount: Int, + action: SelectionAction, onCancelClick: () -> Unit, + onConfirmClick: () -> Unit, modifier: Modifier = Modifier, ) { TopAppBar( @@ -43,6 +46,17 @@ fun SelectionModeTopBar( Icon(imageVector = CompoundIcons.Close(), contentDescription = stringResource(CommonStrings.action_close)) } }, + actions = { + IconButton( + onClick = onConfirmClick, + enabled = selectedCount > 0, + ) { + Icon( + resourceId = action.iconRes, + contentDescription = stringResource(action.titleRes), + ) + } + }, ) } @@ -51,7 +65,12 @@ fun SelectionModeTopBar( internal fun SelectionModeTopBarPreview( @PreviewParameter(SelectionCountProvider::class) count: Int, ) = ElementPreview { - SelectionModeTopBar(selectedCount = count, onCancelClick = {}) + SelectionModeTopBar( + selectedCount = count, + action = SelectionAction.Forward, + onCancelClick = {}, + onConfirmClick = {}, + ) } internal class SelectionCountProvider : PreviewParameterProvider { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt index 4c0abc0a34..152cb8a7a0 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt @@ -28,8 +28,12 @@ import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMess import io.element.android.features.messages.impl.threads.list.aThreadListItem import io.element.android.features.messages.impl.timeline.FakeMarkAsFullyRead import io.element.android.features.messages.impl.timeline.MarkAsFullyRead +import io.element.android.features.messages.impl.timeline.SelectionAction +import io.element.android.features.messages.impl.timeline.SelectionState import io.element.android.features.messages.impl.timeline.TimelineController import io.element.android.features.messages.impl.timeline.TimelineEvent +import io.element.android.features.messages.impl.timeline.TimelineState +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent import io.element.android.features.messages.impl.timeline.aTimelineState import io.element.android.features.messages.impl.timeline.model.TimelineItemThreadInfo import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent @@ -76,6 +80,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSen import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_AVATAR_URL 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.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -113,6 +118,7 @@ import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testWithLifecycleOwner import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -285,17 +291,37 @@ class MessagesPresenterTest { } @Test - fun `present - handle action forward`() = runTest { - val onForwardEventClickLambda = lambdaRecorder { } - val navigator = FakeMessagesNavigator( - onForwardEventClickLambda = onForwardEventClickLambda, + fun `present - handle action forward enters selection mode when multi select is enabled`() = runTest { + val timelineEventSink = lambdaRecorder { } + val presenter = createMessagesPresenter( + timelineEventSink = timelineEventSink, + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), ) - val presenter = createMessagesPresenter(navigator = navigator) presenter.testWithLifecycleOwner { val initialState = awaitItem() initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Forward, aMessageEvent())) assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) - onForwardEventClickLambda.assertions().isCalledOnce().with(value(AN_EVENT_ID)) + timelineEventSink.assertions().isCalledOnce() + .with(value(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID))) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - handle action forward navigates directly when multi select is disabled`() = runTest { + val onForwardEventClickLambda = lambdaRecorder, Unit> { } + val navigator = FakeMessagesNavigator( + onForwardEventClickLambda = onForwardEventClickLambda, + ) + val presenter = createMessagesPresenter( + navigator = navigator, + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to false)), + ) + presenter.testWithLifecycleOwner { + val initialState = awaitItem() + initialState.eventSink(MessagesEvent.HandleAction(TimelineItemAction.Forward, aMessageEvent())) + assertThat(awaitItem().actionListState.target).isEqualTo(ActionListState.Target.None) + onForwardEventClickLambda.assertions().isCalledOnce().with(value(listOf(AN_EVENT_ID))) } } @@ -1424,6 +1450,49 @@ class MessagesPresenterTest { canPinUnpin = canPinUnpin, ) + @Test + fun `present - confirm selection action forwards the selection in timeline order`() = runTest { + val onForwardEventClickLambda = lambdaRecorder, Unit> { } + val navigator = FakeMessagesNavigator(onForwardEventClickLambda = onForwardEventClickLambda) + val timelineEventSink = lambdaRecorder { } + val presenter = createMessagesPresenter( + navigator = navigator, + timelineEventSink = timelineEventSink, + timelineState = aTimelineState( + // The timeline is rendered from the most recent event to the oldest one. + timelineItems = persistentListOf( + aTimelineItemEvent(eventId = AN_EVENT_ID_2), + aTimelineItemEvent(eventId = AN_EVENT_ID), + ), + selectionState = SelectionState.Active( + SelectionAction.Forward, + persistentSetOf(AN_EVENT_ID_2, AN_EVENT_ID), + ), + eventSink = timelineEventSink, + ), + ) + presenter.testWithLifecycleOwner { + val initialState = awaitItem() + initialState.eventSink(MessagesEvent.ConfirmSelectionAction) + onForwardEventClickLambda.assertions().isCalledOnce().with(value(listOf(AN_EVENT_ID, AN_EVENT_ID_2))) + timelineEventSink.assertions().isCalledOnce().with(value(TimelineEvent.ExitSelectionMode)) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - confirm selection action does nothing when no selection is active`() = runTest { + val onForwardEventClickLambda = lambdaRecorder, Unit> { } + val navigator = FakeMessagesNavigator(onForwardEventClickLambda = onForwardEventClickLambda) + val presenter = createMessagesPresenter(navigator = navigator) + presenter.testWithLifecycleOwner { + val initialState = awaitItem() + initialState.eventSink(MessagesEvent.ConfirmSelectionAction) + onForwardEventClickLambda.assertions().isNeverCalled() + cancelAndIgnoreRemainingEvents() + } + } + private fun TestScope.createMessagesPresenter( coroutineDispatchers: CoroutineDispatchers = testCoroutineDispatchers(), timeline: Timeline = FakeTimeline(), @@ -1446,6 +1515,7 @@ class MessagesPresenterTest { clipboardHelper: FakeClipboardHelper = FakeClipboardHelper(), analyticsService: FakeAnalyticsService = FakeAnalyticsService(), timelineEventSink: (TimelineEvent) -> Unit = {}, + timelineState: TimelineState = aTimelineState(eventSink = timelineEventSink), permalinkParser: PermalinkParser = FakePermalinkParser(), messageComposerPresenter: Presenter = Presenter { aMessageComposerState( @@ -1468,7 +1538,7 @@ class MessagesPresenterTest { room = joinedRoom, composerPresenter = messageComposerPresenter, voiceMessageComposerPresenterFactory = FakeDefaultVoiceMessageComposerPresenterFactory(backgroundScope), - timelinePresenter = { aTimelineState(eventSink = timelineEventSink) }, + timelinePresenter = { timelineState }, timelineProtectionPresenter = { aTimelineProtectionState() }, identityChangeStatePresenter = { anIdentityChangeState() }, linkPresenter = { aLinkState() }, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt index 2988fbe894..e987a32480 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt @@ -70,44 +70,6 @@ class ActionListPresenterTest { } } - @Test - fun `present - Select action is present for a remote message when multi-select is enabled`() = runTest { - val presenter = createActionListPresenter( - isDeveloperModeEnabled = false, - featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke( - ActionListEvent.ComputeForMessage( - event = aMessageEvent(), - userEventPermissions = aUserEventPermissions(canSendMessage = true), - ) - ) - val successState = awaitItem() - assertThat((successState.target as ActionListState.Target.Success).actions).contains(TimelineItemAction.Select) - } - } - - @Test - fun `present - Select action is absent when multi-select is disabled`() = runTest { - val presenter = createActionListPresenter( - isDeveloperModeEnabled = false, - featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to false)), - ) - presenter.test { - val initialState = awaitItem() - initialState.eventSink.invoke( - ActionListEvent.ComputeForMessage( - event = aMessageEvent(), - userEventPermissions = aUserEventPermissions(canSendMessage = true), - ) - ) - val successState = awaitItem() - assertThat((successState.target as ActionListState.Target.Success).actions).doesNotContain(TimelineItemAction.Select) - } - } - @Test fun `present - compute for message from me redacted`() = runTest { val presenter = createActionListPresenter(isDeveloperModeEnabled = true) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt new file mode 100644 index 0000000000..4939ec3a50 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.timeline.model.event.aStaticLocationMode +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStickerContent +import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import org.junit.Test + +class SelectionActionTest { + @Test + fun `Forward - a remote text message can be selected`() { + assertThat(SelectionAction.Forward.canApplyTo(aTimelineItemEvent())).isTrue() + } + + @Test + fun `Forward - a local echo cannot be selected`() { + assertThat(SelectionAction.Forward.canApplyTo(aTimelineItemEvent().copy(eventId = null))).isFalse() + } + + @Test + fun `Forward - a failed to send message cannot be selected`() { + // A message which failed to send is a local echo: it has no event id. + val item = aTimelineItemEvent(sendState = LocalEventSendState.Failed.Unknown("failed")).copy(eventId = null) + assertThat(SelectionAction.Forward.canApplyTo(item)).isFalse() + } + + @Test + fun `Forward - a state event cannot be selected`() { + val item = aTimelineItemEvent(content = aTimelineItemStateEventContent()) + assertThat(SelectionAction.Forward.canApplyTo(item)).isFalse() + } + + @Test + fun `Forward - a redacted event cannot be selected`() { + val item = aTimelineItemEvent(content = aTimelineItemRedactedContent()) + assertThat(SelectionAction.Forward.canApplyTo(item)).isFalse() + } + + @Test + fun `Forward - a sticker cannot be selected, it cannot be forwarded`() { + val item = aTimelineItemEvent(content = aTimelineItemStickerContent()) + assertThat(SelectionAction.Forward.canApplyTo(item)).isFalse() + } + + @Test + fun `Forward - a static location can be selected, it can be forwarded`() { + val item = aTimelineItemEvent(content = aTimelineItemLocationContent(mode = aStaticLocationMode())) + assertThat(SelectionAction.Forward.canApplyTo(item)).isTrue() + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 6dcf107e0b..d6534deb23 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -50,8 +50,10 @@ import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState +import io.element.android.libraries.matrix.api.timeline.item.event.OtherState 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.StateContent import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -1835,9 +1837,9 @@ class TimelinePresenterTest { presenter.test { val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() assertThat(initialState.selectionState).isEqualTo(SelectionState.Disabled) - initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) val state = consumeItemsUntilPredicate { it.isSelectionModeActive }.last() - assertThat(state.selectionState).isEqualTo(SelectionState.Active(persistentSetOf(AN_EVENT_ID))) + assertThat(state.selectionState).isEqualTo(SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID))) assertThat(state.selectedCount).isEqualTo(1) cancelAndIgnoreRemainingEvents() } @@ -1849,7 +1851,7 @@ class TimelinePresenterTest { val presenter = createTimelinePresenter(featureFlagService = featureFlagService) presenter.test { val initialState = awaitFirstItem() - initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) expectNoEvents() assertThat(initialState.selectionState).isEqualTo(SelectionState.Disabled) cancelAndIgnoreRemainingEvents() @@ -1867,7 +1869,7 @@ class TimelinePresenterTest { ) presenter.test { val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() - initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) consumeItemsUntilPredicate { it.isSelectionModeActive } initialState.eventSink(TimelineEvent.ToggleSelection(AN_EVENT_ID)) val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() @@ -1888,7 +1890,7 @@ class TimelinePresenterTest { ) presenter.test { val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() - initialState.eventSink(TimelineEvent.EnterSelectionMode(AN_EVENT_ID)) + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) consumeItemsUntilPredicate { it.isSelectionModeActive } featureFlagService.setFeatureEnabled(FeatureFlags.MessageMultiSelect, false) val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() @@ -1897,6 +1899,32 @@ class TimelinePresenterTest { } } + @Test + fun `present - selection drops events the action cannot apply to`() = runTest { + val timelineItems = MutableStateFlow( + listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) + ) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) + consumeItemsUntilPredicate { it.isSelectionModeActive } + // The message is replaced by a state event, which cannot be forwarded. + timelineItems.value = listOf( + MatrixTimelineItem.Event( + UniqueId("1"), + anEventTimelineItem(eventId = AN_EVENT_ID, content = StateContent("", OtherState.RoomCreate)), + ) + ) + val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Disabled) + cancelAndIgnoreRemainingEvents() + } + } + private suspend fun ReceiveTurbine.awaitFirstItem(): T { return awaitItem() } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt new file mode 100644 index 0000000000..3ee818e438 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent +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.AN_EVENT_ID_3 +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentSetOf +import org.junit.Test + +class TimelineStateTest { + @Test + fun `selectedEventIdsInTimelineOrder - when selection is disabled, the list is empty`() { + val state = aTimelineState( + timelineItems = persistentListOf(aTimelineItemEvent(eventId = AN_EVENT_ID)), + selectionState = SelectionState.Disabled, + ) + assertThat(state.selectedEventIdsInTimelineOrder()).isEmpty() + } + + @Test + fun `selectedEventIdsInTimelineOrder - the events are sorted from the oldest to the most recent one`() { + // The timeline items are rendered from the most recent to the oldest event. + val state = aTimelineState( + timelineItems = persistentListOf( + aTimelineItemEvent(eventId = AN_EVENT_ID_3), + aTimelineItemEvent(eventId = AN_EVENT_ID_2), + aTimelineItemEvent(eventId = AN_EVENT_ID), + ), + // The events have been tapped in a random order. + selectionState = SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID_2, AN_EVENT_ID, AN_EVENT_ID_3)), + ) + assertThat(state.selectedEventIdsInTimelineOrder()).containsExactly(AN_EVENT_ID, AN_EVENT_ID_2, AN_EVENT_ID_3).inOrder() + } + + @Test + fun `selectedEventIdsInTimelineOrder - the events which are not in the timeline are ignored`() { + val state = aTimelineState( + timelineItems = persistentListOf( + aTimelineItemEvent(eventId = AN_EVENT_ID_2), + aTimelineItemEvent(eventId = AN_EVENT_ID), + ), + selectionState = SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID_2, AN_EVENT_ID_3)), + ) + assertThat(state.selectedEventIdsInTimelineOrder()).containsExactly(AN_EVENT_ID_2) + } + + @Test + fun `canSelect - returns false when selection mode is disabled`() { + val event = aTimelineItemEvent(eventId = AN_EVENT_ID) + val state = aTimelineState( + timelineItems = persistentListOf(event), + selectionState = SelectionState.Disabled, + ) + assertThat(state.canSelect(event)).isFalse() + } + + @Test + fun `canSelect - follows the action predicate when selection mode is active`() { + val forwardable = aTimelineItemEvent(eventId = AN_EVENT_ID) + val notForwardable = aTimelineItemEvent(eventId = AN_EVENT_ID_2, content = aTimelineItemRedactedContent()) + val state = aTimelineState( + timelineItems = persistentListOf(forwardable, notForwardable), + selectionState = SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID)), + ) + assertThat(state.canSelect(forwardable)).isTrue() + assertThat(state.canSelect(notForwardable)).isFalse() + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt deleted file mode 100644 index e293246900..0000000000 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2026 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.messages.impl.timeline.model - -import com.google.common.truth.Truth.assertThat -import io.element.android.features.messages.impl.timeline.aTimelineItemEvent -import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent -import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemStateEventContent -import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import org.junit.Test - -class TimelineItemSelectionTest { - @Test - fun `a remote text message can be selected`() { - val item = aTimelineItemEvent() - assertThat(item.canBeSelected()).isTrue() - } - - @Test - fun `a local echo cannot be selected`() { - val item = aTimelineItemEvent().copy(eventId = null) - assertThat(item.canBeSelected()).isFalse() - } - - @Test - fun `a failed to send message cannot be selected`() { - val item = aTimelineItemEvent(sendState = LocalEventSendState.Failed.Unknown("failed")) - assertThat(item.canBeSelected()).isFalse() - } - - @Test - fun `a state event cannot be selected`() { - val item = aTimelineItemEvent(content = aTimelineItemStateEventContent()) - assertThat(item.canBeSelected()).isFalse() - } - - @Test - fun `a redacted event cannot be selected`() { - val item = aTimelineItemEvent(content = aTimelineItemRedactedContent()) - assertThat(item.canBeSelected()).isFalse() - } -} From f4399a392ba962e69b4d4c6a21946b657928ada9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:47:48 +0200 Subject: [PATCH 05/92] Use a Checkbox for the timeline selection indicator --- .../timeline/components/SelectionIndicator.kt | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt index e980b47155..86261057c8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt @@ -7,44 +7,38 @@ package io.element.android.features.messages.impl.timeline.components -import androidx.compose.foundation.layout.size +import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import io.element.android.compound.theme.ElementTheme -import io.element.android.compound.tokens.generated.CompoundIcons 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.Checkbox /** - * Compact selection indicator used in the timeline selection mode, in place of a Material - * [androidx.compose.material3.Checkbox] (which reserves a 48dp interactive area). It is purely - * visual: the tap is handled by the whole-row click target, so this only reflects [selected]. + * Selection indicator used in the timeline selection mode. It renders the Compound [Checkbox], with the minimum + * interactive size enforcement disabled so that it fits the compact selection column. It is purely visual: the tap + * is handled by the whole-row click target, so this only reflects [selected]. */ @Composable internal fun SelectionIndicator( selected: Boolean, modifier: Modifier = Modifier, ) { - Icon( - modifier = modifier, - imageVector = if (selected) CompoundIcons.CheckCircleSolid() else CompoundIcons.Circle(), - contentDescription = null, - tint = if (selected) ElementTheme.colors.iconAccentPrimary else ElementTheme.colors.iconTertiary, - ) + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) { + Checkbox( + modifier = modifier, + checked = selected, + onCheckedChange = null, + ) + } } @PreviewsDayNight @Composable internal fun SelectionIndicatorPreview() = ElementPreview { - Icon( - modifier = Modifier.size(24.dp), - imageVector = CompoundIcons.Circle(), - contentDescription = null, - tint = ElementTheme.colors.iconTertiary, - ) + SelectionIndicator(selected = false) } @PreviewsDayNight From 0a11e88f06609ddc773bb3eaf5399ce2388a696a Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:48:58 +0200 Subject: [PATCH 06/92] Align the selection indicator with the message bubble --- .../timeline/components/MessageEventBubble.kt | 16 +++ .../timeline/components/TimelineItemRow.kt | 130 ++++++++++-------- 2 files changed, 88 insertions(+), 58 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 0dcf489fe3..7f4375e632 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -32,6 +32,8 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.layer.CompositingStrategy +import androidx.compose.ui.layout.HorizontalAlignmentLine +import androidx.compose.ui.layout.layout import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp @@ -53,12 +55,20 @@ import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import io.element.android.libraries.ui.utils.graphics.drawInLayer +import kotlin.math.min private val BUBBLE_RADIUS = 12.dp private val avatarRadius = AvatarSize.TimelineSender.dp / 2 private val MIN_BUBBLE_WIDTH = 80.dp +/** + * Alignment line published by [MessageEventBubble] at its vertical center. + * It lets the timeline row align decorations with the bubble itself, instead of with the whole row, whose height also + * covers the sender information, the reactions, the thread summary and the read receipts. + */ +val BubbleVerticalCenter = HorizontalAlignmentLine(::min) + @Composable fun MessageEventBubble( state: BubbleState, @@ -93,6 +103,12 @@ fun MessageEventBubble( val updatedBorderColor by rememberUpdatedState(borderColor) BoxWithConstraints( modifier = modifier + .layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + layout(placeable.width, placeable.height, mapOf(BubbleVerticalCenter to placeable.height / 2)) { + placeable.place(0, 0) + } + } .drawWithCache { // Calculate the outline of the background and cache it val outline = bubbleShape.createOutline(size, layoutDirection, this) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 7f08aa465e..d547d823a8 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -8,7 +8,6 @@ package io.element.android.features.messages.impl.timeline.components -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box @@ -18,18 +17,20 @@ import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.AlignmentLine +import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.isTraversalGroup import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp @@ -39,7 +40,6 @@ import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayoutData import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.canBeSelected import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent @@ -106,10 +106,7 @@ internal fun TimelineItemRow( val selectionProgress = selectionData.progress val isSelected = selectionData.isSelected val backgroundModifier = when { - isSelected -> { - Modifier.background(ElementTheme.colors.bgAccentSelected) - } - timelineItem.isEvent(focusedEventId) -> { + timelineItem.isEvent(focusedEventId) && !isSelectionModeActive -> { val focusedEventOffset = if ((timelineItem as? TimelineItem.Event)?.showSenderInformation == true) { 14.dp @@ -122,7 +119,7 @@ internal fun TimelineItemRow( Modifier } } - val selectableEvent = (timelineItem as? TimelineItem.Event)?.takeIf { it.canBeSelected() } + val selectableEvent = (timelineItem as? TimelineItem.Event)?.takeIf { selectionData.canBeSelected } // Only start-aligned message bubbles slide to make room for the selection indicator: // - Outgoing (own) messages are end-aligned, opposite the indicator, so they already have room. // - Centered tiles (state events, call tiles, day dividers, grouped events) must stay put. @@ -130,60 +127,77 @@ internal fun TimelineItemRow( val shouldSlideContent = timelineItem.rendersAsBubble() && !isOutgoing val layoutDirection = LocalLayoutDirection.current val contentSlide = if (shouldSlideContent) SELECTION_COLUMN_WIDTH * selectionProgress else 0.dp - Box( - modifier = modifier.then(backgroundModifier) - ) { - TimelineItemRowContent( - modifier = Modifier - .fillMaxWidth() - .offset(x = contentSlide), - selectionContentOffset = contentSlide, - timelineItem = timelineItem, - timelineMode = timelineMode, - timelineRoomInfo = timelineRoomInfo, - timelineProtectionState = timelineProtectionState, - isLastOutgoingMessage = isLastOutgoingMessage, - focusedEventId = focusedEventId, - displayThreadSummaries = displayThreadSummaries, - onUserDataClick = onUserDataClick, - onLinkClick = onLinkClick, - onLinkLongClick = onLinkLongClick, - onContentClick = onContentClick, - onGalleryItemClick = onGalleryItemClick, - onLongClick = onLongClick, - inReplyToClick = inReplyToClick, - onReactionClick = onReactionClick, - onReactionLongClick = onReactionLongClick, - onMoreReactionsClick = onMoreReactionsClick, - onReadReceiptClick = onReadReceiptClick, - onJoinCallClick = onJoinCallClick, - onSwipeToReply = onSwipeToReply, - eventSink = eventSink, - eventContentView = eventContentView, - ) - if (selectableEvent != null && selectionProgress > 0f) { - val slideSign = if (layoutDirection == LayoutDirection.Ltr) -1f else 1f - SelectionIndicator( - selected = isSelected, - modifier = Modifier - .align(Alignment.CenterStart) - .graphicsLayer { translationX = slideSign * SELECTION_COLUMN_WIDTH.toPx() * (1f - selectionProgress) } - .padding(start = 12.dp) - .size(24.dp), - ) - } - if (isSelectionModeActive && selectableEvent != null) { - // In selection mode the whole row toggles selection. - Box( - modifier = Modifier - .matchParentSize() - .clickable { onContentClick(selectableEvent) } - ) + Layout( + modifier = modifier.then(backgroundModifier), + contents = listOf( + { + TimelineItemRowContent( + modifier = Modifier + .fillMaxWidth() + .offset(x = contentSlide), + selectionContentOffset = contentSlide, + timelineItem = timelineItem, + timelineMode = timelineMode, + timelineRoomInfo = timelineRoomInfo, + timelineProtectionState = timelineProtectionState, + isLastOutgoingMessage = isLastOutgoingMessage, + focusedEventId = focusedEventId, + displayThreadSummaries = displayThreadSummaries, + onUserDataClick = onUserDataClick, + onLinkClick = onLinkClick, + onLinkLongClick = onLinkLongClick, + onContentClick = onContentClick, + onGalleryItemClick = onGalleryItemClick, + onLongClick = onLongClick, + inReplyToClick = inReplyToClick, + onReactionClick = onReactionClick, + onReactionLongClick = onReactionLongClick, + onMoreReactionsClick = onMoreReactionsClick, + onReadReceiptClick = onReadReceiptClick, + onJoinCallClick = onJoinCallClick, + onSwipeToReply = onSwipeToReply, + eventSink = eventSink, + eventContentView = eventContentView, + ) + }, + { + if (selectableEvent != null && selectionProgress > 0f) { + val slideSign = if (layoutDirection == LayoutDirection.Ltr) -1f else 1f + SelectionIndicator( + selected = isSelected, + modifier = Modifier + .graphicsLayer { translationX = slideSign * SELECTION_COLUMN_WIDTH.toPx() * (1f - selectionProgress) } + .padding(start = 16.dp) + .size(24.dp), + ) + } + }, + { + if (isSelectionModeActive && selectableEvent != null) { + // In selection mode the whole row toggles selection. + Box(modifier = Modifier.clickable { onContentClick(selectableEvent) }) + } + }, + ), + ) { (contentMeasurables, indicatorMeasurables, overlayMeasurables), constraints -> + val contentPlaceable = contentMeasurables.first().measure(constraints) + val width = contentPlaceable.width + val height = contentPlaceable.height + val indicatorPlaceable = indicatorMeasurables.firstOrNull()?.measure(constraints.copy(minWidth = 0, minHeight = 0)) + val overlayPlaceable = overlayMeasurables.firstOrNull()?.measure(Constraints.fixed(width, height)) + layout(width = width, height = height) { + contentPlaceable.place(0, 0) + if (indicatorPlaceable != null) { + val bubbleCenter = contentPlaceable[BubbleVerticalCenter].takeIf { it != AlignmentLine.Unspecified } ?: (height / 2) + val x = if (layoutDirection == LayoutDirection.Ltr) 0 else width - indicatorPlaceable.width + indicatorPlaceable.place(x = x, y = bubbleCenter - indicatorPlaceable.height / 2) + } + overlayPlaceable?.place(0, 0) } } } -/** Width reserved for the selection indicator; the row content slides by this amount. */ +/** Width reserved for the selection indicator : the row content slides by this amount. */ private val SELECTION_COLUMN_WIDTH = 36.dp /** From 971f196d5429d6a4604a5529106c517db9b22b75 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:48:58 +0200 Subject: [PATCH 07/92] Intercept clicks on every row in selection mode --- .../messages/impl/timeline/components/TimelineItemRow.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index d547d823a8..3db8508617 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.layout.AlignmentLine import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.isTraversalGroup import androidx.compose.ui.semantics.semantics @@ -173,9 +174,12 @@ internal fun TimelineItemRow( } }, { - if (isSelectionModeActive && selectableEvent != null) { + if (isSelectionModeActive) { // In selection mode the whole row toggles selection. - Box(modifier = Modifier.clickable { onContentClick(selectableEvent) }) + Box(modifier = Modifier.clickable(enabled = selectionData.canBeSelected, role = Role.Checkbox) { + if(selectableEvent != null) + onContentClick(selectableEvent) + }) } }, ), From d8b66945754046bcac6f1b2b47b93c35a306f69c Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 25 Sep 2026 18:50:51 +0200 Subject: [PATCH 08/92] Apply ktlint formatting --- .../android/features/messages/impl/timeline/TimelineState.kt | 4 ++-- .../messages/impl/timeline/components/TimelineItemRow.kt | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) 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 bc571f6634..f8976de874 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 @@ -112,7 +112,7 @@ val TimelineState.selectedCount: Int fun TimelineState.isSelected(item: TimelineItem): Boolean { val selection = selectionState as? SelectionState.Active ?: return false - return when(item){ + return when (item) { is TimelineItem.Event -> selection.selectedEventIds.contains(item.eventId) else -> false } @@ -123,7 +123,7 @@ fun TimelineState.isSelected(item: TimelineItem): Boolean { */ fun TimelineState.canSelect(item: TimelineItem): Boolean { val selection = selectionState as? SelectionState.Active ?: return false - return when(item) { + return when (item) { is TimelineItem.Event -> selection.action.canApplyTo(item) else -> false } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 3db8508617..6f26b8c441 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -177,8 +177,9 @@ internal fun TimelineItemRow( if (isSelectionModeActive) { // In selection mode the whole row toggles selection. Box(modifier = Modifier.clickable(enabled = selectionData.canBeSelected, role = Role.Checkbox) { - if(selectableEvent != null) - onContentClick(selectableEvent) + if (selectableEvent != null) { + onContentClick(selectableEvent) + } }) } }, From 2dcaffcdf6a5606c7b29daa8cb242c506972c5d9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 07:29:34 +0000 Subject: [PATCH 09/92] Update kotlin --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5ede6519a9..12074b22f5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,9 +5,9 @@ # Project android_gradle_plugin = "9.3.2" # When updating this, please also update the version in the file ./idea/kotlinc.xml -kotlin = "2.4.10" +kotlin = "2.4.20" kotlinpoet = "2.4.0" -ksp = "2.3.11" +ksp = "2.3.12" firebaseAppDistribution = "5.3.0" # AndroidX From 4761c4a8d1bc1626c9bb7d0289610d8aede3129c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:37:36 +0000 Subject: [PATCH 10/92] Update dependency com.github.matrix-org:matrix-analytics-events to v0.38.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 eb2c7bd59e..8545ca65c5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -224,7 +224,7 @@ color_picker = "io.mhssn:colorpicker:1.0.0" posthog = "com.posthog:posthog-android:3.60.7" sentry = "io.sentry:sentry-android:8.55.0" # main branch can be tested replacing the version with main-SNAPSHOT -matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.37.0" +matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.38.0" # Emojibase matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.5.3" From 69a1870f58a952c6e78ddcbbdf0087348e30a361 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 29 Sep 2026 16:35:33 +0200 Subject: [PATCH 11/92] Use existing strings --- .../messages/impl/timeline/TimelinePresenter.kt | 3 ++- .../messages/impl/topbars/SelectionModeTopBar.kt | 3 ++- .../impl/src/main/res/values/temporary.xml | 15 --------------- 3 files changed, 4 insertions(+), 17 deletions(-) delete mode 100644 features/messages/impl/src/main/res/values/temporary.xml 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 320e362024..a712e47cba 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 @@ -63,6 +63,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.preferences.api.store.SessionPreferencesStore +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.DisplayFirstTimelineItems import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationToMessage import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom @@ -275,7 +276,7 @@ class TimelinePresenter( if (next.isEmpty()) SelectionState.Disabled else current.copy(selectedEventIds = next) } selected.size >= MAX_SELECTION_COUNT -> { - snackbarDispatcher.post(SnackbarMessage(R.string.screen_room_timeline_selection_limit_reached)) + snackbarDispatcher.post(SnackbarMessage(CommonStrings.screen_room_maximum_messages_selected)) current } else -> current.copy(selectedEventIds = (selected + event.eventId).toPersistentSet()) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt index 1e96d7b1e1..e918c0da2b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -23,6 +23,7 @@ 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.theme.components.TopAppBar +import io.element.android.libraries.ui.strings.CommonPlurals import io.element.android.libraries.ui.strings.CommonStrings @OptIn(ExperimentalMaterial3Api::class) @@ -37,7 +38,7 @@ fun SelectionModeTopBar( TopAppBar( modifier = modifier, title = { - Text(text = pluralStringResource(R.plurals.screen_room_timeline_selection_title, selectedCount, selectedCount)) + Text(text = pluralStringResource(CommonPlurals.common_selected_count, selectedCount, selectedCount)) }, navigationIcon = { IconButton( diff --git a/features/messages/impl/src/main/res/values/temporary.xml b/features/messages/impl/src/main/res/values/temporary.xml deleted file mode 100644 index 5dfeaa7b85..0000000000 --- a/features/messages/impl/src/main/res/values/temporary.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - %1$d selected - %1$d selected - - You can select up to 30 messages - From 62f9b7f0b40cc112b3fdc68c26438ea4d6fc7ae8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 29 Sep 2026 20:53:51 +0200 Subject: [PATCH 12/92] Use "Forward to" as forward screen title --- .../android/libraries/roomselect/impl/RoomSelectView.kt | 2 +- libraries/ui-strings/src/main/res/values/localazy.xml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt index 43b91068f9..118acf25cb 100644 --- a/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt +++ b/libraries/roomselect/impl/src/main/kotlin/io/element/android/libraries/roomselect/impl/RoomSelectView.kt @@ -123,7 +123,7 @@ fun RoomSelectView( topBar = { TopAppBar( titleStr = when (state.mode) { - RoomSelectMode.Forward -> stringResource(CommonStrings.common_forward_message) + RoomSelectMode.Forward -> stringResource(CommonStrings.common_forward_to) RoomSelectMode.Share -> stringResource(CommonStrings.common_send_to) }, navigationIcon = { diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 7a0ce8fd4e..0079c8f47e 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -98,6 +98,7 @@ "Choose file" "Choose photo" "Clear" + "Clear all" "Close" "Complete verification" "Confirm" @@ -286,6 +287,7 @@ Reason: %1$s." "File saved" "File saved to Downloads" "Forward message" + "Forward to" "Frequently used" "Gallery" @@ -412,6 +414,7 @@ Reason: %1$s." "Shared live location" "Shared location" "Shared space" + "Signed in" "Removing device" "Something went wrong" "We encountered an issue. Please try again." From bb649838322fc56b06e1b00ca680a24f2fb95c3c Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Sep 2026 11:48:31 +0200 Subject: [PATCH 13/92] Move TimelineItemSelectionData to model package --- .../messages/impl/pinned/list/PinnedMessagesListView.kt | 2 +- .../features/messages/impl/timeline/TimelinePresenter.kt | 1 - .../features/messages/impl/timeline/TimelineView.kt | 2 +- .../timeline/components/TimelineItemGroupedEventsRow.kt | 1 + .../messages/impl/timeline/components/TimelineItemRow.kt | 1 + .../{components => model}/TimelineItemSelectionData.kt | 8 +++++--- .../features/messages/impl/topbars/SelectionModeTopBar.kt | 1 - 7 files changed, 9 insertions(+), 7 deletions(-) rename features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/{components => model}/TimelineItemSelectionData.kt (66%) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt index c47b93b02b..f7bba24f95 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt @@ -33,13 +33,13 @@ import io.element.android.features.messages.impl.link.LinkEvent import io.element.android.features.messages.impl.link.LinkView import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.components.TimelineItemRow -import io.element.android.features.messages.impl.timeline.components.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryView import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.poll.api.pollcontent.PollTitleView 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 3c0c22a2e9..c46e28de1a 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 @@ -25,7 +25,6 @@ import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject import io.element.android.features.location.api.live.ActiveLiveLocationShareManager import io.element.android.features.messages.impl.MessagesNavigator -import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.UserEventPermissions import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureEvent import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureState 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 71c7bd3540..f948f478b5 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 @@ -84,13 +84,13 @@ import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.messages.impl.crypto.sendfailure.resolve.ResolveVerifiedUserSendFailureView import io.element.android.features.messages.impl.timeline.components.FloatingDateBadgeOverlay import io.element.android.features.messages.impl.timeline.components.TimelineItemRow -import io.element.android.features.messages.impl.timeline.components.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.components.toText import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.focus.FocusRequestStateView import io.element.android.features.messages.impl.timeline.model.NewEventState import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentPreviewParam import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt index 1cd10fdf01..b03b76dd79 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemGroupedEventsRow.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.timeline.components.receipt.Rea import io.element.android.features.messages.impl.timeline.components.receipt.TimelineItemReadReceiptView import io.element.android.features.messages.impl.timeline.groups.isRedactedMessagesGroup import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState import io.element.android.libraries.designsystem.preview.ElementPreview diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index e1b3204552..32cef08ea7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -40,6 +40,7 @@ import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.TimelineRoomInfo import io.element.android.features.messages.impl.timeline.components.event.TimelineItemEventContentView import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemSelectionData import io.element.android.features.messages.impl.timeline.model.event.RtcNotificationState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt similarity index 66% rename from features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt rename to features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt index 641be44fe8..60a30d5c73 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemSelectionData.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt @@ -1,14 +1,14 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.messages.impl.timeline.components +package io.element.android.features.messages.impl.timeline.model /** - * Row-scoped selection state passed to [TimelineItemRow]. + * Row-scoped selection state passed to [io.element.android.features.messages.impl.timeline.components.TimelineItemRow]. * * @param isSelectionModeActive whether the timeline is currently in selection mode. Drives * behaviour (e.g. the whole-row click target) and must reflect the real mode, not [progress]. @@ -16,6 +16,8 @@ package io.element.android.features.messages.impl.timeline.components * Animated once in TimelineView and shared by every row. Differs from [isSelectionModeActive] * during the enter/exit animation. * @param isSelected whether this specific row's event is part of the current selection. + * @param canBeSelected whether this specific row's event can be selected for the action selection mode was entered for. + * Rows which cannot be selected render no indicator, and their content does not slide. */ data class TimelineItemSelectionData( val isSelectionModeActive: Boolean = false, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt index e918c0da2b..1d12699277 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.messages.impl.R import io.element.android.features.messages.impl.timeline.SelectionAction import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight From ae409aa8d2da4fbeeeaec22fabe0d7b44e020df6 Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 30 Sep 2026 11:49:26 +0200 Subject: [PATCH 14/92] Add preview for messages view in selection mode --- .../features/messages/impl/MessagesView.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) 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 9097ed682b..b4f084ebd6 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 @@ -77,13 +77,16 @@ import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBan import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerView import io.element.android.features.messages.impl.pinned.banner.PinnedMessagesBannerViewDefaults import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS +import io.element.android.features.messages.impl.timeline.SelectionAction import io.element.android.features.messages.impl.timeline.SelectionState import io.element.android.features.messages.impl.timeline.TimelineEvent import io.element.android.features.messages.impl.timeline.TimelineView import io.element.android.features.messages.impl.timeline.aGroupedEvents import io.element.android.features.messages.impl.timeline.aTimelineItemDaySeparator import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +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.canApplyTo import io.element.android.features.messages.impl.timeline.components.CallMenuItem import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvent import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvent @@ -138,6 +141,7 @@ import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.link.Link import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentSet import timber.log.Timber import kotlin.time.Duration.Companion.milliseconds @@ -756,6 +760,40 @@ internal fun MessagesViewPreview(@PreviewParameter(MessagesStatePreviewParam::cl ) } +@PreviewsDayNight +@Composable +internal fun MessagesViewSelectionModePreview() = ElementPreview { + val timelineItems = aTimelineItemList(aTimelineItemTextContent()) + val selectedEventIds = timelineItems + .filterIsInstance() + .filter { SelectionAction.Forward.canApplyTo(it) } + .take(2) + .mapNotNull { it.eventId } + .toPersistentSet() + MessagesView( + state = aMessagesState( + timelineState = aTimelineState( + timelineItems = timelineItems, + selectionState = SelectionState.Active(SelectionAction.Forward, selectedEventIds), + ), + ), + onBackClick = {}, + onRoomDetailsClick = {}, + onEventContentClick = { _, _ -> false }, + onGalleryEventItemClick = { _, _, _ -> false }, + onUserDataClick = {}, + onLinkClick = { _, _ -> }, + onSendLocationClick = {}, + onCreatePollClick = {}, + onJoinCallClick = {}, + onViewAllPinnedMessagesClick = { }, + forceJumpToBottomVisibility = true, + knockRequestsBannerView = {}, + customReactionBottomSheet = {}, + onThreadsListClick = {}, + ) +} + @Preview @Composable internal fun MessagesViewA11yPreview() = ElementPreview { From fd9bd1b9aff7f5db2f13621af206e66e671dd4e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 14:09:54 +0000 Subject: [PATCH 15/92] Update dependency androidx.javascriptengine:javascriptengine to v1.1.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 beea9ddef2..f5ec7c0448 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -102,7 +102,7 @@ androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constr androidx_camera_lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } androidx_camera_view = { module = "androidx.camera:camera-view", version.ref = "camera" } androidx_camera_camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" } -androidx_javascriptengine = "androidx.javascriptengine:javascriptengine:1.1.0" +androidx_javascriptengine = "androidx.javascriptengine:javascriptengine:1.1.1" androidx_workmanager_runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "work" } androidx_recyclerview = "androidx.recyclerview:recyclerview:1.4.0" From d00d3036768052d4158a60b868e3a8a1ba15f5e8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2026 14:10:03 +0000 Subject: [PATCH 16/92] Update dependency androidx.webkit:webkit to v1.17.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 beea9ddef2..769adc45cf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -122,7 +122,7 @@ androidx_activity_activity = { module = "androidx.activity:activity", version.re androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx_startup = "androidx.startup:startup-runtime:1.2.0" androidx_preference = "androidx.preference:preference:1.2.1" -androidx_webkit = "androidx.webkit:webkit:1.17.0" +androidx_webkit = "androidx.webkit:webkit:1.17.1" androidx_compose_bom = { module = "androidx.compose:compose-bom", version.ref = "compose_bom" } androidx_compose_material3 = { module = "androidx.compose.material3:material3", version = '1.5.0-alpha29' } From cb19232e5ac8ee32b183a122c58ba5aa1b5cda43 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Oct 2026 16:45:18 +0200 Subject: [PATCH 17/92] Fix compilation warnings after Kotlin 2.4.20 and KSP 2.3.12 upgrade - Update .idea/kotlinc.xml to Kotlin 2.4.20. - Remove `-Xannotation-default-target=first-only`: the new annotation default target rule is stable in Kotlin 2.4, and disabling it now produces a CLI_ARG_DISABLES_STABLE_FEATURE warning. - Replace deprecated `KSNode.validate()` with `validate(enableNewFeatures = false)` to keep the same behavior. Co-Authored-By: Claude Opus 5.5 (1M context) --- .idea/kotlinc.xml | 2 +- build.gradle.kts | 4 ---- .../io/element/android/codegen/ContributesNodeProcessor.kt | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index cc87bc5140..4f368d4513 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -2,6 +2,6 @@ \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e7a03b403d..11190f7325 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -101,10 +101,6 @@ allprojects { // Uncomment to suppress Compose Kotlin compiler compatibility warning // freeCompilerArgs.addAll(listOf("-P", "plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=true")) - - // Fix compilation warning for annotations - // See https://youtrack.jetbrains.com/issue/KT-73255/Change-defaulting-rule-for-annotations for more details - freeCompilerArgs.add("-Xannotation-default-target=first-only") } } } diff --git a/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt b/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt index 3e6c05ad01..854f327665 100644 --- a/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt +++ b/codegen/src/main/kotlin/io/element/android/codegen/ContributesNodeProcessor.kt @@ -53,7 +53,7 @@ class ContributesNodeProcessor( val annotatedSymbols = resolver.getSymbolsWithAnnotation(ContributesNode::class.qualifiedName!!) .filterIsInstance() - val (validSymbols, invalidSymbols) = annotatedSymbols.partition { it.validate() } + val (validSymbols, invalidSymbols) = annotatedSymbols.partition { it.validate(enableNewFeatures = false) } if (validSymbols.isEmpty()) return invalidSymbols From 954eb731e774cc8c0d3d5c79da8a8ff5905f76e4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 30 Sep 2026 10:14:41 +0200 Subject: [PATCH 18/92] Remove unused Gradle dependencies Remove dependencies flagged as unused by the Dependency Analysis Gradle Plugin (`./gradlew buildHealth`) and explicitly declared in module build files. Removals were skipped when the dependency provides transitively something the module (or a consumer of an `api` dependency) uses directly, as well as for runtime-only dependencies (element-call-embedded, sentry-android, rustls-platform-verifier). Dependencies added by the convention plugin helpers (composeDependencies, testCommonDependencies) are left untouched. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/build.gradle.kts | 6 ------ appconfig/build.gradle.kts | 1 - appnav/build.gradle.kts | 1 - features/analytics/api/build.gradle.kts | 2 -- features/call/api/build.gradle.kts | 1 - features/call/impl/build.gradle.kts | 1 - features/call/test/build.gradle.kts | 1 - features/callnative/impl/build.gradle.kts | 2 -- features/contentscanner/api/build.gradle.kts | 8 -------- features/contentscanner/impl/build.gradle.kts | 1 - features/createroom/impl/build.gradle.kts | 2 -- features/deactivation/impl/build.gradle.kts | 1 - features/enterprise/api/build.gradle.kts | 1 - features/enterprise/impl-foss/build.gradle.kts | 1 - features/enterprise/test/build.gradle.kts | 1 - features/forward/test/build.gradle.kts | 1 - features/ftue/impl/build.gradle.kts | 2 -- features/home/impl/build.gradle.kts | 6 ------ features/invite/api/build.gradle.kts | 3 --- features/invite/impl/build.gradle.kts | 1 - features/invitepeople/impl/build.gradle.kts | 2 -- features/joinroom/impl/build.gradle.kts | 3 --- features/knockrequests/impl/build.gradle.kts | 1 - features/knockrequests/test/build.gradle.kts | 1 - features/licenses/api/build.gradle.kts | 2 -- features/licenses/impl/build.gradle.kts | 1 - features/linknewdevice/impl/build.gradle.kts | 14 -------------- features/linknewdevice/test/build.gradle.kts | 1 - features/location/api/build.gradle.kts | 3 --- features/location/impl/build.gradle.kts | 5 ----- features/lockscreen/impl/build.gradle.kts | 4 ---- features/login/impl/build.gradle.kts | 4 ---- features/logout/api/build.gradle.kts | 3 --- features/logout/impl/build.gradle.kts | 3 --- features/messages/api/build.gradle.kts | 2 -- features/messages/impl/build.gradle.kts | 7 ------- features/networkmonitor/impl/build.gradle.kts | 3 --- features/poll/impl/build.gradle.kts | 1 - features/preferences/impl/build.gradle.kts | 8 -------- features/rageshake/impl/build.gradle.kts | 3 --- features/rageshake/test/build.gradle.kts | 1 - features/reportroom/impl/build.gradle.kts | 2 -- .../rolesandpermissions/api/build.gradle.kts | 2 -- .../roomaliasresolver/impl/build.gradle.kts | 2 -- features/roomcall/api/build.gradle.kts | 1 - features/roomcall/impl/build.gradle.kts | 1 - features/roomdetails/impl/build.gradle.kts | 13 ------------- features/roomdetailsedit/impl/build.gradle.kts | 7 ------- features/roomdetailsedit/test/build.gradle.kts | 1 - features/roomdirectory/impl/build.gradle.kts | 2 -- .../roommembermoderation/api/build.gradle.kts | 2 -- features/securebackup/impl/build.gradle.kts | 2 -- .../securityandprivacy/api/build.gradle.kts | 1 - .../securityandprivacy/impl/build.gradle.kts | 3 --- .../securityandprivacy/test/build.gradle.kts | 1 - features/share/impl/build.gradle.kts | 7 ------- features/share/test/build.gradle.kts | 2 -- features/signedout/impl/build.gradle.kts | 1 - features/space/impl/build.gradle.kts | 3 --- features/startchat/impl/build.gradle.kts | 8 -------- features/startchat/test/build.gradle.kts | 2 -- features/userprofile/impl/build.gradle.kts | 3 --- features/userprofile/shared/build.gradle.kts | 6 ------ features/verifysession/impl/build.gradle.kts | 5 ----- libraries/accountselect/impl/build.gradle.kts | 2 -- libraries/cachestore/impl/build.gradle.kts | 2 -- libraries/cryptography/impl/build.gradle.kts | 1 - libraries/dateformatter/impl/build.gradle.kts | 3 --- libraries/dateformatter/test/build.gradle.kts | 1 - libraries/emoji/api/build.gradle.kts | 1 - libraries/emoji/impl/build.gradle.kts | 1 - libraries/featureflag/api/build.gradle.kts | 1 - libraries/featureflag/impl/build.gradle.kts | 3 --- libraries/fullscreenintent/api/build.gradle.kts | 2 -- .../fullscreenintent/impl/build.gradle.kts | 4 ---- libraries/htmlrenderer/impl/build.gradle.kts | 1 - libraries/indicator/api/build.gradle.kts | 4 ---- libraries/indicator/impl/build.gradle.kts | 2 -- libraries/indicator/test/build.gradle.kts | 1 - libraries/matrix/api/build.gradle.kts | 1 - libraries/matrix/impl/build.gradle.kts | 3 --- libraries/matrixmedia/impl/build.gradle.kts | 1 - libraries/matrixui/build.gradle.kts | 2 -- libraries/mediapickers/api/build.gradle.kts | 2 -- libraries/mediapickers/impl/build.gradle.kts | 1 - libraries/mediapickers/test/build.gradle.kts | 1 - libraries/mediaplayer/impl/build.gradle.kts | 1 - libraries/mediaplayer/test/build.gradle.kts | 1 - libraries/mediaupload/api/build.gradle.kts | 4 ---- libraries/mediaviewer/impl/build.gradle.kts | 4 ---- libraries/network/build.gradle.kts | 1 - libraries/oauth/impl/build.gradle.kts | 7 ------- libraries/permissions/impl/build.gradle.kts | 4 ---- libraries/push/impl/build.gradle.kts | 2 -- libraries/push/test/build.gradle.kts | 1 - libraries/pushproviders/api/build.gradle.kts | 1 - .../pushproviders/firebase/build.gradle.kts | 3 --- .../pushproviders/unifiedpush/build.gradle.kts | 3 --- libraries/session-storage/impl/build.gradle.kts | 1 - libraries/session-storage/test/build.gradle.kts | 1 - libraries/troubleshoot/api/build.gradle.kts | 1 - libraries/usersearch/impl/build.gradle.kts | 1 - libraries/usersearch/test/build.gradle.kts | 1 - libraries/voiceplayer/api/build.gradle.kts | 2 -- libraries/voiceplayer/impl/build.gradle.kts | 2 -- libraries/wellknown/api/build.gradle.kts | 4 ---- libraries/wellknown/impl/build.gradle.kts | 17 ----------------- libraries/wellknown/test/build.gradle.kts | 5 ----- libraries/workmanager/impl/build.gradle.kts | 1 - services/analytics/api/build.gradle.kts | 1 - services/analytics/test/build.gradle.kts | 1 - .../analyticsproviders/posthog/build.gradle.kts | 1 - services/apperror/impl/build.gradle.kts | 3 --- services/appnavstate/api/build.gradle.kts | 1 - services/appnavstate/test/build.gradle.kts | 1 - 115 files changed, 308 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6b58f8520b..706a82f0d6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -280,7 +280,6 @@ dependencies { implementation(projects.features.migration.api) implementation(projects.appnav) implementation(projects.appconfig) - implementation(projects.libraries.uiStrings) implementation(projects.services.analytics.compose) if (ModulesConfig.pushProvidersConfig.includeFirebase) { @@ -298,18 +297,13 @@ dependencies { implementation(libs.androidx.splash) implementation(libs.androidx.core) implementation(libs.androidx.corektx) - implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.process) implementation(libs.androidx.activity.compose) implementation(libs.androidx.startup) implementation(libs.androidx.preference) - implementation(libs.coil) implementation(platform(libs.network.okhttp.bom)) implementation(libs.network.okhttp.logging) - implementation(libs.serialization.json) - - implementation(libs.matrix.emojibase.bindings) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) diff --git a/appconfig/build.gradle.kts b/appconfig/build.gradle.kts index 64b9b76a14..e355499350 100644 --- a/appconfig/build.gradle.kts +++ b/appconfig/build.gradle.kts @@ -48,7 +48,6 @@ android { } dependencies { - implementation(libs.coroutines.core) implementation(libs.androidx.annotationjvm) implementation(libs.androidx.corektx) implementation(projects.libraries.matrix.api) diff --git a/appnav/build.gradle.kts b/appnav/build.gradle.kts index f65692feb4..cc11f5c033 100644 --- a/appnav/build.gradle.kts +++ b/appnav/build.gradle.kts @@ -44,7 +44,6 @@ dependencies { implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.wellknown.api) implementation(projects.features.login.api) implementation(libs.coil) diff --git a/features/analytics/api/build.gradle.kts b/features/analytics/api/build.gradle.kts index 5c7f0b223a..f861a375b3 100644 --- a/features/analytics/api/build.gradle.kts +++ b/features/analytics/api/build.gradle.kts @@ -16,6 +16,4 @@ android { dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) - implementation(projects.libraries.androidutils) - implementation(projects.libraries.uiStrings) } diff --git a/features/call/api/build.gradle.kts b/features/call/api/build.gradle.kts index 1480a31fd3..572e3fdee4 100644 --- a/features/call/api/build.gradle.kts +++ b/features/call/api/build.gradle.kts @@ -17,6 +17,5 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) } diff --git a/features/call/impl/build.gradle.kts b/features/call/impl/build.gradle.kts index e872678b63..e80622fa74 100644 --- a/features/call/impl/build.gradle.kts +++ b/features/call/impl/build.gradle.kts @@ -90,7 +90,6 @@ dependencies { api(projects.features.call.api) testCommonDependencies(libs, true) - testImplementation(projects.features.call.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.matrix.test) diff --git a/features/call/test/build.gradle.kts b/features/call/test/build.gradle.kts index f06c5ed16e..920670287e 100644 --- a/features/call/test/build.gradle.kts +++ b/features/call/test/build.gradle.kts @@ -17,7 +17,6 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.core) api(projects.features.call.api) implementation(projects.libraries.matrix.api) diff --git a/features/callnative/impl/build.gradle.kts b/features/callnative/impl/build.gradle.kts index a135f46ad3..a7fdd18252 100644 --- a/features/callnative/impl/build.gradle.kts +++ b/features/callnative/impl/build.gradle.kts @@ -47,8 +47,6 @@ dependencies { testCommonDependencies(libs) testImplementation(libs.element.call.test) testImplementation(projects.features.call.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.preferences.test) testImplementation(projects.services.appnavstate.test) } diff --git a/features/contentscanner/api/build.gradle.kts b/features/contentscanner/api/build.gradle.kts index 0e3d592878..b90b0c277f 100644 --- a/features/contentscanner/api/build.gradle.kts +++ b/features/contentscanner/api/build.gradle.kts @@ -8,16 +8,8 @@ android { } dependencies { - implementation(projects.libraries.core) - implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) - implementation(projects.libraries.designsystem) - testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) - testImplementation(libs.molecule.runtime) - testImplementation(libs.test.truth) - testImplementation(libs.test.turbine) - testImplementation(projects.libraries.matrix.test) } diff --git a/features/contentscanner/impl/build.gradle.kts b/features/contentscanner/impl/build.gradle.kts index 34558a182e..1f17a0d991 100644 --- a/features/contentscanner/impl/build.gradle.kts +++ b/features/contentscanner/impl/build.gradle.kts @@ -24,7 +24,6 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) - implementation(projects.libraries.designsystem) testImplementation(libs.test.junit) testImplementation(libs.coroutines.test) diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index 2cd3961ead..ce0584828b 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -34,7 +34,6 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) - implementation(projects.libraries.deeplink.api) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.permissions.api) @@ -52,7 +51,6 @@ dependencies { testImplementation(projects.libraries.mediapickers.test) testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.permissions.test) - testImplementation(projects.features.startchat.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.enterprise.test) } diff --git a/features/deactivation/impl/build.gradle.kts b/features/deactivation/impl/build.gradle.kts index bca144071b..c0a38cabab 100644 --- a/features/deactivation/impl/build.gradle.kts +++ b/features/deactivation/impl/build.gradle.kts @@ -28,7 +28,6 @@ setupDependencyInjection() dependencies { implementation(projects.libraries.androidutils) - implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) diff --git a/features/enterprise/api/build.gradle.kts b/features/enterprise/api/build.gradle.kts index 5e7f81e4b4..1c541c5080 100644 --- a/features/enterprise/api/build.gradle.kts +++ b/features/enterprise/api/build.gradle.kts @@ -17,5 +17,4 @@ dependencies { implementation(projects.libraries.compound) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.wellknown.api) } diff --git a/features/enterprise/impl-foss/build.gradle.kts b/features/enterprise/impl-foss/build.gradle.kts index 2585d00d23..a600920586 100644 --- a/features/enterprise/impl-foss/build.gradle.kts +++ b/features/enterprise/impl-foss/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.androidutils) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.wellknown.api) implementation(libs.serialization.json) testCommonDependencies(libs) diff --git a/features/enterprise/test/build.gradle.kts b/features/enterprise/test/build.gradle.kts index 99dbe733dd..c37fc53de3 100644 --- a/features/enterprise/test/build.gradle.kts +++ b/features/enterprise/test/build.gradle.kts @@ -18,6 +18,5 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.compound) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.wellknown.api) implementation(projects.tests.testutils) } diff --git a/features/forward/test/build.gradle.kts b/features/forward/test/build.gradle.kts index bf9db05e34..a3d8428be2 100644 --- a/features/forward/test/build.gradle.kts +++ b/features/forward/test/build.gradle.kts @@ -15,7 +15,6 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(projects.features.forward.api) implementation(projects.tests.testutils) } diff --git a/features/ftue/impl/build.gradle.kts b/features/ftue/impl/build.gradle.kts index 49b3baa71b..f6d1970a5a 100644 --- a/features/ftue/impl/build.gradle.kts +++ b/features/ftue/impl/build.gradle.kts @@ -32,12 +32,10 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.preferences.api) implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.testtags) implementation(projects.features.analytics.api) implementation(projects.features.logout.api) implementation(projects.features.securebackup.api) diff --git a/features/home/impl/build.gradle.kts b/features/home/impl/build.gradle.kts index 5a9bb700f2..7fcc2dde32 100644 --- a/features/home/impl/build.gradle.kts +++ b/features/home/impl/build.gradle.kts @@ -45,14 +45,11 @@ dependencies { implementation(projects.libraries.indicator.api) implementation(projects.libraries.deeplink.api) implementation(projects.libraries.fullscreenintent.api) - implementation(projects.libraries.permissions.api) - implementation(projects.libraries.permissions.noop) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.features.announcement.api) implementation(projects.features.invite.api) - implementation(projects.features.networkmonitor.api) implementation(projects.features.leaveroom.api) implementation(projects.features.rageshake.api) implementation(projects.services.analytics.api) @@ -72,11 +69,8 @@ dependencies { testImplementation(projects.libraries.dateformatter.test) testImplementation(projects.libraries.eventformatter.test) testImplementation(projects.libraries.indicator.test) - testImplementation(projects.libraries.permissions.noop) - testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.push.test) testImplementation(projects.services.analytics.test) - testImplementation(projects.services.toolbox.test) } diff --git a/features/invite/api/build.gradle.kts b/features/invite/api/build.gradle.kts index 084ad1088d..390fc6f3eb 100644 --- a/features/invite/api/build.gradle.kts +++ b/features/invite/api/build.gradle.kts @@ -18,7 +18,4 @@ android { dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) - implementation(projects.libraries.designsystem) - implementation(projects.services.analytics.api) } diff --git a/features/invite/impl/build.gradle.kts b/features/invite/impl/build.gradle.kts index e033f2740c..0ea9923f5a 100644 --- a/features/invite/impl/build.gradle.kts +++ b/features/invite/impl/build.gradle.kts @@ -32,7 +32,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/invitepeople/impl/build.gradle.kts b/features/invitepeople/impl/build.gradle.kts index 2ab2fcb4a3..7d1dd2ac59 100644 --- a/features/invitepeople/impl/build.gradle.kts +++ b/features/invitepeople/impl/build.gradle.kts @@ -39,12 +39,10 @@ dependencies { implementation(projects.libraries.testtags) implementation(libs.coil.compose) implementation(projects.services.apperror.api) - implementation(projects.libraries.featureflag.api) api(projects.features.invitepeople.api) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.services.apperror.test) - testImplementation(projects.libraries.featureflag.test) } diff --git a/features/joinroom/impl/build.gradle.kts b/features/joinroom/impl/build.gradle.kts index 71acdad128..c18a4b8a81 100644 --- a/features/joinroom/impl/build.gradle.kts +++ b/features/joinroom/impl/build.gradle.kts @@ -37,12 +37,9 @@ dependencies { implementation(projects.features.invite.api) implementation(projects.features.roomdirectory.api) implementation(projects.services.analytics.api) - implementation(projects.libraries.preferences.api) - implementation(projects.appconfig) testCommonDependencies(libs, true) testImplementation(projects.features.invite.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.previewutils) } diff --git a/features/knockrequests/impl/build.gradle.kts b/features/knockrequests/impl/build.gradle.kts index e6a1a30167..2e60838b41 100644 --- a/features/knockrequests/impl/build.gradle.kts +++ b/features/knockrequests/impl/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) implementation(projects.libraries.designsystem) diff --git a/features/knockrequests/test/build.gradle.kts b/features/knockrequests/test/build.gradle.kts index dc3407da01..726ea6969f 100644 --- a/features/knockrequests/test/build.gradle.kts +++ b/features/knockrequests/test/build.gradle.kts @@ -17,6 +17,5 @@ android { dependencies { implementation(projects.features.knockrequests.api) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) } diff --git a/features/licenses/api/build.gradle.kts b/features/licenses/api/build.gradle.kts index fcbf81f210..86070f65f9 100644 --- a/features/licenses/api/build.gradle.kts +++ b/features/licenses/api/build.gradle.kts @@ -15,6 +15,4 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) } diff --git a/features/licenses/impl/build.gradle.kts b/features/licenses/impl/build.gradle.kts index 52f21ac0bd..85023488cd 100644 --- a/features/licenses/impl/build.gradle.kts +++ b/features/licenses/impl/build.gradle.kts @@ -31,5 +31,4 @@ dependencies { testCommonDependencies(libs) testImplementation(libs.coroutines.core) - testImplementation(projects.libraries.matrix.test) } diff --git a/features/linknewdevice/impl/build.gradle.kts b/features/linknewdevice/impl/build.gradle.kts index 4659a2d2b8..7c86e1d057 100644 --- a/features/linknewdevice/impl/build.gradle.kts +++ b/features/linknewdevice/impl/build.gradle.kts @@ -28,38 +28,24 @@ setupDependencyInjection() dependencies { // TODO Cleanup - implementation(projects.appconfig) implementation(projects.features.enterprise.api) implementation(projects.features.lockscreen.api) - implementation(projects.features.rageshake.api) implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) - implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) implementation(projects.libraries.permissions.api) - implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.qrcode) - implementation(projects.libraries.oauth.api) - implementation(projects.libraries.uiUtils) - implementation(projects.libraries.wellknown.api) implementation(libs.androidx.browser) implementation(libs.androidx.webkit) - implementation(libs.serialization.json) api(projects.features.linknewdevice.api) testCommonDependencies(libs, true) - testImplementation(projects.features.linknewdevice.test) testImplementation(projects.features.lockscreen.test) testImplementation(projects.features.enterprise.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) - testImplementation(projects.libraries.sessionStorage.test) - testImplementation(projects.libraries.wellknown.test) } diff --git a/features/linknewdevice/test/build.gradle.kts b/features/linknewdevice/test/build.gradle.kts index 388612f920..39a99e7d67 100644 --- a/features/linknewdevice/test/build.gradle.kts +++ b/features/linknewdevice/test/build.gradle.kts @@ -14,6 +14,5 @@ android { } dependencies { - implementation(projects.features.linknewdevice.api) implementation(projects.tests.testutils) } diff --git a/features/location/api/build.gradle.kts b/features/location/api/build.gradle.kts index 8830b39a22..36dbfdb139 100644 --- a/features/location/api/build.gradle.kts +++ b/features/location/api/build.gradle.kts @@ -68,12 +68,9 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.wellknown.api) implementation(projects.features.enterprise.api) implementation(libs.coil.compose) - implementation(libs.datetime) testCommonDependencies(libs) } diff --git a/features/location/impl/build.gradle.kts b/features/location/impl/build.gradle.kts index 1fcb2e1fc4..9733f3c768 100644 --- a/features/location/impl/build.gradle.kts +++ b/features/location/impl/build.gradle.kts @@ -42,14 +42,11 @@ dependencies { implementation(projects.services.appnavstate.api) implementation(libs.accompanist.permission) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) implementation(projects.libraries.sessionStorage.api) - implementation(projects.libraries.wellknown.api) implementation(libs.androidx.datastore.preferences) - implementation(libs.datetime) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) @@ -58,10 +55,8 @@ dependencies { testImplementation(projects.libraries.testtags) testImplementation(projects.services.analytics.test) testImplementation(projects.features.messages.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.sessionStorage.test) - testImplementation(projects.libraries.wellknown.test) testImplementation(projects.features.location.test) testImplementation(projects.features.enterprise.test) } diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 414b4b5909..523f7919f2 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -31,13 +31,10 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.cryptography.api) implementation(projects.libraries.preferences.api) - implementation(projects.libraries.testtags) implementation(projects.libraries.uiUtils) implementation(projects.features.logout.api) implementation(projects.libraries.uiCommon) @@ -52,7 +49,6 @@ dependencies { testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.cryptography.test) testImplementation(projects.libraries.cryptography.impl) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.services.appnavstate.test) testImplementation(projects.features.logout.test) diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 6cf8945e05..8cbfa9575d 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -71,14 +71,11 @@ dependencies { implementation(projects.libraries.oauth.api) implementation(projects.libraries.preferences.api) implementation(projects.libraries.uiUtils) - implementation(projects.libraries.wellknown.api) implementation(libs.androidx.browser) implementation(libs.androidx.webkit) - implementation(libs.serialization.json) api(projects.features.login.api) testCommonDependencies(libs, true) - testImplementation(projects.features.login.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.preferences.test) testImplementation(projects.libraries.preferences.test) @@ -86,7 +83,6 @@ dependencies { testImplementation(projects.libraries.oauth.test) testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.sessionStorage.test) - testImplementation(projects.libraries.wellknown.test) testImplementation(libs.androidx.camera.camera2) testImplementation(libs.androidx.camera.lifecycle) } diff --git a/features/logout/api/build.gradle.kts b/features/logout/api/build.gradle.kts index 4453d37931..9a0d858dba 100644 --- a/features/logout/api/build.gradle.kts +++ b/features/logout/api/build.gradle.kts @@ -14,8 +14,5 @@ android { } dependencies { - implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) } diff --git a/features/logout/impl/build.gradle.kts b/features/logout/impl/build.gradle.kts index bc6646a68f..612037a618 100644 --- a/features/logout/impl/build.gradle.kts +++ b/features/logout/impl/build.gradle.kts @@ -29,20 +29,17 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) - implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.workmanager.api) api(projects.features.logout.api) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.workmanager.test) } diff --git a/features/messages/api/build.gradle.kts b/features/messages/api/build.gradle.kts index 029ef624e0..20e05a1798 100644 --- a/features/messages/api/build.gradle.kts +++ b/features/messages/api/build.gradle.kts @@ -19,7 +19,5 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.designsystem) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.mediaviewer.api) - implementation(projects.libraries.preferences.api) api(projects.libraries.textcomposer.impl) } diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 882d9f1f97..cd7ce3703a 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -53,7 +53,6 @@ dependencies { implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) implementation(projects.libraries.emoji.api) - implementation(projects.libraries.roomselect.api) implementation(projects.libraries.slashcommands.api) implementation(projects.libraries.audio.api) implementation(projects.libraries.voiceplayer.api) @@ -63,22 +62,17 @@ dependencies { implementation(projects.libraries.uiCommon) implementation(projects.libraries.uiUtils) implementation(projects.libraries.testtags) - implementation(projects.libraries.wellknown.api) - implementation(projects.features.networkmonitor.api) implementation(projects.features.contentscanner.api) implementation(projects.services.analytics.compose) implementation(projects.services.appnavstate.api) implementation(projects.services.toolbox.api) implementation(libs.coil.compose) - implementation(libs.datetime) implementation(libs.jsoup) - implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout.compose) implementation(libs.androidx.exifinterface) implementation(libs.androidx.media3.exoplayer) implementation(libs.androidx.media3.ui) implementation(libs.sigpwned.emoji4j) - implementation(libs.vanniktech.blurhash) implementation(libs.telephoto.zoomableimage) implementation(libs.matrix.emojibase.bindings) implementation(projects.features.knockrequests.api) @@ -106,7 +100,6 @@ dependencies { testImplementation(projects.libraries.voicerecorder.test) testImplementation(projects.libraries.mediaplayer.test) testImplementation(projects.libraries.mediaviewer.test) - testImplementation(projects.libraries.testtags) testImplementation(projects.features.poll.test) testImplementation(projects.libraries.eventformatter.test) testImplementation(projects.libraries.emoji.impl) diff --git a/features/networkmonitor/impl/build.gradle.kts b/features/networkmonitor/impl/build.gradle.kts index 5adc383d64..e08e7e0f78 100644 --- a/features/networkmonitor/impl/build.gradle.kts +++ b/features/networkmonitor/impl/build.gradle.kts @@ -21,11 +21,8 @@ android { dependencies { implementation(libs.coroutines.core) - implementation(projects.libraries.core) implementation(projects.libraries.di) api(projects.features.networkmonitor.api) testCommonDependencies(libs) - testImplementation(projects.libraries.matrix.test) - testImplementation(projects.features.networkmonitor.test) } diff --git a/features/poll/impl/build.gradle.kts b/features/poll/impl/build.gradle.kts index 175f7b4471..0d5790639e 100644 --- a/features/poll/impl/build.gradle.kts +++ b/features/poll/impl/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.services.analytics.api) implementation(projects.features.messages.api) diff --git a/features/preferences/impl/build.gradle.kts b/features/preferences/impl/build.gradle.kts index 11f863e26d..c137d6be06 100644 --- a/features/preferences/impl/build.gradle.kts +++ b/features/preferences/impl/build.gradle.kts @@ -48,15 +48,12 @@ setupDependencyInjection() dependencies { implementation(projects.libraries.androidutils) - implementation(projects.appconfig) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.cachestore.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.featureflag.ui) - implementation(projects.libraries.network) implementation(projects.libraries.pushstore.api) implementation(projects.libraries.indicator.api) implementation(projects.libraries.preferences.api) @@ -80,14 +77,12 @@ dependencies { implementation(projects.features.licenses.api) implementation(projects.features.logout.api) implementation(projects.features.deactivation.api) - implementation(projects.features.home.api) implementation(projects.features.invite.api) implementation(projects.libraries.emoji.api) implementation(projects.services.analytics.api) implementation(projects.services.analytics.compose) implementation(projects.services.appnavstate.api) implementation(projects.services.toolbox.api) - implementation(libs.datetime) implementation(libs.coil.compose) implementation(libs.color.picker) implementation(libs.androidx.browser) @@ -108,7 +103,6 @@ dependencies { testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) - testImplementation(projects.libraries.roomselect.test) testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.troubleshoot.test) testImplementation(projects.features.deactivation.test) @@ -116,11 +110,9 @@ dependencies { testImplementation(projects.features.invite.test) testImplementation(projects.features.licenses.test) testImplementation(projects.features.lockscreen.test) - testImplementation(projects.features.rageshake.test) testImplementation(projects.features.logout.test) testImplementation(projects.libraries.indicator.test) testImplementation(projects.libraries.pushproviders.test) - testImplementation(projects.libraries.cachestore.test) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.services.appnavstate.impl) testImplementation(projects.services.analytics.test) diff --git a/features/rageshake/impl/build.gradle.kts b/features/rageshake/impl/build.gradle.kts index 0200f21e61..578f86a39f 100644 --- a/features/rageshake/impl/build.gradle.kts +++ b/features/rageshake/impl/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { implementation(projects.appconfig) implementation(projects.features.enterprise.api) implementation(projects.features.viewfolder.api) - implementation(projects.services.toolbox.api) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.network) @@ -52,9 +51,7 @@ dependencies { testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.sessionStorage.test) - testImplementation(projects.features.rageshake.test) testImplementation(projects.features.viewfolder.test) testImplementation(projects.libraries.preferences.test) - testImplementation(projects.services.toolbox.test) testImplementation(libs.network.mockwebserver) } diff --git a/features/rageshake/test/build.gradle.kts b/features/rageshake/test/build.gradle.kts index faad812d73..516562c556 100644 --- a/features/rageshake/test/build.gradle.kts +++ b/features/rageshake/test/build.gradle.kts @@ -16,6 +16,5 @@ android { dependencies { implementation(projects.libraries.architecture) implementation(projects.features.rageshake.api) - implementation(libs.coroutines.core) implementation(projects.tests.testutils) } diff --git a/features/reportroom/impl/build.gradle.kts b/features/reportroom/impl/build.gradle.kts index c945465aec..5a25ba5051 100644 --- a/features/reportroom/impl/build.gradle.kts +++ b/features/reportroom/impl/build.gradle.kts @@ -27,10 +27,8 @@ setupDependencyInjection() dependencies { api(projects.features.reportroom.api) - implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/rolesandpermissions/api/build.gradle.kts b/features/rolesandpermissions/api/build.gradle.kts index ca29972606..6ea7080314 100644 --- a/features/rolesandpermissions/api/build.gradle.kts +++ b/features/rolesandpermissions/api/build.gradle.kts @@ -17,7 +17,5 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) - api(projects.libraries.usersearch.api) } diff --git a/features/roomaliasresolver/impl/build.gradle.kts b/features/roomaliasresolver/impl/build.gradle.kts index 1860c77017..0c5e1c2b07 100644 --- a/features/roomaliasresolver/impl/build.gradle.kts +++ b/features/roomaliasresolver/impl/build.gradle.kts @@ -27,11 +27,9 @@ setupDependencyInjection() dependencies { api(projects.features.roomaliasresolver.api) - implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.androidutils) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) diff --git a/features/roomcall/api/build.gradle.kts b/features/roomcall/api/build.gradle.kts index 54bb7bbd02..1220f87a25 100644 --- a/features/roomcall/api/build.gradle.kts +++ b/features/roomcall/api/build.gradle.kts @@ -16,6 +16,5 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(libs.androidx.compose.ui.tooling.preview) } diff --git a/features/roomcall/impl/build.gradle.kts b/features/roomcall/impl/build.gradle.kts index 069c890874..20f4eab571 100644 --- a/features/roomcall/impl/build.gradle.kts +++ b/features/roomcall/impl/build.gradle.kts @@ -26,7 +26,6 @@ dependencies { implementation(projects.features.enterprise.api) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index 77e2d0c229..b8aa939015 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -34,20 +34,13 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) - implementation(projects.libraries.mediapickers.api) - implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.mediaviewer.api) - implementation(projects.libraries.featureflag.api) - implementation(projects.libraries.permissions.api) implementation(projects.libraries.preferences.api) implementation(projects.libraries.push.api) implementation(projects.libraries.testtags) api(projects.features.roomdetails.api) - api(projects.libraries.usersearch.api) - api(projects.services.apperror.api) implementation(libs.coil.compose) implementation(projects.features.call.api) - implementation(projects.features.startchat.api) implementation(projects.features.leaveroom.api) implementation(projects.features.userprofile.shared) implementation(projects.services.analytics.compose) @@ -65,14 +58,9 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.mediaupload.test) - testImplementation(projects.libraries.mediapickers.test) testImplementation(projects.libraries.mediaviewer.test) - testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.push.test) - testImplementation(projects.libraries.usersearch.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.call.test) testImplementation(projects.features.rolesandpermissions.test) testImplementation(projects.features.securityandprivacy.test) @@ -81,7 +69,6 @@ dependencies { testImplementation(projects.features.messages.test) testImplementation(projects.features.poll.test) testImplementation(projects.features.reportroom.test) - testImplementation(projects.features.startchat.test) testImplementation(projects.features.verifysession.test) testImplementation(projects.services.analytics.test) } diff --git a/features/roomdetailsedit/impl/build.gradle.kts b/features/roomdetailsedit/impl/build.gradle.kts index 6b1d886abe..8d5303c7a9 100644 --- a/features/roomdetailsedit/impl/build.gradle.kts +++ b/features/roomdetailsedit/impl/build.gradle.kts @@ -35,23 +35,16 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) - implementation(projects.libraries.mediaviewer.api) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.permissions.api) - implementation(projects.libraries.preferences.api) implementation(projects.services.analytics.api) implementation(projects.libraries.testtags) api(projects.features.roomdetailsedit.api) - api(projects.services.apperror.api) implementation(libs.coil.compose) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.mediaupload.test) testImplementation(projects.libraries.mediapickers.test) - testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.libraries.permissions.test) - testImplementation(projects.libraries.preferences.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.services.analytics.test) } diff --git a/features/roomdetailsedit/test/build.gradle.kts b/features/roomdetailsedit/test/build.gradle.kts index ff9110eb49..4762d37d08 100644 --- a/features/roomdetailsedit/test/build.gradle.kts +++ b/features/roomdetailsedit/test/build.gradle.kts @@ -17,6 +17,5 @@ android { dependencies { implementation(projects.features.roomdetailsedit.api) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) } diff --git a/features/roomdirectory/impl/build.gradle.kts b/features/roomdirectory/impl/build.gradle.kts index 1c63872fc8..fa9492ecf7 100644 --- a/features/roomdirectory/impl/build.gradle.kts +++ b/features/roomdirectory/impl/build.gradle.kts @@ -30,12 +30,10 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.androidutils) implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) - implementation(projects.services.analytics.api) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) diff --git a/features/roommembermoderation/api/build.gradle.kts b/features/roommembermoderation/api/build.gradle.kts index e168d2c8bf..437f10f2d8 100644 --- a/features/roommembermoderation/api/build.gradle.kts +++ b/features/roommembermoderation/api/build.gradle.kts @@ -16,7 +16,5 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) implementation(projects.libraries.matrix.api) } diff --git a/features/securebackup/impl/build.gradle.kts b/features/securebackup/impl/build.gradle.kts index 54d87ef22e..284517ce4a 100644 --- a/features/securebackup/impl/build.gradle.kts +++ b/features/securebackup/impl/build.gradle.kts @@ -33,9 +33,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) - implementation(projects.libraries.oauth.api) implementation(projects.libraries.uiStrings) implementation(projects.libraries.testtags) api(libs.statemachine) diff --git a/features/securityandprivacy/api/build.gradle.kts b/features/securityandprivacy/api/build.gradle.kts index 4260bea661..ec29da2f8b 100644 --- a/features/securityandprivacy/api/build.gradle.kts +++ b/features/securityandprivacy/api/build.gradle.kts @@ -16,6 +16,5 @@ android { } dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) } diff --git a/features/securityandprivacy/impl/build.gradle.kts b/features/securityandprivacy/impl/build.gradle.kts index d42bdef883..0a97231db6 100644 --- a/features/securityandprivacy/impl/build.gradle.kts +++ b/features/securityandprivacy/impl/build.gradle.kts @@ -29,7 +29,6 @@ setupDependencyInjection() dependencies { api(projects.features.securityandprivacy.api) implementation(projects.appconfig) - implementation(projects.appnav) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.core) @@ -39,12 +38,10 @@ dependencies { implementation(projects.libraries.previewutils) implementation(projects.libraries.matrixui) implementation(projects.libraries.uiStrings) - implementation(projects.services.analytics.api) implementation(projects.libraries.featureflag.api) implementation(projects.features.enterprise.api) testCommonDependencies(libs, true) - testImplementation(projects.services.analytics.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.testtags) diff --git a/features/securityandprivacy/test/build.gradle.kts b/features/securityandprivacy/test/build.gradle.kts index 903ef6d194..1d6f0ee3a3 100644 --- a/features/securityandprivacy/test/build.gradle.kts +++ b/features/securityandprivacy/test/build.gradle.kts @@ -17,6 +17,5 @@ android { dependencies { implementation(projects.features.securityandprivacy.api) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) } diff --git a/features/share/impl/build.gradle.kts b/features/share/impl/build.gradle.kts index 255263cf02..f10783d60c 100644 --- a/features/share/impl/build.gradle.kts +++ b/features/share/impl/build.gradle.kts @@ -27,27 +27,20 @@ android { setupDependencyInjection() dependencies { - implementation(projects.appconfig) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.mediaupload.api) implementation(projects.libraries.roomselect.api) - implementation(projects.libraries.uiStrings) - implementation(projects.libraries.testtags) implementation(projects.services.appnavstate.api) api(libs.statemachine) api(projects.features.share.api) testCommonDependencies(libs, true) - testImplementation(projects.features.share.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.mediaupload.test) - testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.roomselect.test) testImplementation(projects.services.appnavstate.impl) } diff --git a/features/share/test/build.gradle.kts b/features/share/test/build.gradle.kts index e4a987c113..6b8f8f7572 100644 --- a/features/share/test/build.gradle.kts +++ b/features/share/test/build.gradle.kts @@ -15,7 +15,5 @@ android { dependencies { implementation(projects.features.share.api) - implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(projects.tests.testutils) } diff --git a/features/signedout/impl/build.gradle.kts b/features/signedout/impl/build.gradle.kts index ed5dec3eb9..ad7643d1e2 100644 --- a/features/signedout/impl/build.gradle.kts +++ b/features/signedout/impl/build.gradle.kts @@ -31,7 +31,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiStrings) diff --git a/features/space/impl/build.gradle.kts b/features/space/impl/build.gradle.kts index deec862fa3..57bb0f7122 100644 --- a/features/space/impl/build.gradle.kts +++ b/features/space/impl/build.gradle.kts @@ -34,10 +34,8 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) - implementation(projects.libraries.deeplink.api) implementation(projects.services.analytics.api) implementation(libs.coil.compose) - implementation(projects.libraries.featureflag.api) implementation(projects.features.createroom.api) implementation(projects.features.invite.api) implementation(projects.libraries.previewutils) @@ -49,7 +47,6 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.services.analytics.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.createroom.test) testImplementation(projects.features.invite.test) testImplementation(projects.features.rolesandpermissions.test) diff --git a/features/startchat/impl/build.gradle.kts b/features/startchat/impl/build.gradle.kts index fe7bfa9ae2..1b04c175e1 100644 --- a/features/startchat/impl/build.gradle.kts +++ b/features/startchat/impl/build.gradle.kts @@ -35,13 +35,9 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) implementation(projects.libraries.deeplink.api) - implementation(projects.libraries.mediapickers.api) - implementation(projects.libraries.mediaupload.api) - implementation(projects.libraries.permissions.api) implementation(projects.libraries.usersearch.api) implementation(projects.services.analytics.api) implementation(libs.coil.compose) - implementation(projects.libraries.featureflag.api) implementation(projects.features.createroom.api) implementation(projects.features.enterprise.api) api(projects.features.startchat.api) @@ -49,12 +45,8 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.services.analytics.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.mediapickers.test) - testImplementation(projects.libraries.mediaupload.test) - testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.features.createroom.test) testImplementation(projects.features.startchat.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.enterprise.test) } diff --git a/features/startchat/test/build.gradle.kts b/features/startchat/test/build.gradle.kts index d6d1471832..40617bd8ad 100644 --- a/features/startchat/test/build.gradle.kts +++ b/features/startchat/test/build.gradle.kts @@ -15,9 +15,7 @@ android { } dependencies { - implementation(libs.coroutines.core) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrix.test) implementation(projects.libraries.architecture) implementation(projects.tests.testutils) api(projects.features.startchat.api) diff --git a/features/userprofile/impl/build.gradle.kts b/features/userprofile/impl/build.gradle.kts index 3e68fb2b9c..b04ca61a23 100644 --- a/features/userprofile/impl/build.gradle.kts +++ b/features/userprofile/impl/build.gradle.kts @@ -31,10 +31,8 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) implementation(projects.libraries.mediaviewer.api) - implementation(projects.libraries.featureflag.api) implementation(projects.features.call.api) implementation(projects.features.enterprise.api) implementation(projects.features.verifysession.api) @@ -47,7 +45,6 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.mediaviewer.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.features.call.test) testImplementation(projects.features.verifysession.test) testImplementation(projects.features.startchat.test) diff --git a/features/userprofile/shared/build.gradle.kts b/features/userprofile/shared/build.gradle.kts index 7f4d96c883..1fdce484dd 100644 --- a/features/userprofile/shared/build.gradle.kts +++ b/features/userprofile/shared/build.gradle.kts @@ -30,16 +30,10 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) - implementation(projects.libraries.mediaviewer.api) - implementation(projects.libraries.featureflag.api) - implementation(projects.libraries.permissions.api) - implementation(projects.libraries.preferences.api) implementation(projects.libraries.testtags) api(projects.features.userprofile.api) - api(projects.services.apperror.api) implementation(libs.coil.compose) implementation(projects.features.startchat.api) - implementation(projects.services.analytics.api) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) diff --git a/features/verifysession/impl/build.gradle.kts b/features/verifysession/impl/build.gradle.kts index fc51d87acd..24ba7ccb3e 100644 --- a/features/verifysession/impl/build.gradle.kts +++ b/features/verifysession/impl/build.gradle.kts @@ -25,7 +25,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.appconfig) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) @@ -33,17 +32,13 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) - implementation(projects.libraries.preferences.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiStrings) - implementation(projects.features.logout.api) api(libs.statemachine) api(projects.features.verifysession.api) testCommonDependencies(libs, true) - testImplementation(projects.features.logout.test) testImplementation(projects.libraries.dateformatter.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.sessionStorage.test) } diff --git a/libraries/accountselect/impl/build.gradle.kts b/libraries/accountselect/impl/build.gradle.kts index 2497299f5b..e28b13bc44 100644 --- a/libraries/accountselect/impl/build.gradle.kts +++ b/libraries/accountselect/impl/build.gradle.kts @@ -20,8 +20,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.libraries.core) - implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) diff --git a/libraries/cachestore/impl/build.gradle.kts b/libraries/cachestore/impl/build.gradle.kts index f0c7ba237c..f357cf8446 100644 --- a/libraries/cachestore/impl/build.gradle.kts +++ b/libraries/cachestore/impl/build.gradle.kts @@ -19,8 +19,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.libraries.androidutils) - implementation(projects.libraries.core) implementation(projects.libraries.encryptedDb) api(projects.libraries.cachestore.api) implementation(libs.sqldelight.driver.android) diff --git a/libraries/cryptography/impl/build.gradle.kts b/libraries/cryptography/impl/build.gradle.kts index 3a1f55126e..b347ce1cbc 100644 --- a/libraries/cryptography/impl/build.gradle.kts +++ b/libraries/cryptography/impl/build.gradle.kts @@ -20,7 +20,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.libraries.di) implementation(libs.coroutines.core) api(projects.libraries.cryptography.api) diff --git a/libraries/dateformatter/impl/build.gradle.kts b/libraries/dateformatter/impl/build.gradle.kts index c9ddf459d6..84f8a0aea6 100644 --- a/libraries/dateformatter/impl/build.gradle.kts +++ b/libraries/dateformatter/impl/build.gradle.kts @@ -37,13 +37,10 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.designsystem) implementation(projects.libraries.di) - implementation(projects.libraries.uiStrings) implementation(projects.services.toolbox.api) api(projects.libraries.dateformatter.api) api(libs.datetime) testCommonDependencies(libs, true) - testImplementation(projects.libraries.dateformatter.test) - testImplementation(projects.services.toolbox.test) } diff --git a/libraries/dateformatter/test/build.gradle.kts b/libraries/dateformatter/test/build.gradle.kts index 0367030ae1..fa0861c1a2 100644 --- a/libraries/dateformatter/test/build.gradle.kts +++ b/libraries/dateformatter/test/build.gradle.kts @@ -16,5 +16,4 @@ android { dependencies { api(projects.libraries.dateformatter.api) - api(libs.datetime) } diff --git a/libraries/emoji/api/build.gradle.kts b/libraries/emoji/api/build.gradle.kts index 48143d9512..d9e56fb68f 100644 --- a/libraries/emoji/api/build.gradle.kts +++ b/libraries/emoji/api/build.gradle.kts @@ -15,7 +15,6 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) implementation(libs.kotlinx.collections.immutable) api(libs.matrix.emojibase.bindings) diff --git a/libraries/emoji/impl/build.gradle.kts b/libraries/emoji/impl/build.gradle.kts index 59a1ad6575..844f8ce403 100644 --- a/libraries/emoji/impl/build.gradle.kts +++ b/libraries/emoji/impl/build.gradle.kts @@ -30,7 +30,6 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.uiStrings) implementation(libs.kotlinx.collections.immutable) implementation(libs.matrix.emojibase.bindings) diff --git a/libraries/featureflag/api/build.gradle.kts b/libraries/featureflag/api/build.gradle.kts index b34e082cd3..f9177a23bc 100644 --- a/libraries/featureflag/api/build.gradle.kts +++ b/libraries/featureflag/api/build.gradle.kts @@ -15,7 +15,6 @@ android { } dependencies { - implementation(projects.appconfig) implementation(projects.libraries.core) implementation(libs.coroutines.core) } diff --git a/libraries/featureflag/impl/build.gradle.kts b/libraries/featureflag/impl/build.gradle.kts index 63d968467a..6d2db6f365 100644 --- a/libraries/featureflag/impl/build.gradle.kts +++ b/libraries/featureflag/impl/build.gradle.kts @@ -23,9 +23,6 @@ setupDependencyInjection() dependencies { api(projects.libraries.featureflag.api) implementation(libs.androidx.datastore.preferences) - implementation(projects.appconfig) - implementation(projects.libraries.di) - implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.preferences.api) implementation(libs.coroutines.core) diff --git a/libraries/fullscreenintent/api/build.gradle.kts b/libraries/fullscreenintent/api/build.gradle.kts index 8d6be454ea..e8ad77c3a9 100644 --- a/libraries/fullscreenintent/api/build.gradle.kts +++ b/libraries/fullscreenintent/api/build.gradle.kts @@ -16,6 +16,4 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.permissions.api) - implementation(projects.libraries.preferences.api) } diff --git a/libraries/fullscreenintent/impl/build.gradle.kts b/libraries/fullscreenintent/impl/build.gradle.kts index 57c4f5b836..326411a635 100644 --- a/libraries/fullscreenintent/impl/build.gradle.kts +++ b/libraries/fullscreenintent/impl/build.gradle.kts @@ -23,16 +23,12 @@ dependencies { api(projects.libraries.fullscreenintent.api) implementation(projects.libraries.core) implementation(projects.libraries.architecture) - implementation(projects.libraries.permissions.api) - implementation(projects.libraries.permissions.noop) implementation(projects.libraries.preferences.api) implementation(projects.services.toolbox.api) implementation(libs.androidx.datastore.preferences) testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.permissions.test) testImplementation(projects.libraries.preferences.test) - testImplementation(projects.libraries.testtags) testImplementation(projects.services.toolbox.test) } diff --git a/libraries/htmlrenderer/impl/build.gradle.kts b/libraries/htmlrenderer/impl/build.gradle.kts index ba17d855f1..3aee77ded6 100644 --- a/libraries/htmlrenderer/impl/build.gradle.kts +++ b/libraries/htmlrenderer/impl/build.gradle.kts @@ -27,7 +27,6 @@ setupDependencyInjection() dependencies { api(projects.libraries.htmlrenderer.api) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.uiCommon) implementation(libs.jsoup) testCommonDependencies(libs) diff --git a/libraries/indicator/api/build.gradle.kts b/libraries/indicator/api/build.gradle.kts index c6f33ec83f..a53cfb7707 100644 --- a/libraries/indicator/api/build.gradle.kts +++ b/libraries/indicator/api/build.gradle.kts @@ -13,7 +13,3 @@ plugins { android { namespace = "io.element.android.libraries.indicator.api" } - -dependencies { - implementation(libs.coroutines.core) -} diff --git a/libraries/indicator/impl/build.gradle.kts b/libraries/indicator/impl/build.gradle.kts index 8f1e9ea56d..4b723b6e75 100644 --- a/libraries/indicator/impl/build.gradle.kts +++ b/libraries/indicator/impl/build.gradle.kts @@ -21,7 +21,6 @@ android { dependencies { implementation(projects.libraries.di) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(libs.coroutines.core) @@ -29,6 +28,5 @@ dependencies { api(projects.libraries.indicator.api) testCommonDependencies(libs) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) } diff --git a/libraries/indicator/test/build.gradle.kts b/libraries/indicator/test/build.gradle.kts index a5ce0c57c2..e97c6f5513 100644 --- a/libraries/indicator/test/build.gradle.kts +++ b/libraries/indicator/test/build.gradle.kts @@ -15,6 +15,5 @@ android { } dependencies { - implementation(projects.libraries.matrix.api) api(projects.libraries.indicator.api) } diff --git a/libraries/matrix/api/build.gradle.kts b/libraries/matrix/api/build.gradle.kts index d0790a6905..65919e69f2 100644 --- a/libraries/matrix/api/build.gradle.kts +++ b/libraries/matrix/api/build.gradle.kts @@ -53,7 +53,6 @@ dependencies { implementation(libs.serialization.json) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.sessionStorage.api) implementation(projects.services.analytics.api) testCommonDependencies(libs) diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 6ef7891e10..3901deb9c9 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -68,7 +68,6 @@ dependencies { implementation(projects.libraries.di) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.network) - implementation(projects.libraries.preferences.api) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.workmanager.api) implementation(projects.services.analytics.api) @@ -77,14 +76,12 @@ dependencies { implementation(projects.libraries.core) implementation(variantOf(libs.jna) { artifactType("aar") }) implementation(libs.androidx.datastore.preferences) - implementation(libs.serialization.json) implementation(libs.kotlinx.collections.immutable) testCommonDependencies(libs) testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.previewutils) testImplementation(projects.libraries.sessionStorage.test) testImplementation(projects.libraries.workmanager.test) diff --git a/libraries/matrixmedia/impl/build.gradle.kts b/libraries/matrixmedia/impl/build.gradle.kts index 62799b5eaa..20c7203c41 100644 --- a/libraries/matrixmedia/impl/build.gradle.kts +++ b/libraries/matrixmedia/impl/build.gradle.kts @@ -31,6 +31,5 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.dateformatter.test) testImplementation(projects.libraries.sessionStorage.test) } diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index 5513dc240e..408751a8b1 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -41,6 +41,4 @@ dependencies { testCommonDependencies(libs, true) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.dateformatter.test) - testImplementation(projects.libraries.sessionStorage.test) } diff --git a/libraries/mediapickers/api/build.gradle.kts b/libraries/mediapickers/api/build.gradle.kts index 5e3d78dbc4..9bbf322536 100644 --- a/libraries/mediapickers/api/build.gradle.kts +++ b/libraries/mediapickers/api/build.gradle.kts @@ -17,9 +17,7 @@ android { } dependencies { - implementation(projects.libraries.uiStrings) implementation(projects.libraries.core) - implementation(projects.libraries.di) testCommonDependencies(libs) } diff --git a/libraries/mediapickers/impl/build.gradle.kts b/libraries/mediapickers/impl/build.gradle.kts index 6ed9cd97dc..8363e2b2b3 100644 --- a/libraries/mediapickers/impl/build.gradle.kts +++ b/libraries/mediapickers/impl/build.gradle.kts @@ -19,7 +19,6 @@ android { } dependencies { - implementation(projects.libraries.core) implementation(projects.libraries.di) api(projects.libraries.mediapickers.api) } diff --git a/libraries/mediapickers/test/build.gradle.kts b/libraries/mediapickers/test/build.gradle.kts index d7c2989871..46729db7de 100644 --- a/libraries/mediapickers/test/build.gradle.kts +++ b/libraries/mediapickers/test/build.gradle.kts @@ -16,6 +16,5 @@ android { dependencies { implementation(projects.libraries.core) - implementation(projects.libraries.di) api(projects.libraries.mediapickers.api) } diff --git a/libraries/mediaplayer/impl/build.gradle.kts b/libraries/mediaplayer/impl/build.gradle.kts index aa9140df35..0eddeb92ec 100644 --- a/libraries/mediaplayer/impl/build.gradle.kts +++ b/libraries/mediaplayer/impl/build.gradle.kts @@ -23,7 +23,6 @@ dependencies { implementation(libs.androidx.media3.exoplayer) implementation(projects.libraries.audio.api) - implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(libs.coroutines.core) diff --git a/libraries/mediaplayer/test/build.gradle.kts b/libraries/mediaplayer/test/build.gradle.kts index e2f1c8ac29..bef4437b24 100644 --- a/libraries/mediaplayer/test/build.gradle.kts +++ b/libraries/mediaplayer/test/build.gradle.kts @@ -19,5 +19,4 @@ dependencies { implementation(projects.tests.testutils) implementation(libs.coroutines.test) - implementation(libs.test.truth) } diff --git a/libraries/mediaupload/api/build.gradle.kts b/libraries/mediaupload/api/build.gradle.kts index a9f876c67f..fd3e2cfd8e 100644 --- a/libraries/mediaupload/api/build.gradle.kts +++ b/libraries/mediaupload/api/build.gradle.kts @@ -15,11 +15,7 @@ android { } dependencies { - implementation(projects.libraries.architecture) implementation(projects.libraries.androidutils) - implementation(projects.libraries.core) - implementation(projects.libraries.di) api(projects.libraries.matrix.api) api(projects.libraries.preferences.api) - implementation(libs.coroutines.core) } diff --git a/libraries/mediaviewer/impl/build.gradle.kts b/libraries/mediaviewer/impl/build.gradle.kts index a5d7369056..891eb4e862 100644 --- a/libraries/mediaviewer/impl/build.gradle.kts +++ b/libraries/mediaviewer/impl/build.gradle.kts @@ -34,7 +34,6 @@ dependencies { implementation(libs.androidx.media3.exoplayer.midi) implementation(libs.androidx.media3.ui) implementation(libs.telephoto.zoomableimage) - implementation(libs.vanniktech.blurhash) implementation(libs.telephoto.flick) implementation(projects.features.enterprise.api) @@ -47,7 +46,6 @@ dependencies { implementation(projects.libraries.dateformatter.api) implementation(projects.libraries.di) implementation(projects.libraries.designsystem) - implementation(projects.libraries.featureflag.api) implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrixui) implementation(projects.libraries.matrixmedia.api) @@ -66,9 +64,7 @@ dependencies { testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.audio.test) testImplementation(projects.libraries.dateformatter.test) - testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.matrixui) testImplementation(projects.libraries.mediaviewer.test) testImplementation(projects.services.toolbox.test) testImplementation(libs.coroutines.core) diff --git a/libraries/network/build.gradle.kts b/libraries/network/build.gradle.kts index faabe91a1c..1546358b7b 100644 --- a/libraries/network/build.gradle.kts +++ b/libraries/network/build.gradle.kts @@ -21,7 +21,6 @@ dependencies { implementation(libs.coroutines.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.core) - implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) implementation(projects.libraries.preferences.api) implementation(platform(libs.network.okhttp.bom)) diff --git a/libraries/oauth/impl/build.gradle.kts b/libraries/oauth/impl/build.gradle.kts index d051c06497..5a3b605277 100644 --- a/libraries/oauth/impl/build.gradle.kts +++ b/libraries/oauth/impl/build.gradle.kts @@ -28,20 +28,13 @@ android { setupDependencyInjection() dependencies { - implementation(projects.appconfig) - implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) implementation(libs.androidx.browser) implementation(platform(libs.network.retrofit.bom)) - implementation(libs.network.retrofit) - implementation(libs.serialization.json) api(projects.libraries.oauth.api) testCommonDependencies(libs) testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.permissions.test) } diff --git a/libraries/permissions/impl/build.gradle.kts b/libraries/permissions/impl/build.gradle.kts index 70e27db7f0..fa033dc064 100644 --- a/libraries/permissions/impl/build.gradle.kts +++ b/libraries/permissions/impl/build.gradle.kts @@ -32,11 +32,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.troubleshoot.api) - implementation(projects.libraries.designsystem) - implementation(projects.libraries.uiStrings) implementation(projects.libraries.preferences.api) implementation(projects.services.toolbox.api) api(projects.libraries.permissions.api) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 8615485bba..cd83392383 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -86,7 +86,6 @@ dependencies { implementation(projects.libraries.uiStrings) implementation(projects.libraries.troubleshoot.api) implementation(projects.libraries.workmanager.api) - implementation(projects.features.announcement.api) implementation(projects.features.call.api) implementation(projects.features.enterprise.api) implementation(projects.features.lockscreen.api) @@ -110,7 +109,6 @@ dependencies { testImplementation(projects.libraries.pushstore.test) testImplementation(projects.libraries.troubleshoot.test) testImplementation(projects.libraries.workmanager.test) - testImplementation(projects.features.announcement.test) testImplementation(projects.features.call.test) testImplementation(projects.features.enterprise.test) testImplementation(projects.features.lockscreen.test) diff --git a/libraries/push/test/build.gradle.kts b/libraries/push/test/build.gradle.kts index d125a2387e..f97a23a907 100644 --- a/libraries/push/test/build.gradle.kts +++ b/libraries/push/test/build.gradle.kts @@ -20,7 +20,6 @@ dependencies { implementation(projects.libraries.designsystem) implementation(projects.libraries.matrix.api) implementation(projects.libraries.preferences.api) - implementation(projects.libraries.matrixui) implementation(projects.libraries.workmanager.api) implementation(projects.tests.testutils) implementation(libs.androidx.core) diff --git a/libraries/pushproviders/api/build.gradle.kts b/libraries/pushproviders/api/build.gradle.kts index 3581a43952..d489ec5bfe 100644 --- a/libraries/pushproviders/api/build.gradle.kts +++ b/libraries/pushproviders/api/build.gradle.kts @@ -14,6 +14,5 @@ android { } dependencies { - implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) } diff --git a/libraries/pushproviders/firebase/build.gradle.kts b/libraries/pushproviders/firebase/build.gradle.kts index cfe5dd01fb..35fef58fa0 100644 --- a/libraries/pushproviders/firebase/build.gradle.kts +++ b/libraries/pushproviders/firebase/build.gradle.kts @@ -51,7 +51,6 @@ dependencies { implementation(projects.libraries.matrix.api) implementation(projects.libraries.push.api) implementation(projects.libraries.sessionStorage.api) - implementation(projects.libraries.uiStrings) implementation(projects.libraries.troubleshoot.api) implementation(projects.services.toolbox.api) @@ -66,8 +65,6 @@ dependencies { } testCommonDependencies(libs) - testImplementation(libs.kotlinx.collections.immutable) - testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushstore.test) diff --git a/libraries/pushproviders/unifiedpush/build.gradle.kts b/libraries/pushproviders/unifiedpush/build.gradle.kts index 1b24bc4b1f..7aa19b093b 100644 --- a/libraries/pushproviders/unifiedpush/build.gradle.kts +++ b/libraries/pushproviders/unifiedpush/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.push.api) - implementation(projects.libraries.uiStrings) api(projects.libraries.troubleshoot.api) implementation(projects.libraries.pushstore.api) @@ -46,8 +45,6 @@ dependencies { api(libs.unifiedpush) testCommonDependencies(libs) - testImplementation(libs.kotlinx.collections.immutable) - testImplementation(projects.features.enterprise.test) testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.push.test) testImplementation(projects.libraries.pushproviders.test) diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts index 8b7d9d6e15..9d00237239 100644 --- a/libraries/session-storage/impl/build.gradle.kts +++ b/libraries/session-storage/impl/build.gradle.kts @@ -20,7 +20,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.encryptedDb) api(projects.libraries.sessionStorage.api) diff --git a/libraries/session-storage/test/build.gradle.kts b/libraries/session-storage/test/build.gradle.kts index 7a89746812..35e40e1fed 100644 --- a/libraries/session-storage/test/build.gradle.kts +++ b/libraries/session-storage/test/build.gradle.kts @@ -15,6 +15,5 @@ android { dependencies { implementation(libs.coroutines.core) - implementation(projects.libraries.matrix.api) implementation(projects.libraries.sessionStorage.api) } diff --git a/libraries/troubleshoot/api/build.gradle.kts b/libraries/troubleshoot/api/build.gradle.kts index 177b2a5fce..5eda87d84f 100644 --- a/libraries/troubleshoot/api/build.gradle.kts +++ b/libraries/troubleshoot/api/build.gradle.kts @@ -16,6 +16,5 @@ android { dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) - implementation(libs.androidx.corektx) implementation(libs.coroutines.core) } diff --git a/libraries/usersearch/impl/build.gradle.kts b/libraries/usersearch/impl/build.gradle.kts index bd8eb47cd1..897f63da12 100644 --- a/libraries/usersearch/impl/build.gradle.kts +++ b/libraries/usersearch/impl/build.gradle.kts @@ -20,7 +20,6 @@ android { setupDependencyInjection() dependencies { - implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.di) implementation(projects.libraries.matrixui) diff --git a/libraries/usersearch/test/build.gradle.kts b/libraries/usersearch/test/build.gradle.kts index 21cc140a7d..89736acecf 100644 --- a/libraries/usersearch/test/build.gradle.kts +++ b/libraries/usersearch/test/build.gradle.kts @@ -16,7 +16,6 @@ android { dependencies { implementation(projects.libraries.architecture) - implementation(projects.libraries.matrixui) implementation(projects.libraries.matrix.api) api(projects.libraries.usersearch.api) } diff --git a/libraries/voiceplayer/api/build.gradle.kts b/libraries/voiceplayer/api/build.gradle.kts index e058210b7d..604fd3f5a2 100644 --- a/libraries/voiceplayer/api/build.gradle.kts +++ b/libraries/voiceplayer/api/build.gradle.kts @@ -14,8 +14,6 @@ android { } dependencies { - implementation(libs.androidx.annotationjvm) - implementation(libs.coroutines.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) } diff --git a/libraries/voiceplayer/impl/build.gradle.kts b/libraries/voiceplayer/impl/build.gradle.kts index 8fe79fb774..1e0040d98e 100644 --- a/libraries/voiceplayer/impl/build.gradle.kts +++ b/libraries/voiceplayer/impl/build.gradle.kts @@ -22,7 +22,6 @@ dependencies { api(projects.libraries.voiceplayer.api) implementation(projects.libraries.architecture) - implementation(projects.libraries.audio.api) implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(projects.libraries.matrix.api) @@ -31,7 +30,6 @@ dependencies { implementation(projects.libraries.uiUtils) implementation(projects.services.analytics.api) - implementation(libs.androidx.annotationjvm) implementation(libs.androidx.datastore.preferences) implementation(libs.coroutines.core) diff --git a/libraries/wellknown/api/build.gradle.kts b/libraries/wellknown/api/build.gradle.kts index 54da0d1445..768214d874 100644 --- a/libraries/wellknown/api/build.gradle.kts +++ b/libraries/wellknown/api/build.gradle.kts @@ -12,7 +12,3 @@ plugins { android { namespace = "io.element.android.libraries.wellknown.api" } - -dependencies { - implementation(projects.libraries.matrix.api) -} diff --git a/libraries/wellknown/impl/build.gradle.kts b/libraries/wellknown/impl/build.gradle.kts index 6e7cc5ef1c..d92846f643 100644 --- a/libraries/wellknown/impl/build.gradle.kts +++ b/libraries/wellknown/impl/build.gradle.kts @@ -22,26 +22,9 @@ android { setupDependencyInjection() dependencies { - api(projects.libraries.wellknown.api) - implementation(libs.androidx.annotationjvm) - implementation(libs.coroutines.core) implementation(platform(libs.network.retrofit.bom)) - implementation(libs.network.retrofit) - implementation(libs.serialization.json) - implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) - implementation(projects.libraries.matrix.api) - implementation(projects.libraries.network) - implementation(projects.libraries.cachestore.api) - implementation(projects.features.enterprise.api) - implementation(projects.services.toolbox.api) testCommonDependencies(libs) - testImplementation(libs.coroutines.core) - testImplementation(projects.libraries.cachestore.test) - testImplementation(projects.libraries.matrix.test) - testImplementation(projects.libraries.wellknown.test) - testImplementation(projects.features.enterprise.test) - testImplementation(projects.services.toolbox.test) } diff --git a/libraries/wellknown/test/build.gradle.kts b/libraries/wellknown/test/build.gradle.kts index 7f12f380c7..ea03e424a6 100644 --- a/libraries/wellknown/test/build.gradle.kts +++ b/libraries/wellknown/test/build.gradle.kts @@ -15,10 +15,5 @@ android { } dependencies { - implementation(libs.serialization.json) - implementation(projects.libraries.androidutils) - implementation(projects.libraries.matrix.api) - implementation(projects.libraries.wellknown.api) - implementation(projects.libraries.wellknown.impl) implementation(projects.tests.testutils) } diff --git a/libraries/workmanager/impl/build.gradle.kts b/libraries/workmanager/impl/build.gradle.kts index c1874bfa74..7f2b926c72 100644 --- a/libraries/workmanager/impl/build.gradle.kts +++ b/libraries/workmanager/impl/build.gradle.kts @@ -20,7 +20,6 @@ setupDependencyInjection() dependencies { api(projects.libraries.workmanager.api) - implementation(projects.libraries.core) implementation(projects.libraries.matrix.api) implementation(projects.libraries.di) implementation(projects.libraries.sessionStorage.api) diff --git a/services/analytics/api/build.gradle.kts b/services/analytics/api/build.gradle.kts index 1e1f0710f1..cc20f29990 100644 --- a/services/analytics/api/build.gradle.kts +++ b/services/analytics/api/build.gradle.kts @@ -17,5 +17,4 @@ dependencies { api(projects.services.analyticsproviders.api) api(projects.services.toolbox.api) implementation(libs.coroutines.core) - implementation(projects.libraries.core) } diff --git a/services/analytics/test/build.gradle.kts b/services/analytics/test/build.gradle.kts index 61ed796d48..fe056a5578 100644 --- a/services/analytics/test/build.gradle.kts +++ b/services/analytics/test/build.gradle.kts @@ -15,7 +15,6 @@ android { dependencies { api(projects.services.analytics.api) - implementation(projects.libraries.core) implementation(projects.tests.testutils) implementation(libs.coroutines.core) } diff --git a/services/analyticsproviders/posthog/build.gradle.kts b/services/analyticsproviders/posthog/build.gradle.kts index 966522e2f2..3d3cc6d9b1 100644 --- a/services/analyticsproviders/posthog/build.gradle.kts +++ b/services/analyticsproviders/posthog/build.gradle.kts @@ -39,7 +39,6 @@ dependencies { implementation(libs.posthog) { exclude("com.android.support", "support-annotations") } - implementation(projects.features.enterprise.api) implementation(projects.libraries.core) implementation(projects.libraries.di) implementation(projects.services.analyticsproviders.api) diff --git a/services/apperror/impl/build.gradle.kts b/services/apperror/impl/build.gradle.kts index 75440dc5a3..7ece1fdf07 100644 --- a/services/apperror/impl/build.gradle.kts +++ b/services/apperror/impl/build.gradle.kts @@ -20,12 +20,9 @@ android { } dependencies { - implementation(projects.libraries.core) - implementation(projects.libraries.di) implementation(projects.services.toolbox.api) implementation(libs.coroutines.core) - implementation(libs.androidx.corektx) api(projects.services.apperror.api) diff --git a/services/appnavstate/api/build.gradle.kts b/services/appnavstate/api/build.gradle.kts index c9849de20f..c25dd012a7 100644 --- a/services/appnavstate/api/build.gradle.kts +++ b/services/appnavstate/api/build.gradle.kts @@ -16,7 +16,6 @@ android { dependencies { implementation(libs.coroutines.core) - implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.process) implementation(libs.androidx.startup) implementation(projects.libraries.matrix.api) diff --git a/services/appnavstate/test/build.gradle.kts b/services/appnavstate/test/build.gradle.kts index af4995eb22..2002190d4b 100644 --- a/services/appnavstate/test/build.gradle.kts +++ b/services/appnavstate/test/build.gradle.kts @@ -18,5 +18,4 @@ dependencies { api(projects.libraries.matrix.api) api(projects.services.appnavstate.api) implementation(libs.coroutines.core) - implementation(libs.androidx.lifecycle.runtime) } From 598ea0a6d525c7873a84baaa3afae68c20f92223 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 30 Sep 2026 10:20:52 +0200 Subject: [PATCH 19/92] Remove unused androidx_constraintlayout from the version catalog It is not used anymore since the dependency has been removed from features/messages/impl. Co-Authored-By: Claude Opus 5.5 (1M context) --- gradle/libs.versions.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 511fc38119..4fe014a448 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,6 @@ firebaseAppDistribution = "5.3.0" # AndroidX core = "1.18.0" datastore = "1.2.1" -constraintlayout = "2.2.2" constraintlayout_compose = "1.1.2" lifecycle = "2.11.0" activity = "1.13.0" @@ -97,7 +96,6 @@ androidx_annotationjvm = "androidx.annotation:annotation-jvm:1.11.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.4.2" -androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx_constraintlayout_compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayout_compose" } androidx_camera_lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } androidx_camera_view = { module = "androidx.camera:camera-view", version.ref = "camera" } From b9575f1740a7e2427c2e30ebd15a1d51ef9a8be8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 30 Sep 2026 11:29:12 +0200 Subject: [PATCH 20/92] Restore matrix.api dependency needed by lockscreen impl PinUnlockActivity calls EnterpriseService.semanticColorsFlow(SessionId?), so the Kotlin compiler needs matrix.api on the classpath even if no symbol from it is imported directly. enterprise.api only exposes it as an implementation dependency. Co-Authored-By: Claude Opus 5.5 (1M context) --- features/lockscreen/impl/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/features/lockscreen/impl/build.gradle.kts b/features/lockscreen/impl/build.gradle.kts index 523f7919f2..d287cbea78 100644 --- a/features/lockscreen/impl/build.gradle.kts +++ b/features/lockscreen/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.androidutils) implementation(projects.libraries.architecture) + implementation(projects.libraries.matrix.api) implementation(projects.libraries.designsystem) implementation(projects.libraries.featureflag.api) implementation(projects.libraries.cryptography.api) From 094f6de40e5147274db2394c0004874b2383060e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Oct 2026 15:45:19 +0200 Subject: [PATCH 21/92] Fix aaptDump.txt permission order Removing unused dependencies changed the library manifest merge order, so RECEIVE_BOOT_COMPLETED is now listed after DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION. The set of permissions is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- tools/manifest/gplay/release/aaptDump.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/manifest/gplay/release/aaptDump.txt b/tools/manifest/gplay/release/aaptDump.txt index ae39f3c459..6b9b9842c5 100644 --- a/tools/manifest/gplay/release/aaptDump.txt +++ b/tools/manifest/gplay/release/aaptDump.txt @@ -22,8 +22,8 @@ uses-permission: name='android.permission.FOREGROUND_SERVICE_CAMERA' uses-permission: name='android.permission.USE_BIOMETRIC' uses-permission: name='android.permission.USE_FINGERPRINT' uses-permission: name='com.google.android.c2dm.permission.RECEIVE' -uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' uses-permission: name='io.element.android.x.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION' +uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' application-label:'Element X' application-label-ar:'Element X' application-label-be:'Element X' From 9e8fb63d8dc038de7055df3b31f0f71c8ed85edc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Oct 2026 15:54:37 +0200 Subject: [PATCH 22/92] Restore matrixui dependency needed by signedout impl Code merged on develop (AccountInfoCard in SignedOutView) now uses libraries.matrixui, which was removed as unused before the rebase. Co-Authored-By: Claude Opus 5.5 (1M context) --- features/signedout/impl/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/features/signedout/impl/build.gradle.kts b/features/signedout/impl/build.gradle.kts index ad7643d1e2..ed5dec3eb9 100644 --- a/features/signedout/impl/build.gradle.kts +++ b/features/signedout/impl/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) implementation(projects.libraries.designsystem) implementation(projects.libraries.sessionStorage.api) implementation(projects.libraries.uiStrings) From 125c38c4d21ffd4c1ca9e8cdc04d793240b69112 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:44:14 +0000 Subject: [PATCH 23/92] Update maplibre.compose to v0.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 f443a8a303..d0cba77805 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,7 +45,7 @@ appyx = "1.7.1" sqldelight = "2.4.0" wysiwyg = "2.42.1" telephoto = "0.19.0" -maplibre-compose = "0.17.0" +maplibre-compose = "0.18.0" # Dependency analysis dependencyAnalysis = "3.19.2" From a2be5176c05471c9f719a8e6bce330c84316c0fc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 2 Oct 2026 18:49:13 +0200 Subject: [PATCH 24/92] fix aaptDump.txt --- tools/manifest/gplay/release/aaptDump.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/manifest/gplay/release/aaptDump.txt b/tools/manifest/gplay/release/aaptDump.txt index 6b9b9842c5..1050329982 100644 --- a/tools/manifest/gplay/release/aaptDump.txt +++ b/tools/manifest/gplay/release/aaptDump.txt @@ -21,9 +21,9 @@ uses-permission: name='android.permission.ACCESS_LOCAL_NETWORK' uses-permission: name='android.permission.FOREGROUND_SERVICE_CAMERA' uses-permission: name='android.permission.USE_BIOMETRIC' uses-permission: name='android.permission.USE_FINGERPRINT' +uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' uses-permission: name='com.google.android.c2dm.permission.RECEIVE' uses-permission: name='io.element.android.x.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION' -uses-permission: name='android.permission.RECEIVE_BOOT_COMPLETED' application-label:'Element X' application-label-ar:'Element X' application-label-be:'Element X' From e99861a5c12a3f778eef5df46231955337e0cb94 Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 2 Oct 2026 20:41:04 +0200 Subject: [PATCH 25/92] Keep selection mode active until explicitly exited --- .../features/messages/impl/MessagesView.kt | 5 ++ .../messages/impl/timeline/SelectionState.kt | 42 ++++++++- .../impl/timeline/TimelinePresenter.kt | 36 +++----- .../impl/timeline/SelectionStateTest.kt | 89 +++++++++++++++++++ .../impl/timeline/TimelinePresenterTest.kt | 70 ++++++++++++--- 5 files changed, 204 insertions(+), 38 deletions(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt 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 b4f084ebd6..92c91dfee5 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 @@ -8,6 +8,7 @@ package io.element.android.features.messages.impl +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically @@ -194,6 +195,10 @@ fun MessagesView( } } + BackHandler(enabled = state.timelineState.isSelectionModeActive) { + state.timelineState.eventSink(TimelineEvent.ExitSelectionMode) + } + fun onContentClick(event: TimelineItem.Event) { Timber.v("onMessageClick= ${event.id}") val eventId = event.eventId diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt index f2738480aa..7a9f073bc0 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt @@ -16,6 +16,7 @@ import io.element.android.features.messages.impl.timeline.model.event.canBeForwa import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.toPersistentSet /** Maximum number of messages that can be selected at once. */ const val MAX_SELECTION_COUNT = 10 @@ -25,7 +26,10 @@ sealed interface SelectionState { /** Selection mode is not active. */ data object Disabled : SelectionState - /** Selection mode is active for [action], with the given selection. */ + /** + * Selection mode is active for [action], with the given selection. + * [selectedEventIds] can be empty: selection mode is never exited automatically, only when the user explicitly leaves it. + */ data class Active( val action: SelectionAction, val selectedEventIds: ImmutableSet, @@ -46,3 +50,39 @@ enum class SelectionAction(@field:StringRes val titleRes: Int, @field:DrawableRe fun SelectionAction.canApplyTo(event: TimelineItem.Event): Boolean = when (this) { SelectionAction.Forward -> event.isRemote && event.content.canBeForwarded() } + +/** + * This selection without [eventIds]. + * Selection mode stays active even when nothing remains selected, see [SelectionState.Active]. + */ +fun SelectionState.deselect(eventIds: Set): SelectionState = when (this) { + SelectionState.Disabled -> this + is SelectionState.Active -> copy(selectedEventIds = (selectedEventIds - eventIds).toPersistentSet()) +} + +/** + * The ids of the selected events which [SelectionState.Active.action] cannot be applied to anymore, for instance because the message has been + * deleted. They are meant to be given to [deselect]. + */ +fun SelectionState.eventIdsToDeselect(timelineItems: List): Set { + val selection = this as? SelectionState.Active ?: return emptySet() + if (selection.selectedEventIds.isEmpty()) return emptySet() + val result = mutableSetOf() + + fun process(event: TimelineItem.Event) { + val eventId = event.eventId ?: return + if (eventId in selection.selectedEventIds && !selection.action.canApplyTo(event)) { + result.add(eventId) + } + } + + timelineItems.forEach { item -> + when (item) { + is TimelineItem.Event -> process(item) + // A deleted message can be folded into a group of deleted messages, so look inside the groups too. + is TimelineItem.GroupedEvents -> item.events.forEach(::process) + is TimelineItem.Virtual -> Unit + } + } + return result +} 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 c46e28de1a..a7d4641085 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 @@ -77,9 +77,11 @@ import kotlinx.collections.immutable.toPersistentSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -273,10 +275,8 @@ class TimelinePresenter( val current = selectionState as? SelectionState.Active ?: return val selected = current.selectedEventIds selectionState = when { - event.eventId in selected -> { - val next = (selected - event.eventId).toPersistentSet() - if (next.isEmpty()) SelectionState.Disabled else current.copy(selectedEventIds = next) - } + // Deselecting the last event does not exit selection mode: it is only left explicitly, as on iOS. + event.eventId in selected -> current.deselect(setOf(event.eventId)) selected.size >= MAX_SELECTION_COUNT -> { snackbarDispatcher.post(SnackbarMessage(CommonStrings.screen_room_maximum_messages_selected)) current @@ -371,6 +371,14 @@ class TimelinePresenter( } .launchIn(this) + // Drop the selected events which the action cannot be applied to anymore, for instance because the message has been deleted. + timelineItemsFactory.timelineItems + .map { items -> selectionState.eventIdsToDeselect(items) } + .filter { eventIdsToDeselect -> eventIdsToDeselect.isNotEmpty() } + .flowOn(dispatchers.computation) + .onEach { eventIdsToDeselect -> selectionState = selectionState.deselect(eventIdsToDeselect) } + .launchIn(this) + combine( timelineController.timelineItems(), room.membersStateFlow, @@ -396,26 +404,6 @@ class TimelinePresenter( computeNewItemState(timelineItems, prevMostRecentItemId, newEventState) } - val selectableEventIds = (selectionState as? SelectionState.Active)?.let { active -> - timelineItems - .filterIsInstance() - .filter { active.action.canApplyTo(it) } - .mapNotNull { it.eventId } - .toSet() - }.orEmpty() - - // Reconcile the selection when the items change: drop any selected event that is no longer - // present or no longer selectable, and exit selection mode if nothing remains selected. - LaunchedEffect(selectableEventIds) { - val active = selectionState as? SelectionState.Active ?: return@LaunchedEffect - val reconciled = active.selectedEventIds.filter { it in selectableEventIds }.toPersistentSet() - selectionState = when { - reconciled.isEmpty() -> SelectionState.Disabled - reconciled.size != active.selectedEventIds.size -> active.copy(selectedEventIds = reconciled) - else -> active - } - } - // Keyed on the full [timelineItems] reference (not just .size) so we re-scan when the // read marker advances in place — the SDK swaps the marker virtual item to a new position // without changing the list length, e.g. when [markRoomAsFullyRead] is sent while at the diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt new file mode 100644 index 0000000000..ea45813a10 --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2026 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.messages.impl.timeline + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemRedactedContent +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UniqueId +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.AN_EVENT_ID_3 +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentSet +import org.junit.Test + +class SelectionStateTest { + @Test + fun `eventIdsToDeselect - selection mode being disabled has nothing to drop`() { + val items = listOf(aTimelineItemEvent(eventId = AN_EVENT_ID, content = aTimelineItemRedactedContent())) + assertThat(SelectionState.Disabled.eventIdsToDeselect(items)).isEmpty() + } + + @Test + fun `eventIdsToDeselect - an empty selection has nothing to drop`() { + val items = listOf(aTimelineItemEvent(eventId = AN_EVENT_ID, content = aTimelineItemRedactedContent())) + assertThat(anActiveSelection().eventIdsToDeselect(items)).isEmpty() + } + + @Test + fun `eventIdsToDeselect - a selected event the action still applies to is kept`() { + val items = listOf(aTimelineItemEvent(eventId = AN_EVENT_ID)) + assertThat(anActiveSelection(listOf(AN_EVENT_ID)).eventIdsToDeselect(items)).isEmpty() + } + + @Test + fun `eventIdsToDeselect - a selected event which has been deleted is dropped`() { + val items = listOf( + aTimelineItemEvent(eventId = AN_EVENT_ID_2, content = aTimelineItemRedactedContent()), + aTimelineItemEvent(eventId = AN_EVENT_ID), + ) + assertThat(anActiveSelection(listOf(AN_EVENT_ID, AN_EVENT_ID_2)).eventIdsToDeselect(items)).containsExactly(AN_EVENT_ID_2) + } + + @Test + fun `eventIdsToDeselect - a selected event which is not in the timeline is ignored`() { + val items = listOf(aTimelineItemEvent(eventId = AN_EVENT_ID)) + // AN_EVENT_ID_3 may simply not be loaded anymore, so it is not dropped here. + assertThat(anActiveSelection(listOf(AN_EVENT_ID, AN_EVENT_ID_3)).eventIdsToDeselect(items)).isEmpty() + } + + @Test + fun `eventIdsToDeselect - a deleted selected event folded into a group is dropped`() { + val items = listOf( + TimelineItem.GroupedEvents( + id = UniqueId("group"), + events = persistentListOf( + aTimelineItemEvent(eventId = AN_EVENT_ID_2, content = aTimelineItemRedactedContent()), + aTimelineItemEvent(eventId = AN_EVENT_ID_3, content = aTimelineItemRedactedContent()), + ), + aggregatedReadReceipts = persistentListOf(), + ), + aTimelineItemEvent(eventId = AN_EVENT_ID), + ) + assertThat(anActiveSelection(listOf(AN_EVENT_ID, AN_EVENT_ID_2)).eventIdsToDeselect(items)).containsExactly(AN_EVENT_ID_2) + } + + @Test + fun `deselect - removes the given events and keeps selection mode active`() { + val selection = anActiveSelection(listOf(AN_EVENT_ID, AN_EVENT_ID_2)) + assertThat(selection.deselect(setOf(AN_EVENT_ID))).isEqualTo(anActiveSelection(listOf(AN_EVENT_ID_2))) + assertThat(selection.deselect(setOf(AN_EVENT_ID, AN_EVENT_ID_2))).isEqualTo(anActiveSelection()) + } + + @Test + fun `deselect - does nothing when selection mode is disabled`() { + assertThat(SelectionState.Disabled.deselect(setOf(AN_EVENT_ID))).isEqualTo(SelectionState.Disabled) + } + + private fun anActiveSelection(eventIds: List = emptyList()) = SelectionState.Active( + action = SelectionAction.Forward, + selectedEventIds = eventIds.toPersistentSet(), + ) +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 45667e298c..eeb97befff 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -50,10 +50,8 @@ import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.EventReaction import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.OtherState 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.StateContent import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.virtual.VirtualTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID @@ -1862,7 +1860,7 @@ class TimelinePresenterTest { } @Test - fun `present - toggling the last selected event exits selection mode`() = runTest { + fun `present - toggling the last selected event keeps selection mode active`() = runTest { val timelineItems = MutableStateFlow( listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) ) @@ -1875,6 +1873,28 @@ class TimelinePresenterTest { initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) consumeItemsUntilPredicate { it.isSelectionModeActive } initialState.eventSink(TimelineEvent.ToggleSelection(AN_EVENT_ID)) + // Selection mode is only exited explicitly, so an empty selection keeps it active. + val state = consumeItemsUntilPredicate { it.selectedCount == 0 }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Active(SelectionAction.Forward, persistentSetOf())) + assertThat(state.isSelectionModeActive).isTrue() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - exiting selection mode clears the selection`() = runTest { + val timelineItems = MutableStateFlow( + listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) + ) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) + consumeItemsUntilPredicate { it.isSelectionModeActive } + initialState.eventSink(TimelineEvent.ExitSelectionMode) val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() assertThat(state.selectionState).isEqualTo(SelectionState.Disabled) cancelAndIgnoreRemainingEvents() @@ -1903,7 +1923,35 @@ class TimelinePresenterTest { } @Test - fun `present - selection drops events the action cannot apply to`() = runTest { + fun `present - a selected event which cannot be forwarded anymore is deselected`() = runTest { + val timelineItems = MutableStateFlow( + listOf( + MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent())), + MatrixTimelineItem.Event(UniqueId("2"), anEventTimelineItem(eventId = AN_EVENT_ID_2, content = aMessageContent())), + ) + ) + val presenter = createTimelinePresenter( + timeline = FakeTimeline(timelineItems = timelineItems), + featureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.MessageMultiSelect.key to true)), + ) + presenter.test { + val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() + initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) + initialState.eventSink(TimelineEvent.ToggleSelection(AN_EVENT_ID_2)) + consumeItemsUntilPredicate { it.selectedCount == 2 } + // The first message is deleted, so it cannot be forwarded anymore. + timelineItems.value = listOf( + MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aRedactedContent())), + MatrixTimelineItem.Event(UniqueId("2"), anEventTimelineItem(eventId = AN_EVENT_ID_2, content = aMessageContent())), + ) + val state = consumeItemsUntilPredicate { it.selectedCount == 1 }.last() + assertThat(state.selectionState).isEqualTo(SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID_2))) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - the selection survives a timeline which no longer contains the selected event`() = runTest { val timelineItems = MutableStateFlow( listOf(MatrixTimelineItem.Event(UniqueId("1"), anEventTimelineItem(eventId = AN_EVENT_ID, content = aMessageContent()))) ) @@ -1915,15 +1963,11 @@ class TimelinePresenterTest { val initialState = consumeItemsUntilPredicate { it.hasAnyEvent }.last() initialState.eventSink(TimelineEvent.EnterSelectionMode(SelectionAction.Forward, AN_EVENT_ID)) consumeItemsUntilPredicate { it.isSelectionModeActive } - // The message is replaced by a state event, which cannot be forwarded. - timelineItems.value = listOf( - MatrixTimelineItem.Event( - UniqueId("1"), - anEventTimelineItem(eventId = AN_EVENT_ID, content = StateContent("", OtherState.RoomCreate)), - ) - ) - val state = consumeItemsUntilPredicate { !it.isSelectionModeActive }.last() - assertThat(state.selectionState).isEqualTo(SelectionState.Disabled) + // The timeline no longer holds the selected event, for instance because it has been reset or because it switched to a detached timeline. + timelineItems.value = emptyList() + val state = consumeItemsUntilPredicate { !it.hasAnyEvent }.last() + // The selection is the user intent: it is only validated when the action is confirmed, so that a transient timeline update cannot drop it. + assertThat(state.selectionState).isEqualTo(SelectionState.Active(SelectionAction.Forward, persistentSetOf(AN_EVENT_ID))) cancelAndIgnoreRemainingEvents() } } From f59f5da64cece3e1f13de75d00c3bddc46f41396 Mon Sep 17 00:00:00 2001 From: Bardi Harborow Date: Sun, 4 Oct 2026 20:24:22 +1100 Subject: [PATCH 26/92] Mark the recovery key as sensitive when copying it to the clipboard The recovery key was copied without EXTRA_IS_SENSITIVE, so Android 13+ showed it in the clipboard preview and keyboards could store it in their clipboard history. Add an isSensitive option to CopyToClipboardUseCase and Context.copyToClipboard, and set it when copying the recovery key. --- .../impl/setup/SecureBackupSetupView.kt | 1 + .../system/CopyToClipboardUseCase.kt | 26 ++++++++++++++++--- .../androidutils/system/SystemUtils.kt | 5 ++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index c22bd44c99..802b9ae070 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -122,6 +122,7 @@ private fun Content( context.copyToClipboard( text = formattedRecoveryKey, toastMessage = toastMessage, + isSensitive = true, ) state.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt index 53a1c33995..e20e80884f 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt @@ -9,15 +9,35 @@ package io.element.android.libraries.androidutils.system import android.content.ClipData +import android.content.ClipDescription import android.content.ClipboardManager import android.content.Context +import android.os.Build +import android.os.PersistableBundle import androidx.core.content.getSystemService class CopyToClipboardUseCase( private val context: Context, ) { - fun execute(text: CharSequence) { - context.getSystemService() - ?.setPrimaryClip(ClipData.newPlainText("", text)) + /** + * @param isSensitive if true, the clip is flagged so the system clipboard preview (Android 13+) and keyboards hide its content. + */ + fun execute(text: CharSequence, isSensitive: Boolean = false) { + val clipData = ClipData.newPlainText("", text) + if (isSensitive) { + clipData.description.extras = PersistableBundle().apply { + putBoolean(EXTRA_IS_SENSITIVE, true) + } + } + context.getSystemService()?.setPrimaryClip(clipData) + } + + private companion object { + // The constant only exists from API 33, but the documentation recommends using its value on older versions too. + val EXTRA_IS_SENSITIVE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ClipDescription.EXTRA_IS_SENSITIVE + } else { + "android.content.extra.IS_SENSITIVE" + } } } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 861aca032d..076cda5704 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -63,9 +63,10 @@ fun Context.getVersionCodeFromManifest(): Long { */ fun Context.copyToClipboard( text: CharSequence, - toastMessage: String? = null + toastMessage: String? = null, + isSensitive: Boolean = false, ) { - CopyToClipboardUseCase(this).execute(text) + CopyToClipboardUseCase(this).execute(text, isSensitive) toastMessage?.let { toast(it) } } From 50460ad031ca28fcbd477fe421563bd7b26fd399 Mon Sep 17 00:00:00 2001 From: bmarty <3940906+bmarty@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:40:51 +0000 Subject: [PATCH 27/92] Sync Strings from Localazy --- .../src/main/res/values-zh/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-hr/translations.xml | 4 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-ru/translations.xml | 4 +- .../src/main/res/values-hr/translations.xml | 2 +- .../src/main/res/values-in/translations.xml | 2 +- .../src/main/res/values-pl/translations.xml | 3 + .../src/main/res/values-ru/translations.xml | 7 +- .../src/main/res/values-uk/translations.xml | 2 + .../src/main/res/values-zh/translations.xml | 3 + .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-pl/translations.xml | 6 + .../src/main/res/values-uk/translations.xml | 3 + .../src/main/res/values-zh/translations.xml | 3 + .../src/main/res/values-pl/translations.xml | 9 +- .../src/main/res/values-uk/translations.xml | 9 +- .../src/main/res/values-zh/translations.xml | 9 +- .../src/main/res/values-pl/translations.xml | 1 + .../src/main/res/values-uk/translations.xml | 1 + .../src/main/res/values-zh/translations.xml | 1 + .../src/main/res/values-in/translations.xml | 4 +- .../src/main/res/values-pl/translations.xml | 15 + .../src/main/res/values-ru/translations.xml | 9 + .../src/main/res/values-uk/translations.xml | 4 + .../src/main/res/values-zh/translations.xml | 7 +- .../src/main/res/values/localazy.xml | 3 + screenshots/html/data.js | 2344 +++++++++-------- 29 files changed, 1269 insertions(+), 1199 deletions(-) diff --git a/features/createroom/impl/src/main/res/values-zh/translations.xml b/features/createroom/impl/src/main/res/values-zh/translations.xml index b9934dfb63..8b0f251bdb 100644 --- a/features/createroom/impl/src/main/res/values-zh/translations.xml +++ b/features/createroom/impl/src/main/res/values-zh/translations.xml @@ -8,7 +8,7 @@ "新房间" "新空间" "仅限受邀人员加入。" - "私密" + "私有" "任何人都能找到此房间。 你可以随时在房间设置中更改。" "任何人都可以加入,包括你的组织之外的人员。" @@ -18,7 +18,7 @@ "%1$s 中的任何人都可以加入,但其他人必须申请访问。" "申请加入" "仅限受邀人员加入。" - "私密" + "私有" "任何人都可以加入,包括你的组织之外的人员。" "公共" "%1$s 中的任何人都可以加入。" diff --git a/features/home/impl/src/main/res/values-in/translations.xml b/features/home/impl/src/main/res/values-in/translations.xml index 36512038ba..3b293502b1 100644 --- a/features/home/impl/src/main/res/values-in/translations.xml +++ b/features/home/impl/src/main/res/values-in/translations.xml @@ -14,7 +14,7 @@ "Penyimpanan kunci Anda tidak sinkron" "Untuk memastikan Anda tidak melewatkan panggilan penting, silakan ubah pengaturan Anda untuk memperbolehkan notifikasi layar penuh ketika ponsel Anda terkunci." "Tingkatkan pengalaman panggilan Anda" - "Semua Obrolan" + "Obrolan" "Space" "Apakah Anda yakin ingin menolak undangan untuk bergabung ke %1$s?" "Tolak undangan" @@ -46,7 +46,7 @@ Untuk sementara, Anda dapat membatalkan pilihan saringan untuk melihat percakapa "Selamat! Anda tidak memiliki pesan yang belum dibaca!" "Permintaan untuk bergabung dikirim" - "Semua Obrolan" + "Obrolan" "Tandai sebagai dibaca" "Tandai sebagai belum dibaca" "Ruangan ini telah ditingkatkan" diff --git a/features/login/impl/src/main/res/values-in/translations.xml b/features/login/impl/src/main/res/values-in/translations.xml index 34ba1bd0ed..7a071a285b 100644 --- a/features/login/impl/src/main/res/values-in/translations.xml +++ b/features/login/impl/src/main/res/values-in/translations.xml @@ -89,7 +89,7 @@ Coba masuk secara manual, atau pindai kode QR dengan perangkat lain." "%1$s tidak didukung" "Siap untuk memindai" "Buka %1$s di perangkat desktop" - "Klik pada avatar Anda" + "Klik avatar Anda" "Pilih %1$s" "“Tautkan perangkat baru”" "Pindai kode QR dengan perangkat ini" diff --git a/features/messages/impl/src/main/res/values-hr/translations.xml b/features/messages/impl/src/main/res/values-hr/translations.xml index 33daf3b00f..8b8a43cde7 100644 --- a/features/messages/impl/src/main/res/values-hr/translations.xml +++ b/features/messages/impl/src/main/res/values-hr/translations.xml @@ -48,9 +48,9 @@ "Anketa" "Oblikovanje teksta" "Razlog (neobavezno)" - "Unesite…" + "Unesite razlog…" "(Neobavezno)" - "Potvrdite uklanjanje" + "Ukloniti poruku?" "Povijest poruka trenutačno nije dostupna." "Povijest poruka nije dostupna u ovoj sobi. Potvrdite ovaj uređaj kako biste vidjeli povijest poruka." "Želite li ih pozvati natrag?" diff --git a/features/messages/impl/src/main/res/values-in/translations.xml b/features/messages/impl/src/main/res/values-in/translations.xml index bae097e818..bed52fe550 100644 --- a/features/messages/impl/src/main/res/values-in/translations.xml +++ b/features/messages/impl/src/main/res/values-in/translations.xml @@ -36,7 +36,7 @@ "Tambahkan media" "Blokir pengguna" "Centang jika Anda ingin menyembunyikan semua pesan saat ini dan yang akan datang dari pengguna ini" - "Pesan ini akan dilaporkan ke administrator homeserver Anda. Mereka tidak akan dapat membaca pesan terenkripsi apa pun." + "Pesan ini akan dilaporkan ke penyedia akun Anda. Mereka tidak akan dapat membaca pesan terenkripsi apa pun." "Alasan melaporkan konten ini" "Kamera" "Ambil foto" 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 1fab6ed5b3..d23ba8febc 100644 --- a/features/messages/impl/src/main/res/values-ru/translations.xml +++ b/features/messages/impl/src/main/res/values-ru/translations.xml @@ -49,9 +49,9 @@ "Опрос" "Форматирование текста" "Причина (необязательно)" - "Введите…" + "Укажите причину…" "(Необязательно)" - "Подтвердите удаление" + "Удалить сообщение?" "В настоящее время история сообщений недоступна в этой комнате." "История сообщений в этой комнате недоступна. Проверьте это устройство, чтобы увидеть историю сообщений." "Хотите пригласить их снова?" diff --git a/features/preferences/impl/src/main/res/values-hr/translations.xml b/features/preferences/impl/src/main/res/values-hr/translations.xml index 1bec1cb75b..387da63a4e 100644 --- a/features/preferences/impl/src/main/res/values-hr/translations.xml +++ b/features/preferences/impl/src/main/res/values-hr/translations.xml @@ -38,7 +38,7 @@ "Ako je to isključeno, nećete moći slati ili primati potvrde o čitanju ili obavijesti o tipkanju." "Uvijek sakrij" "Uvijek prikaži" - "U privatnim sobama" + "Prikaži u privatnim sobama" "Skriveni medij uvijek se može prikazati tako se da se dodirne" "Prikaži medije na vremenskoj traci" "Omogući opciju za prikaz izvora poruke na vremenskoj traci." diff --git a/features/preferences/impl/src/main/res/values-in/translations.xml b/features/preferences/impl/src/main/res/values-in/translations.xml index faad2bece7..379bca4fb0 100644 --- a/features/preferences/impl/src/main/res/values-in/translations.xml +++ b/features/preferences/impl/src/main/res/values-in/translations.xml @@ -85,7 +85,7 @@ Jika Anda melanjutkan, beberapa pengaturan Anda dapat berubah." "Semua" "Sebutan" "Beri tahu saya tentang" - "Beri tahu saya pada @room" + "Penyebutan @room" "Kustom" "Suara kustom" "Terjadi kesalahan saat menghapus file." diff --git a/features/preferences/impl/src/main/res/values-pl/translations.xml b/features/preferences/impl/src/main/res/values-pl/translations.xml index 3142639d94..9b1904e95d 100644 --- a/features/preferences/impl/src/main/res/values-pl/translations.xml +++ b/features/preferences/impl/src/main/res/values-pl/translations.xml @@ -93,6 +93,8 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "Wzmianki" "Powiadamiaj mnie przez" "Wzmianki @room" + "Kropki lub liczba nieprzeczytanych wiadomości" + "Pokaż całą aktywność na liście pokoi" "Własny" "Własny dźwięk…" "Błąd usuwania pliku" @@ -109,6 +111,7 @@ Niektóre ustawienia mogą ulec zmianie, jeśli kontynuujesz." "ustawienia systemowe" "Powiadomienia systemowe wyłączone" "Powiadomienia" + "Dodaj konto" "Z dala od urządzenia" "Zaraz wracam" "Własny" 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 c0c1383f1c..96430521d5 100644 --- a/features/preferences/impl/src/main/res/values-ru/translations.xml +++ b/features/preferences/impl/src/main/res/values-ru/translations.xml @@ -38,8 +38,8 @@ "Если выключено, Вы не будете видеть, кто печатает и читает сообщения, а также другие пользователи не будут знать, когда Вы печатаете или читаете сообщения." "Всегда скрывать" "Всегда показывать" - "В приватных комнатах" - "Скрытые медиа всегда можно просмотреть, нажав на них." + "Показывать в приватных комнатах" + "Скрытый контент всегда можно отобразить, нажав на него." "Показать медиа в истории сообщений" "Включить опцию просмотра источника сообщения в истории сообщений." "У Вас нет заблокированных пользователей" @@ -62,6 +62,9 @@ "Попробуйте функции в разработке. Эти функции ещё не завершены, они нестабильны и могут измениться." "Хотите попробовать?" "Лаборатория" + "Приглашения" + "Другие пользователи" + "Ваш внешний вид" "Дополнительные параметры" "Мелодия звонка" "Аудио и видео звонки" diff --git a/features/preferences/impl/src/main/res/values-uk/translations.xml b/features/preferences/impl/src/main/res/values-uk/translations.xml index 9ba85717b5..bdce557b12 100644 --- a/features/preferences/impl/src/main/res/values-uk/translations.xml +++ b/features/preferences/impl/src/main/res/values-uk/translations.xml @@ -93,6 +93,8 @@ "Згадки" "Сповіщати мене про" "Згадки @room" + "Крапки або кількість непрочитаних повідомлень" + "Показати всю діяльність у списку кімнат" "Власний" "Користувацький звук…" "Помилка видалення файлу" diff --git a/features/preferences/impl/src/main/res/values-zh/translations.xml b/features/preferences/impl/src/main/res/values-zh/translations.xml index 48c269815e..bf6bc9fa51 100644 --- a/features/preferences/impl/src/main/res/values-zh/translations.xml +++ b/features/preferences/impl/src/main/res/values-zh/translations.xml @@ -91,6 +91,8 @@ "提及" "通知我以下类型" "含有 @room 的提及" + "用于未读消息的圆点或数字" + "显示房间列表中的所有活动" "自定义" "自定义提示音…" "删除文件时出错" @@ -107,6 +109,7 @@ "系统设置" "系统通知已关闭" "通知" + "添加账户" "离开" "马上回来" "自定义…" diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 6e70c9b685..c702ac62a3 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -110,6 +110,7 @@ If you proceed, some of your settings may change." "system settings" "System notifications turned off" "Notifications" + "Add account" "Away" "Be right back" "Custom…" diff --git a/features/roomdetails/impl/src/main/res/values-pl/translations.xml b/features/roomdetails/impl/src/main/res/values-pl/translations.xml index f446cb8334..4b805104f7 100644 --- a/features/roomdetails/impl/src/main/res/values-pl/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-pl/translations.xml @@ -42,6 +42,9 @@ "Masz niezapisane zmiany." "Zapisać zmiany?" "Dodaj temat" + "Naciśnij i mów" + "Pokój audio" + "Rozmowy wideo" "Szyfrowany" "Nieszyfrowany" "Pokój publiczny" @@ -67,6 +70,9 @@ "Profil" "Prośby o dołączenie" "Role i uprawnienia" + "Naciśnij i mów" + "Pokój audio" + "Rozmowy wideo" "Nazwa" "Bezpieczeństwo i prywatność" "Bezpieczeństwo" diff --git a/features/roomdetails/impl/src/main/res/values-uk/translations.xml b/features/roomdetails/impl/src/main/res/values-uk/translations.xml index 9094e032a9..a523f25f20 100644 --- a/features/roomdetails/impl/src/main/res/values-uk/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-uk/translations.xml @@ -42,6 +42,9 @@ "У вас є не збережені зміни." "Зберегти зміни?" "Додати тему" + "Натисніть, щоб говорити" + "Аудіо в кімнаті" + "Відеовиклики" "Зашифровано" "Не зашифровано" "Загальнодоступна кімната" diff --git a/features/roomdetails/impl/src/main/res/values-zh/translations.xml b/features/roomdetails/impl/src/main/res/values-zh/translations.xml index dea8350422..768e5d7eb5 100644 --- a/features/roomdetails/impl/src/main/res/values-zh/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-zh/translations.xml @@ -42,6 +42,9 @@ "你有未保存的更改。" "保存更改?" "添加主题" + "按下发言" + "房间音频" + "视频通话" "已加密" "未加密" "公共房间" diff --git a/features/signedout/impl/src/main/res/values-pl/translations.xml b/features/signedout/impl/src/main/res/values-pl/translations.xml index 9b83e7d692..7da7ecc2f0 100644 --- a/features/signedout/impl/src/main/res/values-pl/translations.xml +++ b/features/signedout/impl/src/main/res/values-pl/translations.xml @@ -1,8 +1,9 @@ - "Zmieniono hasło w innej sesji" - "Sesja została usunięta z innej sesji" - "Administrator serwera unieważnił Twój dostęp" + "Usunięto urządzenie ze swojego konta" + "Hasło zostało zmienione na innym urządzeniu" + "Sesja serwera wygasła lub administrator zablokował Twój dostęp" "Mogłeś zostać wylogowany z powodów wymienionych poniżej. Zaloguj się ponownie, aby dalej korzystać z %s." - "Zostałeś wylogowany" + "Urządzenie zostało usunięte przez jedną z poniższych przyczyn. Zaloguj się ponownie, aby powiązać to urządzenie ze swoim kontem." + "Urządzenie usunięte" diff --git a/features/signedout/impl/src/main/res/values-uk/translations.xml b/features/signedout/impl/src/main/res/values-uk/translations.xml index ebf364518b..8360646986 100644 --- a/features/signedout/impl/src/main/res/values-uk/translations.xml +++ b/features/signedout/impl/src/main/res/values-uk/translations.xml @@ -1,8 +1,9 @@ - "Ви змінили пароль під час іншого сеансу" - "Ви видалили сеанс з іншого сеансу" - "Адміністратор вашого сервера визнав ваш доступ недійсним" + "Ви вилучили цей пристрій зі свого облікового запису" + "Ви змінили пароль на іншому пристрої" + "Термін дії сеансу на вашому сервері закінчився або адміністратор заблокував ваш доступ" "Можливо, ви вийшли з системи з однієї з причин, наведених нижче. Будь ласка, увійдіть знову, щоб продовжити використання %s." - "Ви вийшли з системи" + "Цей пристрій було видалено з однієї з наведених нижче причин. Будь ласка, увійдіть знову, щоб підключити цей пристрій до вашого облікового запису." + "Пристрій вилучено" diff --git a/features/signedout/impl/src/main/res/values-zh/translations.xml b/features/signedout/impl/src/main/res/values-zh/translations.xml index 6768608294..6c9b5a4bbe 100644 --- a/features/signedout/impl/src/main/res/values-zh/translations.xml +++ b/features/signedout/impl/src/main/res/values-zh/translations.xml @@ -1,8 +1,9 @@ - "你在另一个会话中更改了密码" - "你已从其它会话中删除此会话" - "服务器管理员已禁止你的访问" + "你已为账户移除此设备" + "你已在另一设备上更改密码" + "你的服务器会话已过期,或管理员已阻止你的访问。" "你可能因以下原因而被注销。请重新登录以继续使用 %s。" - "你已注销" + "此设备因以下原因之一已被移除。请重新登录以连接设备到你的账户。" + "设备已移除" diff --git a/features/verifysession/impl/src/main/res/values-pl/translations.xml b/features/verifysession/impl/src/main/res/values-pl/translations.xml index 71cea2c403..bc326cbc00 100644 --- a/features/verifysession/impl/src/main/res/values-pl/translations.xml +++ b/features/verifysession/impl/src/main/res/values-pl/translations.xml @@ -44,6 +44,7 @@ "Otwórz aplikację na drugim zweryfikowanym urządzeniu" "Dla dodatkowej ochrony, zweryfikuj tego użytkownika, porównując zestaw emoji na obu urządzeniach. Zrób to, korzystając z zaufanej metody komunikacji." "Zweryfikować tego użytkownika?" + "Użytkownik proszący o weryfikację:" "Dla dodatkowej ochrony, inny użytkownik chce zweryfikować Twoją tożsamość. Pojawi się unikalny zestaw emoji do porównania." "Powinno wyskoczyć okno na drugim urządzeniu. Rozpocznij tam weryfikację." "Rozpocznij weryfikację na drugim urządzeniu" diff --git a/features/verifysession/impl/src/main/res/values-uk/translations.xml b/features/verifysession/impl/src/main/res/values-uk/translations.xml index 803e57fa39..620229e2f6 100644 --- a/features/verifysession/impl/src/main/res/values-uk/translations.xml +++ b/features/verifysession/impl/src/main/res/values-uk/translations.xml @@ -44,6 +44,7 @@ "Відкрийте застосунок на іншому звіреному пристрої" "Для додаткової безпеки звірте цього користувача, порівнявши набір емоджі на ваших пристроях. Зробіть це, використовуючи надійний спосіб спілкування." "Звірити цього користувача?" + "Користувач, який запитує звірку:" "Для додаткової безпеки інший користувач хоче звірити вашу цифрову ідентичність. Вам буде показано набір емодзі для порівняння." "На іншому пристрої має з’явитися спливне вікно. Розпочніть звіряння звідти." "Розпочати звіряння на іншому пристрої" diff --git a/features/verifysession/impl/src/main/res/values-zh/translations.xml b/features/verifysession/impl/src/main/res/values-zh/translations.xml index 4b32d53ea6..3ae122cbb7 100644 --- a/features/verifysession/impl/src/main/res/values-zh/translations.xml +++ b/features/verifysession/impl/src/main/res/values-zh/translations.xml @@ -44,6 +44,7 @@ "在另一台验证的设备上打开应用" "为提高安全性,请通过比较设备上的一组 Emoji 以验证此用户。通过使用安全的方式比如面对面来实施此步骤。" "验证此用户?" + "用户正在请求验证:" "为提高安全性,另一用户想要验证你的数字身份。你将看到一组 Emoij 供你比较。" "你应该会在另一台设备上看到一个弹出窗口。现在从该处开始验证。" "在另一台设备上开始验证" diff --git a/libraries/push/impl/src/main/res/values-in/translations.xml b/libraries/push/impl/src/main/res/values-in/translations.xml index db9147c0cf..fcd8eaa76b 100644 --- a/libraries/push/impl/src/main/res/values-in/translations.xml +++ b/libraries/push/impl/src/main/res/values-in/translations.xml @@ -45,7 +45,7 @@ "%1$s disebut atau dibalas" "Kami mengundang Anda untuk bergabung di ruang ini." "%1$s mengundang Anda untuk bergabung ke ruang ini." - "Anda sedang melihat pemberitahuan ini! Klik saya!" + "Anda sedang melihat notifikasi ini! Klik saya!" "Diskusi di %1$s" "%1$s: %2$s" "%1$s: %2$s %3$s" @@ -87,7 +87,7 @@ "Tidak dapat menampilkan notifikasi." "Notifikasi telah diklik!" "Tampilan notifikasi" - "Silakan klik pada notifikasi untuk melanjutkan tes." + "Silakan klik notifikasi untuk melanjutkan tes." "Pastikan aplikasi menerima notifikasi dorongan." "Kesalahan: pendorong telah menolak permintaan." "Kesalahan: %1$s." diff --git a/libraries/ui-strings/src/main/res/values-pl/translations.xml b/libraries/ui-strings/src/main/res/values-pl/translations.xml index 0555716c57..cad2548b2d 100644 --- a/libraries/ui-strings/src/main/res/values-pl/translations.xml +++ b/libraries/ui-strings/src/main/res/values-pl/translations.xml @@ -101,6 +101,7 @@ "Wybierz plik" "Wybierz zdjęcie" "Wyczyść" + "Wyczyść wszystko" "Zamknij" "Dokończ weryfikację" "Potwierdź" @@ -139,6 +140,11 @@ "Zakończ" "Nie pamiętasz hasła?" "Przekaż dalej" + + "Przekaż %1$d wiadomość" + "Przekaż %1$d wiadomości" + "Przekaż %1$d wiadomości" + "Wróć" "Przejdź do ról i uprawnień" "Przejdź do ustawień" @@ -245,6 +251,7 @@ "Rozpoczęto rozmowę" "Odrzuciłeś połączenie" "Backup czatu" + "Czaty" "Blok kodu" "Skopiowano do schowka" "Prawa autorskie" @@ -285,6 +292,7 @@ Powód: %1$s." "Plik zapisany" "Plik zapisany do folderu Pobrane" "Przekaż wiadomość" + "Przekaż do" "Często używane" "Galeria" @@ -299,6 +307,7 @@ Powód: %1$s." "GIF" "Trwa połączenie grupowe" + "Pokoje" "Zdjęcie" "W odpowiedzi do %1$s" "Zainstaluj APK" @@ -419,6 +428,7 @@ Powód: %1$s." "Udostępniona lokalizacja na żywo" "Udostępniona lokalizacja" "Udostępniona przestrzeń" + "Zalogowano" "Usuwanie urządzenia" "Coś poszło nie tak" "Napotkaliśmy problem. Spróbuj ponownie." @@ -579,6 +589,11 @@ Czy na pewno chcesz kontynuować?" "Zmniejsz" "Udostępnianie lokalizacji na żywo" "Zaznaczono maksymalną liczbę wiadomości" + + "Przekazano %1$d wiadomość" + "Przekazano %1$d wiadomości" + "Przekazano %1$d wiadomości" + "Już oglądasz ten pokój!" "%1$s z %2$s" "%1$s przypiętych wiadomości" 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 c80403468f..7279b906c0 100644 --- a/libraries/ui-strings/src/main/res/values-ru/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ru/translations.xml @@ -111,6 +111,7 @@ "Скопировать ссылку" "Скопировать ссылку на сообщение" "Копировать текст" + "Скопировать в буфер обмена" "Создать" "Создать комнату" "Создать пространство" @@ -224,6 +225,7 @@ "Доступно обновление" "О приложении" "Политика допустимого использования" + "Настройки аккаунта" "Добавить аккаунт" "Добавить другой аккаунт" "Добавление подписи" @@ -232,6 +234,7 @@ "Аналитика" "Вы покинули комнату" "Вы вышли из сессии" + "Настройки приложения" "Внешний вид" "Аудио" "Бета-версия" @@ -242,6 +245,7 @@ "Звонок начат" "Ты отклонил звонок" "Резервная копия чатов" + "Блок кода" "Скопировано в буфер обмена" "Авторское право" "Создание комнаты…" @@ -308,11 +312,13 @@ "Трансляция местоположения завершена" "Загрузка…" "Загрузить больше…" + "Обмен местоположением" "%d" "%d другие" "%d другие" + "Качество загружаемого медиа" "%1$d участник" "%1$d участников" @@ -323,6 +329,7 @@ "Ошибка отправки сообщения" "Оформление сообщения" "Сообщение удалено" + "Модерация и безопасность" "Современный" "Выкл. звук" "Имя" @@ -428,6 +435,7 @@ "Успешно" "Рекомендуемые" "Предложения" + "Сменить аккаунт" "Синхронизация" "Текст" "Уведомления о третьих лицах" @@ -467,6 +475,7 @@ "Ожидание трансляции местоположения…" "Любой может видеть историю" "Вы" + "Ваш аккаунт" "Загрузка недоступна." "Файл не найден" "Сканирование…" diff --git a/libraries/ui-strings/src/main/res/values-uk/translations.xml b/libraries/ui-strings/src/main/res/values-uk/translations.xml index e791c4b5be..0bc43a3027 100644 --- a/libraries/ui-strings/src/main/res/values-uk/translations.xml +++ b/libraries/ui-strings/src/main/res/values-uk/translations.xml @@ -101,6 +101,7 @@ "Вибрати файл" "Вибрати фото" "Очистити" + "Очистити все" "Закрити" "Завершити звіряння" "Підтвердити" @@ -250,6 +251,7 @@ "Виклик розпочато" "Ви відхилили виклик" "Резервне копіювання бесіди" + "Бесіди" "Блок коду" "Скопійовано до буферу обміну" "Авторське право" @@ -290,6 +292,7 @@ "Файл збережено" "Файл збережений у Завантаження" "Переслати повідомлення" + "Переслати" "Частовживані" "Галерея" @@ -424,6 +427,7 @@ "Поділитися місцезнаходженням в реальному часі" "Поширене розташування" "Простір спільного користування" + "Вхід виконано" "Вилучення пристрою" "Щось пішло не так" "Ми зіткнулися з проблемою. Будь ласка, повторіть спробу." diff --git a/libraries/ui-strings/src/main/res/values-zh/translations.xml b/libraries/ui-strings/src/main/res/values-zh/translations.xml index 36331e302b..74cc7bf9f7 100644 --- a/libraries/ui-strings/src/main/res/values-zh/translations.xml +++ b/libraries/ui-strings/src/main/res/values-zh/translations.xml @@ -95,6 +95,7 @@ "选择文件" "选择照片" "清除" + "全部清除" "关闭" "完成验证" "确认" @@ -242,6 +243,7 @@ "通话已开始" "你已拒接来电" "聊天记录备份" + "聊天" "代码块" "已复制到剪贴板" "版权" @@ -282,6 +284,7 @@ "文件已保存" "文件已保存到“下载”" "转发消息" + "转发到" "常用" "媒体组" @@ -292,6 +295,7 @@ "GIF" "群组通话进行中" + "房间" "图片" "回复 %1$s" "安装 APK" @@ -349,7 +353,7 @@ "正在准备…" "隐私政策" - "私密" + "私有" "私有房间" "私有空间" "公共" @@ -400,6 +404,7 @@ "已共享实时位置" "共享位置" "已共享的空间" + "已登录" "正在移除设备" "发生了一些错误" "我们遇到了一个问题。请重试。" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 102c627d90..0eeb366d72 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -247,6 +247,7 @@ "Call started" "You declined a call" "Chat backup" + "Chats" "Code block" "Copied to clipboard" "Copyright" @@ -287,6 +288,7 @@ Reason: %1$s." "File saved" "File saved to Downloads" "Forward message" + "Forward to" "Frequently used" "Gallery" @@ -299,6 +301,7 @@ Reason: %1$s." "GIF" "Group call in progress" + "Rooms" "Image" "In reply to %1$s" "Install APK" diff --git a/screenshots/html/data.js b/screenshots/html/data.js index 0daf273924..4ad747b07c 100644 --- a/screenshots/html/data.js +++ b/screenshots/html/data.js @@ -1,81 +1,85 @@ // Generated file, do not edit -export const buildDate = 1790556171; +export const buildDate = 1791160096; export const screenshots = [ ["en","en-dark","de",], -["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",20722,], -["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20722,], +["features.messages.impl.timeline.components.event_ATimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components.event_ATimelineItemEventRow_Night_0_en",20728,], +["features.preferences.impl.about_AboutView_Day_0_en","features.preferences.impl.about_AboutView_Night_0_en",20728,], ["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_0_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_0_en",0,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20722,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20722,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20722,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20722,], -["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20722,], -["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20722,], -["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20722,], -["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20722,], -["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20722,], -["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20722,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_1_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_1_en",20728,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_2_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_2_en",20728,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_3_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_3_en",20728,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_4_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_4_en",20728,], +["features.invite.impl.acceptdecline_AcceptDeclineInviteView_Day_5_en","features.invite.impl.acceptdecline_AcceptDeclineInviteView_Night_5_en",20728,], +["features.logout.impl_AccountDeactivationView_Day_0_en","features.logout.impl_AccountDeactivationView_Night_0_en",20728,], +["features.logout.impl_AccountDeactivationView_Day_1_en","features.logout.impl_AccountDeactivationView_Night_1_en",20728,], +["features.logout.impl_AccountDeactivationView_Day_2_en","features.logout.impl_AccountDeactivationView_Night_2_en",20728,], +["features.logout.impl_AccountDeactivationView_Day_3_en","features.logout.impl_AccountDeactivationView_Night_3_en",20728,], +["features.logout.impl_AccountDeactivationView_Day_4_en","features.logout.impl_AccountDeactivationView_Night_4_en",20728,], +["libraries.matrix.ui.components.accountinfo_AccountInfoCard_Day_0_en","libraries.matrix.ui.components.accountinfo_AccountInfoCard_Night_0_en",0,], +["libraries.matrix.ui.components.accountinfo_AccountInfoCard_Day_1_en","libraries.matrix.ui.components.accountinfo_AccountInfoCard_Night_1_en",0,], +["libraries.matrix.ui.components.accountinfo_AccountInfoCard_Day_2_en","libraries.matrix.ui.components.accountinfo_AccountInfoCard_Night_2_en",0,], +["libraries.matrix.ui.components.accountinfo_AccountInfoCard_Day_3_en","libraries.matrix.ui.components.accountinfo_AccountInfoCard_Night_3_en",0,], ["features.login.impl.accountprovider_AccountProviderView_Day_0_en","features.login.impl.accountprovider_AccountProviderView_Night_0_en",0,], -["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20722,], -["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20722,], +["libraries.accountselect.impl_AccountSelectView_Day_0_en","libraries.accountselect.impl_AccountSelectView_Night_0_en",20728,], +["libraries.accountselect.impl_AccountSelectView_Day_1_en","libraries.accountselect.impl_AccountSelectView_Night_1_en",20728,], ["features.messages.impl.actionlist_ActionListViewContent_Day_0_en","features.messages.impl.actionlist_ActionListViewContent_Night_0_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20722,], +["features.messages.impl.actionlist_ActionListViewContent_Day_10_en","features.messages.impl.actionlist_ActionListViewContent_Night_10_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_11_en","features.messages.impl.actionlist_ActionListViewContent_Night_11_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_12_en","features.messages.impl.actionlist_ActionListViewContent_Night_12_en",20728,], ["features.messages.impl.actionlist_ActionListViewContent_Day_1_en","features.messages.impl.actionlist_ActionListViewContent_Night_1_en",0,], -["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20722,], -["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20722,], -["features.messages.impl.timeline.components_ActiveCallTimelineItemView_Day_0_en","features.messages.impl.timeline.components_ActiveCallTimelineItemView_Night_0_en",20722,], -["features.messages.impl.timeline.components_ActiveCallTimelineItemView_en","",20722,], +["features.messages.impl.actionlist_ActionListViewContent_Day_2_en","features.messages.impl.actionlist_ActionListViewContent_Night_2_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_3_en","features.messages.impl.actionlist_ActionListViewContent_Night_3_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_4_en","features.messages.impl.actionlist_ActionListViewContent_Night_4_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_5_en","features.messages.impl.actionlist_ActionListViewContent_Night_5_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_6_en","features.messages.impl.actionlist_ActionListViewContent_Night_6_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_7_en","features.messages.impl.actionlist_ActionListViewContent_Night_7_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_8_en","features.messages.impl.actionlist_ActionListViewContent_Night_8_en",20728,], +["features.messages.impl.actionlist_ActionListViewContent_Day_9_en","features.messages.impl.actionlist_ActionListViewContent_Night_9_en",20728,], +["features.messages.impl.timeline.components_ActiveCallTimelineItemView_Day_0_en","features.messages.impl.timeline.components_ActiveCallTimelineItemView_Night_0_en",20728,], +["features.messages.impl.timeline.components_ActiveCallTimelineItemView_en","",20728,], ["libraries.designsystem.text_AdaptativeTitle_Day_0_en","libraries.designsystem.text_AdaptativeTitle_Night_0_en",0,], -["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20722,], -["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20722,], -["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20722,], -["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20722,], -["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20722,], -["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20722,], -["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20722,], +["features.createroom.impl.addpeople_AddPeopleView_Day_0_en","features.createroom.impl.addpeople_AddPeopleView_Night_0_en",20728,], +["features.createroom.impl.addpeople_AddPeopleView_Day_1_en","features.createroom.impl.addpeople_AddPeopleView_Night_1_en",20728,], +["features.createroom.impl.addpeople_AddPeopleView_Day_2_en","features.createroom.impl.addpeople_AddPeopleView_Night_2_en",20728,], +["features.createroom.impl.addpeople_AddPeopleView_Day_3_en","features.createroom.impl.addpeople_AddPeopleView_Night_3_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_0_en","features.space.impl.addroom_AddRoomToSpaceView_Night_0_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_1_en","features.space.impl.addroom_AddRoomToSpaceView_Night_1_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_2_en","features.space.impl.addroom_AddRoomToSpaceView_Night_2_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_3_en","features.space.impl.addroom_AddRoomToSpaceView_Night_3_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_4_en","features.space.impl.addroom_AddRoomToSpaceView_Night_4_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_5_en","features.space.impl.addroom_AddRoomToSpaceView_Night_5_en",20728,], +["features.space.impl.addroom_AddRoomToSpaceView_Day_6_en","features.space.impl.addroom_AddRoomToSpaceView_Night_6_en",20728,], +["libraries.designsystem.components.dialogs_AlertDialogContent_Dialogs_en","",20728,], +["libraries.designsystem.components.dialogs_AlertDialog_Day_0_en","libraries.designsystem.components.dialogs_AlertDialog_Night_0_en",20728,], ["libraries.designsystem.theme.components_AllIcons_Icons_en","",0,], -["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20722,], -["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20722,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20722,], -["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20722,], -["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20722,], +["features.analytics.impl_AnalyticsOptInView_Day_0_en","features.analytics.impl_AnalyticsOptInView_Night_0_en",20728,], +["features.analytics.impl_AnalyticsOptInView_Day_1_en","features.analytics.impl_AnalyticsOptInView_Night_1_en",20728,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_0_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_0_en",20728,], +["features.analytics.api.preferences_AnalyticsPreferencesView_Day_1_en","features.analytics.api.preferences_AnalyticsPreferencesView_Night_1_en",20728,], +["features.preferences.impl.analytics_AnalyticsSettingsView_Day_0_en","features.preferences.impl.analytics_AnalyticsSettingsView_Night_0_en",20728,], ["libraries.designsystem.components_Announcement_Day_0_en","libraries.designsystem.components_Announcement_Night_0_en",0,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20722,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20722,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20722,], -["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_2_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_2_en",20724,], -["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20722,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsPage_Night_0_en",20728,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_0_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_0_en",20728,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_1_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_1_en",20728,], +["features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Day_2_en","features.preferences.impl.developer.appsettings_AppDeveloperSettingsView_Night_2_en",20728,], +["services.apperror.api_AppErrorView_Day_0_en","services.apperror.api_AppErrorView_Night_0_en",20728,], ["libraries.designsystem.components.async_AsyncActionView_Day_0_en","libraries.designsystem.components.async_AsyncActionView_Night_0_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20722,], +["libraries.designsystem.components.async_AsyncActionView_Day_1_en","libraries.designsystem.components.async_AsyncActionView_Night_1_en",20728,], ["libraries.designsystem.components.async_AsyncActionView_Day_2_en","libraries.designsystem.components.async_AsyncActionView_Night_2_en",0,], -["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20722,], +["libraries.designsystem.components.async_AsyncActionView_Day_3_en","libraries.designsystem.components.async_AsyncActionView_Night_3_en",20728,], ["libraries.designsystem.components.async_AsyncActionView_Day_4_en","libraries.designsystem.components.async_AsyncActionView_Night_4_en",0,], -["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20722,], +["libraries.designsystem.components.async_AsyncFailure_Day_0_en","libraries.designsystem.components.async_AsyncFailure_Night_0_en",20728,], ["libraries.designsystem.components.async_AsyncIndicatorFailure_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorFailure_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncIndicatorLoading_Day_0_en","libraries.designsystem.components.async_AsyncIndicatorLoading_Night_0_en",0,], ["libraries.designsystem.components.async_AsyncLoading_Day_0_en","libraries.designsystem.components.async_AsyncLoading_Night_0_en",0,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en","",20722,], -["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en","",20722,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_0_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_1_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_2_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_3_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_4_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_5_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_6_en","",20728,], +["features.messages.impl.attachments.preview.imageeditor_AttachmentImageEditorView_7_en","",20728,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_0_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_0_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_1_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_1_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_2_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_2_en",0,], @@ -85,22 +89,22 @@ export const screenshots = [ ["libraries.matrix.ui.components_AttachmentThumbnail_Day_6_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_6_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_7_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_7_en",0,], ["libraries.matrix.ui.components_AttachmentThumbnail_Day_8_en","libraries.matrix.ui.components_AttachmentThumbnail_Night_8_en",0,], -["features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_en","features.messages.impl.messagecomposer_AttachmentsBottomSheet_Night_0_en",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewGalleryView_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20722,], -["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20722,], -["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",20722,], -["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20722,], +["features.messages.impl.messagecomposer_AttachmentsBottomSheet_Day_0_en","features.messages.impl.messagecomposer_AttachmentsBottomSheet_Night_0_en",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewGalleryView_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_0_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_1_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_2_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_3_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_4_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_5_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_6_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_7_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_8_en","",20728,], +["features.messages.impl.attachments.preview_AttachmentsPreviewView_9_en","",20728,], +["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_0_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_1_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_AudioItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_AudioItemView_Night_2_en",20728,], +["libraries.matrix.ui.components_AvatarActionBottomSheet_Day_0_en","libraries.matrix.ui.components_AvatarActionBottomSheet_Night_0_en",20728,], ["libraries.designsystem.components.avatar.internal_AvatarCluster_Avatars_en","",0,], ["libraries.matrix.ui.components_AvatarPickerSizes_Day_0_en","libraries.matrix.ui.components_AvatarPickerSizes_Night_0_en",0,], ["libraries.matrix.ui.components_AvatarPickerViewRtl_Day_0_en","libraries.matrix.ui.components_AvatarPickerViewRtl_Night_0_en",0,], @@ -130,22 +134,22 @@ export const screenshots = [ ["libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradientDisabled_Night_0_en",0,], ["libraries.designsystem.modifiers_BackgroundVerticalGradient_Day_0_en","libraries.designsystem.modifiers_BackgroundVerticalGradient_Night_0_en",0,], ["libraries.designsystem.components_Badge_Day_0_en","libraries.designsystem.components_Badge_Night_0_en",0,], -["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20722,], +["features.home.impl.components_BatteryOptimizationBanner_Day_0_en","features.home.impl.components_BatteryOptimizationBanner_Night_0_en",20728,], ["libraries.designsystem.atomic.atoms_BetaLabel_Day_0_en","libraries.designsystem.atomic.atoms_BetaLabel_Night_0_en",0,], ["libraries.designsystem.components_BigIcon_Day_0_en","libraries.designsystem.components_BigIcon_Night_0_en",0,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20722,], -["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20722,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_0_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_0_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_1_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_1_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_2_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_2_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_3_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_3_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_4_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_4_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_5_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_5_en",20728,], +["features.preferences.impl.blockedusers_BlockedUsersView_Day_6_en","features.preferences.impl.blockedusers_BlockedUsersView_Night_6_en",20728,], ["libraries.designsystem.theme.components_BottomSheetDragHandle_Day_0_en","libraries.designsystem.theme.components_BottomSheetDragHandle_Night_0_en",0,], -["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20722,], -["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20722,], -["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20722,], -["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20722,], -["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20722,], +["features.rageshake.impl.bugreport_BugReportViewDay_0_en","",20728,], +["features.rageshake.impl.bugreport_BugReportViewDay_1_en","",20728,], +["features.rageshake.impl.bugreport_BugReportViewDay_2_en","",20728,], +["features.rageshake.impl.bugreport_BugReportViewDay_3_en","",20728,], +["features.rageshake.impl.bugreport_BugReportViewDay_4_en","",20728,], ["features.rageshake.impl.bugreport_BugReportViewNight_0_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_1_en","",0,], ["features.rageshake.impl.bugreport_BugReportViewNight_2_en","",0,], @@ -156,140 +160,140 @@ export const screenshots = [ ["features.messages.impl.timeline.components_CallMenuItem_Day_0_en","features.messages.impl.timeline.components_CallMenuItem_Night_0_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_1_en","features.messages.impl.timeline.components_CallMenuItem_Night_1_en",0,], ["features.messages.impl.timeline.components_CallMenuItem_Day_2_en","features.messages.impl.timeline.components_CallMenuItem_Night_2_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20722,], -["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20722,], +["features.messages.impl.timeline.components_CallMenuItem_Day_3_en","features.messages.impl.timeline.components_CallMenuItem_Night_3_en",20728,], +["features.messages.impl.timeline.components_CallMenuItem_Day_4_en","features.messages.impl.timeline.components_CallMenuItem_Night_4_en",20728,], ["features.messages.impl.timeline.components_CallMenuItem_Day_5_en","features.messages.impl.timeline.components_CallMenuItem_Night_5_en",0,], -["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20722,], +["features.messages.impl.timeline.components_CallMenuItem_Day_6_en","features.messages.impl.timeline.components_CallMenuItem_Night_6_en",20728,], ["features.messages.impl.timeline.components_CallMenuItem_Day_7_en","features.messages.impl.timeline.components_CallMenuItem_Night_7_en",0,], ["features.call.impl.ui_CallScreenView_Day_0_en","features.call.impl.ui_CallScreenView_Night_0_en",0,], -["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20722,], -["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20722,], -["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20722,], -["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20722,], +["features.call.impl.ui_CallScreenView_Day_1_en","features.call.impl.ui_CallScreenView_Night_1_en",20728,], +["features.call.impl.ui_CallScreenView_Day_2_en","features.call.impl.ui_CallScreenView_Night_2_en",20728,], +["features.call.impl.ui_CallScreenView_Day_3_en","features.call.impl.ui_CallScreenView_Night_3_en",20728,], +["libraries.textcomposer_CaptionWarningBottomSheet_Day_0_en","libraries.textcomposer_CaptionWarningBottomSheet_Night_0_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_0_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_0_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_10_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_10_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_11_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_11_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_12_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_12_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_13_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_13_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_1_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_1_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_2_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_2_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_3_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_3_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_4_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_4_en",20728,], ["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_5_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_5_en",0,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20722,], -["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20722,], -["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20722,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_6_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_6_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_7_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_7_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_8_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_8_en",20728,], +["features.rolesandpermissions.impl.roles_ChangeRolesView_Day_9_en","features.rolesandpermissions.impl.roles_ChangeRolesView_Night_9_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_0_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_0_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_1_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_1_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_2_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_2_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_3_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_3_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_4_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_4_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_5_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_5_en",20728,], +["features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Day_6_en","features.rolesandpermissions.impl.permissions_ChangeRoomPermissionsView_Night_6_en",20728,], ["features.login.impl.changeserver_ChangeServerView_Day_0_en","features.login.impl.changeserver_ChangeServerView_Night_0_en",0,], -["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20722,], -["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20722,], -["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20722,], -["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20722,], -["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20722,], -["features.login.impl.changeserver_ChangeServerView_Day_6_en","features.login.impl.changeserver_ChangeServerView_Night_6_en",20722,], +["features.login.impl.changeserver_ChangeServerView_Day_1_en","features.login.impl.changeserver_ChangeServerView_Night_1_en",20728,], +["features.login.impl.changeserver_ChangeServerView_Day_2_en","features.login.impl.changeserver_ChangeServerView_Night_2_en",20728,], +["features.login.impl.changeserver_ChangeServerView_Day_3_en","features.login.impl.changeserver_ChangeServerView_Night_3_en",20728,], +["features.login.impl.changeserver_ChangeServerView_Day_4_en","features.login.impl.changeserver_ChangeServerView_Night_4_en",20728,], +["features.login.impl.changeserver_ChangeServerView_Day_5_en","features.login.impl.changeserver_ChangeServerView_Night_5_en",20728,], +["features.login.impl.changeserver_ChangeServerView_Day_6_en","features.login.impl.changeserver_ChangeServerView_Night_6_en",20728,], ["libraries.matrix.ui.components_CheckableResolvedUserRow_en","",0,], -["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20722,], +["libraries.matrix.ui.components_CheckableUnresolvedUserRow_en","",20728,], ["libraries.designsystem.theme.components_Checkboxes_Toggles_en","",0,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20722,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20722,], -["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20722,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20722,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20722,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20722,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20722,], -["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20722,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_0_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_0_en",20728,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_1_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_1_en",20728,], +["features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Day_2_en","features.login.impl.screens.chooseaccountprovider_ChooseAccountProviderView_Night_2_en",20728,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_0_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_0_en",20728,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_1_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_1_en",20728,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_2_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_2_en",20728,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_3_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_3_en",20728,], +["features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Day_4_en","features.ftue.impl.sessionverification.choosemode_ChooseSelfVerificationModeView_Night_4_en",20728,], ["libraries.designsystem.theme.components_CircularProgressIndicator_Progress_Indicators_en","",0,], ["libraries.designsystem.components_ClickableLinkText_Text_en","",0,], -["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20722,], +["features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Day_0_en","features.linknewdevice.impl.screens.confirmation_CodeConfirmationView_Night_0_en",20728,], ["libraries.designsystem.theme_ColorAliases_Day_0_en","libraries.designsystem.theme_ColorAliases_Night_0_en",0,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20722,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20722,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20722,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20722,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20722,], -["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20722,], -["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20722,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_0_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_0_en",20728,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_1_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_1_en",20728,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_2_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_2_en",20728,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_3_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_3_en",20728,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_4_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_4_en",20728,], +["libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Day_5_en","libraries.designsystem.atomic.molecules_ComposerAlertMolecule_Night_5_en",20728,], +["libraries.textcomposer_ComposerModeView_Day_0_en","libraries.textcomposer_ComposerModeView_Night_0_en",20728,], ["libraries.textcomposer_ComposerModeView_Day_1_en","libraries.textcomposer_ComposerModeView_Night_1_en",0,], ["libraries.textcomposer_ComposerModeView_Day_2_en","libraries.textcomposer_ComposerModeView_Night_2_en",0,], ["libraries.textcomposer_ComposerModeView_Day_3_en","libraries.textcomposer_ComposerModeView_Night_3_en",0,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20722,], -["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20722,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20722,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20722,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20722,], -["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_3_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_3_en",20722,], -["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20722,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_0_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_1_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_2_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_3_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_4_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_5_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_6_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_7_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewDark_8_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_0_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_1_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_2_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_3_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_4_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_5_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_6_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_7_en","",20728,], +["features.createroom.impl.configureroom_ConfigureRoomViewLight_8_en","",20728,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_0_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_0_en",20728,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_1_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_1_en",20728,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_2_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_2_en",20728,], +["features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Day_3_en","features.login.impl.screens.confirmaccountprovider_ConfirmAccountProviderView_Night_3_en",20728,], +["features.home.impl.components_ConfirmRecoveryKeyBanner_Day_0_en","features.home.impl.components_ConfirmRecoveryKeyBanner_Night_0_en",20728,], ["libraries.designsystem.components.dialogs_ConfirmationDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ConfirmationDialog_Day_0_en","libraries.designsystem.components.dialogs_ConfirmationDialog_Night_0_en",0,], ["features.networkmonitor.api.ui_ConnectivityIndicator_Day_0_en","features.networkmonitor.api.ui_ConnectivityIndicator_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CopiableTextAtom_Day_0_en","libraries.designsystem.atomic.atoms_CopiableTextAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_CounterAtom_Day_0_en","libraries.designsystem.atomic.atoms_CounterAtom_Night_0_en",0,], -["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20722,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20722,], -["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20722,], -["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20722,], -["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20722,], -["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20722,], -["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20722,], -["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20722,], -["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20722,], -["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20722,], -["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20722,], -["features.poll.impl.create_CreatePollView_Day_8_en","features.poll.impl.create_CreatePollView_Night_8_en",20722,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20722,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20722,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20722,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20722,], -["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20722,], +["features.rageshake.api.crash_CrashDetectionView_Day_0_en","features.rageshake.api.crash_CrashDetectionView_Night_0_en",20728,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_0_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_0_en",20728,], +["libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Day_1_en","libraries.matrix.ui.components_CreateDmConfirmationBottomSheet_Night_1_en",20728,], +["features.poll.impl.create_CreatePollView_Day_0_en","features.poll.impl.create_CreatePollView_Night_0_en",20728,], +["features.poll.impl.create_CreatePollView_Day_1_en","features.poll.impl.create_CreatePollView_Night_1_en",20728,], +["features.poll.impl.create_CreatePollView_Day_2_en","features.poll.impl.create_CreatePollView_Night_2_en",20728,], +["features.poll.impl.create_CreatePollView_Day_3_en","features.poll.impl.create_CreatePollView_Night_3_en",20728,], +["features.poll.impl.create_CreatePollView_Day_4_en","features.poll.impl.create_CreatePollView_Night_4_en",20728,], +["features.poll.impl.create_CreatePollView_Day_5_en","features.poll.impl.create_CreatePollView_Night_5_en",20728,], +["features.poll.impl.create_CreatePollView_Day_6_en","features.poll.impl.create_CreatePollView_Night_6_en",20728,], +["features.poll.impl.create_CreatePollView_Day_7_en","features.poll.impl.create_CreatePollView_Night_7_en",20728,], +["features.poll.impl.create_CreatePollView_Day_8_en","features.poll.impl.create_CreatePollView_Night_8_en",20728,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_0_en","",20728,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_1_en","",20728,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_2_en","",20728,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_3_en","",20728,], +["libraries.dateformatter.impl.previews_DateFormatterModeView_4_en","",20728,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_DateItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_DateItemView_Night_1_en",0,], -["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20722,], -["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20722,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20722,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20722,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20722,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20722,], -["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20722,], +["libraries.designsystem.theme.components.previews_DatePickerDark_DateTime_pickers_en","",20728,], +["libraries.designsystem.theme.components.previews_DatePickerLight_DateTime_pickers_en","",20728,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_0_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_0_en",20728,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_1_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_1_en",20728,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_2_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_2_en",20728,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_3_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_3_en",20728,], +["features.invite.impl.declineandblock_DeclineAndBlockView_Day_4_en","features.invite.impl.declineandblock_DeclineAndBlockView_Night_4_en",20728,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_0_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_0_en",0,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20722,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20722,], -["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20722,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_1_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_1_en",20728,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_2_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_2_en",20728,], +["features.logout.impl.direct_DefaultDirectLogoutView_Day_3_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_3_en",20728,], ["features.logout.impl.direct_DefaultDirectLogoutView_Day_4_en","features.logout.impl.direct_DefaultDirectLogoutView_Night_4_en",0,], -["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20722,], +["features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Day_0_en","features.preferences.impl.notifications.edit_DefaultNotificationSettingOption_Night_0_en",20728,], ["features.licenses.impl.details_DependenciesDetailsView_Day_0_en","features.licenses.impl.details_DependenciesDetailsView_Night_0_en",0,], -["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20722,], -["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20722,], -["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20722,], -["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20722,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20722,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20722,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20722,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20722,], -["features.preferences.impl.developer_DeveloperSettingsView_Day_3_en","features.preferences.impl.developer_DeveloperSettingsView_Night_3_en",20722,], +["features.licenses.impl.list_DependencyLicensesListView_Day_0_en","features.licenses.impl.list_DependencyLicensesListView_Night_0_en",20728,], +["features.licenses.impl.list_DependencyLicensesListView_Day_1_en","features.licenses.impl.list_DependencyLicensesListView_Night_1_en",20728,], +["features.licenses.impl.list_DependencyLicensesListView_Day_2_en","features.licenses.impl.list_DependencyLicensesListView_Night_2_en",20728,], +["features.licenses.impl.list_DependencyLicensesListView_Day_3_en","features.licenses.impl.list_DependencyLicensesListView_Night_3_en",20728,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_0_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Day_1_en","features.linknewdevice.impl.screens.desktop_DesktopNoticeView_Night_1_en",20728,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_0_en","features.preferences.impl.developer_DeveloperSettingsView_Night_0_en",20728,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_1_en","features.preferences.impl.developer_DeveloperSettingsView_Night_1_en",20728,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_2_en","features.preferences.impl.developer_DeveloperSettingsView_Night_2_en",20728,], +["features.preferences.impl.developer_DeveloperSettingsView_Day_3_en","features.preferences.impl.developer_DeveloperSettingsView_Night_3_en",20728,], ["libraries.designsystem.theme.components_DialogWithDestructiveButton_Dialog_with_destructive_button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithOnlyMessageAndOkButton_Dialog_with_only_message_and_ok_button_Dialogs_en","",0,], ["libraries.designsystem.theme.components_DialogWithThirdButton_Dialog_with_third_button_Dialogs_en","",0,], @@ -302,20 +306,20 @@ export const screenshots = [ ["libraries.designsystem.text_DpScale_1_0f__en","",0,], ["libraries.designsystem.text_DpScale_1_5f__en","",0,], ["libraries.designsystem.theme.components_DropdownMenuItem_Menus_en","",0,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20722,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20722,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20722,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20722,], -["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20722,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20722,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20722,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20722,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20722,], -["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20722,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20722,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20722,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20722,], -["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20722,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_0_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_0_en",20728,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_1_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_1_en",20728,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_2_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_2_en",20728,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_3_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_3_en",20728,], +["features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Day_4_en","features.preferences.impl.notifications.edit_EditDefaultNotificationSettingView_Night_4_en",20728,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_0_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_0_en",20728,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_1_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_1_en",20728,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_2_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_2_en",20728,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_3_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_3_en",20728,], +["features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Day_4_en","features.securityandprivacy.impl.editroomaddress_EditRoomAddressView_Night_4_en",20728,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_0_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_0_en",20728,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_1_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_1_en",20728,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_2_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_2_en",20728,], +["features.preferences.impl.user.editprofile_EditUserProfileView_Day_3_en","features.preferences.impl.user.editprofile_EditUserProfileView_Night_3_en",20728,], ["libraries.matrix.ui.components_EditableOrgAvatarRtl_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatarRtl_Night_0_en",0,], ["libraries.matrix.ui.components_EditableOrgAvatar_Day_0_en","libraries.matrix.ui.components_EditableOrgAvatar_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomLargeNoBlurShadow_Night_0_en",0,], @@ -324,36 +328,36 @@ export const screenshots = [ ["libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Day_0_en","libraries.designsystem.atomic.atoms_ElementLogoAtomMedium_Night_0_en",0,], ["libraries.emoji.impl.picker_EmojiItemWithPopup_Day_0_en","libraries.emoji.impl.picker_EmojiItemWithPopup_Night_0_en",0,], ["libraries.emoji.impl.picker_EmojiItem_Day_0_en","libraries.emoji.impl.picker_EmojiItem_Night_0_en",0,], -["libraries.emoji.impl.picker_EmojiPickerView_Day_0_en","libraries.emoji.impl.picker_EmojiPickerView_Night_0_en",20722,], -["libraries.emoji.impl.picker_EmojiPickerView_Day_1_en","libraries.emoji.impl.picker_EmojiPickerView_Night_1_en",20722,], +["libraries.emoji.impl.picker_EmojiPickerView_Day_0_en","libraries.emoji.impl.picker_EmojiPickerView_Night_0_en",20728,], +["libraries.emoji.impl.picker_EmojiPickerView_Day_1_en","libraries.emoji.impl.picker_EmojiPickerView_Night_1_en",20728,], ["libraries.emoji.impl.picker_EmojiPickerView_Day_2_en","libraries.emoji.impl.picker_EmojiPickerView_Night_2_en",0,], ["libraries.emoji.impl.picker_EmojiPickerView_Day_3_en","libraries.emoji.impl.picker_EmojiPickerView_Night_3_en",0,], ["libraries.ui.common.nodes_EmptyView_Day_0_en","libraries.ui.common.nodes_EmptyView_Night_0_en",0,], -["libraries.ui.common.nodes_EmptyView_Day_1_en","libraries.ui.common.nodes_EmptyView_Night_1_en",20724,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20722,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20722,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20722,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20722,], -["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20722,], -["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20722,], -["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20722,], -["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20722,], -["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20722,], +["libraries.ui.common.nodes_EmptyView_Day_1_en","libraries.ui.common.nodes_EmptyView_Night_1_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_0_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_1_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_1_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_2_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_2_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_3_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_3_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_4_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_4_en",20728,], +["features.linknewdevice.impl.screens.number_EnterNumberView_Day_5_en","features.linknewdevice.impl.screens.number_EnterNumberView_Night_5_en",20728,], +["libraries.designsystem.components.dialogs_ErrorDialogContent_Dialogs_en","",20728,], +["libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialogWithDoNotShowAgain_Night_0_en",20728,], +["libraries.designsystem.components.dialogs_ErrorDialog_Day_0_en","libraries.designsystem.components.dialogs_ErrorDialog_Night_0_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_0_en","features.linknewdevice.impl.screens.error_ErrorView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_1_en","features.linknewdevice.impl.screens.error_ErrorView_Night_1_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_2_en","features.linknewdevice.impl.screens.error_ErrorView_Night_2_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_3_en","features.linknewdevice.impl.screens.error_ErrorView_Night_3_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_4_en","features.linknewdevice.impl.screens.error_ErrorView_Night_4_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_5_en","features.linknewdevice.impl.screens.error_ErrorView_Night_5_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_6_en","features.linknewdevice.impl.screens.error_ErrorView_Night_6_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_7_en","features.linknewdevice.impl.screens.error_ErrorView_Night_7_en",20728,], +["features.linknewdevice.impl.screens.error_ErrorView_Day_8_en","features.linknewdevice.impl.screens.error_ErrorView_Night_8_en",20728,], ["features.messages.impl.timeline.debug_EventDebugInfoView_Day_0_en","features.messages.impl.timeline.debug_EventDebugInfoView_Night_0_en",0,], ["libraries.designsystem.components_ExpandableBottomSheetLayout_en","",0,], ["libraries.featureflag.ui_FeatureListView_Day_0_en","libraries.featureflag.ui_FeatureListView_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_0_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_1_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_2_en",20722,], +["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_0_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_1_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_FileItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_FileItemView_Night_2_en",20728,], ["libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_0_en","libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_0_en",0,], ["libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_1_en","libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_1_en",0,], ["libraries.mediaviewer.impl.fileviewer_FileViewerView_Day_2_en","libraries.mediaviewer.impl.fileviewer_FileViewerView_Night_2_en",0,], @@ -370,18 +374,18 @@ export const screenshots = [ ["features.messages.impl.timeline.components_FloatingDateBadge_Day_0_en","features.messages.impl.timeline.components_FloatingDateBadge_Night_0_en",0,], ["libraries.designsystem.atomic.pages_FlowStepPage_Day_0_en","libraries.designsystem.atomic.pages_FlowStepPage_Night_0_en",0,], ["features.messages.impl.timeline.focus_FocusRequestStateView_Day_0_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_0_en",0,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20722,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20722,], -["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20722,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_1_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_1_en",20728,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_2_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_2_en",20728,], +["features.messages.impl.timeline.focus_FocusRequestStateView_Day_3_en","features.messages.impl.timeline.focus_FocusRequestStateView_Night_3_en",20728,], ["features.messages.impl.timeline.components_FocusedEvent_Day_0_en","features.messages.impl.timeline.components_FocusedEvent_Night_0_en",0,], ["libraries.textcomposer.components_FormattingOption_Day_0_en","libraries.textcomposer.components_FormattingOption_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_0_en","features.forward.impl_ForwardMessagesView_Night_0_en",0,], ["features.forward.impl_ForwardMessagesView_Day_1_en","features.forward.impl_ForwardMessagesView_Night_1_en",0,], ["features.forward.impl_ForwardMessagesView_Day_2_en","features.forward.impl_ForwardMessagesView_Night_2_en",0,], -["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20722,], -["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20722,], +["features.forward.impl_ForwardMessagesView_Day_3_en","features.forward.impl_ForwardMessagesView_Night_3_en",20728,], +["features.home.impl.components_FullScreenIntentPermissionBanner_Day_0_en","features.home.impl.components_FullScreenIntentPermissionBanner_Night_0_en",20728,], ["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_0_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_0_en",0,], -["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20722,], +["features.announcement.impl.fullscreen_FullscreenAnnouncementView_Day_1_en","features.announcement.impl.fullscreen_FullscreenAnnouncementView_Night_1_en",20728,], ["features.home.impl.search_GlobalSearchView_Day_0_en","features.home.impl.search_GlobalSearchView_Night_0_en",0,], ["features.home.impl.search_GlobalSearchView_Day_1_en","features.home.impl.search_GlobalSearchView_Night_1_en",0,], ["features.home.impl.search_GlobalSearchView_Day_2_en","features.home.impl.search_GlobalSearchView_Night_2_en",0,], @@ -394,36 +398,36 @@ export const screenshots = [ ["features.messages.impl.timeline.components.group_GroupHeaderView_Day_0_en","features.messages.impl.timeline.components.group_GroupHeaderView_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPageScrollable_Night_0_en",0,], ["libraries.designsystem.atomic.pages_HeaderFooterPage_Day_0_en","libraries.designsystem.atomic.pages_HeaderFooterPage_Night_0_en",0,], -["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20722,], -["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20722,], -["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20722,], -["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20722,], -["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20722,], +["features.home.impl.spaces_HomeSpacesView_Day_0_en","features.home.impl.spaces_HomeSpacesView_Night_0_en",20728,], +["features.home.impl.spaces_HomeSpacesView_Day_1_en","features.home.impl.spaces_HomeSpacesView_Night_1_en",20728,], +["features.home.impl.spaces_HomeSpacesView_Day_2_en","features.home.impl.spaces_HomeSpacesView_Night_2_en",20728,], +["features.home.impl.components_HomeTopBarMultiAccount_Day_0_en","features.home.impl.components_HomeTopBarMultiAccount_Night_0_en",20728,], +["features.home.impl.components_HomeTopBarSpaceFiltersSelected_Day_0_en","features.home.impl.components_HomeTopBarSpaceFiltersSelected_Night_0_en",20728,], ["features.home.impl.components_HomeTopBarSpaces_Day_0_en","features.home.impl.components_HomeTopBarSpaces_Night_0_en",0,], -["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20722,], -["features.home.impl.components_HomeTopBarWithStatus_Day_0_en","features.home.impl.components_HomeTopBarWithStatus_Night_0_en",20722,], -["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20722,], +["features.home.impl.components_HomeTopBarWithIndicator_Day_0_en","features.home.impl.components_HomeTopBarWithIndicator_Night_0_en",20728,], +["features.home.impl.components_HomeTopBarWithStatus_Day_0_en","features.home.impl.components_HomeTopBarWithStatus_Night_0_en",20728,], +["features.home.impl.components_HomeTopBar_Day_0_en","features.home.impl.components_HomeTopBar_Night_0_en",20728,], ["features.home.impl_HomeViewA11y_en","",0,], -["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20722,], -["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20722,], +["features.home.impl_HomeView_Day_0_en","features.home.impl_HomeView_Night_0_en",20728,], +["features.home.impl_HomeView_Day_10_en","features.home.impl_HomeView_Night_10_en",20728,], ["features.home.impl_HomeView_Day_11_en","features.home.impl_HomeView_Night_11_en",0,], ["features.home.impl_HomeView_Day_12_en","features.home.impl_HomeView_Night_12_en",0,], -["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20722,], -["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20722,], -["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20722,], -["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20722,], -["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20722,], -["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20722,], -["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20722,], -["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20722,], -["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20722,], -["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20722,], -["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20722,], -["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20722,], -["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20722,], +["features.home.impl_HomeView_Day_13_en","features.home.impl_HomeView_Night_13_en",20728,], +["features.home.impl_HomeView_Day_14_en","features.home.impl_HomeView_Night_14_en",20728,], +["features.home.impl_HomeView_Day_15_en","features.home.impl_HomeView_Night_15_en",20728,], +["features.home.impl_HomeView_Day_16_en","features.home.impl_HomeView_Night_16_en",20728,], +["features.home.impl_HomeView_Day_1_en","features.home.impl_HomeView_Night_1_en",20728,], +["features.home.impl_HomeView_Day_2_en","features.home.impl_HomeView_Night_2_en",20728,], +["features.home.impl_HomeView_Day_3_en","features.home.impl_HomeView_Night_3_en",20728,], +["features.home.impl_HomeView_Day_4_en","features.home.impl_HomeView_Night_4_en",20728,], +["features.home.impl_HomeView_Day_5_en","features.home.impl_HomeView_Night_5_en",20728,], +["features.home.impl_HomeView_Day_6_en","features.home.impl_HomeView_Night_6_en",20728,], +["features.home.impl_HomeView_Day_7_en","features.home.impl_HomeView_Night_7_en",20728,], +["features.home.impl_HomeView_Day_8_en","features.home.impl_HomeView_Night_8_en",20728,], +["features.home.impl_HomeView_Day_9_en","features.home.impl_HomeView_Night_9_en",20728,], ["libraries.designsystem.theme.components_HorizontalDivider_Dividers_en","",0,], -["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",20722,], -["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",20722,], +["libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbarNoFab_Night_0_en",20728,], +["libraries.designsystem.theme.components_HorizontalFloatingToolbar_Day_0_en","libraries.designsystem.theme.components_HorizontalFloatingToolbar_Night_0_en",20728,], ["libraries.designsystem.ruler_HorizontalRuler_Day_0_en","libraries.designsystem.ruler_HorizontalRuler_Night_0_en",0,], ["libraries.htmlrenderer.api.ui_HtmlMessageContent_Day_0_en","libraries.htmlrenderer.api.ui_HtmlMessageContent_Night_0_en",0,], ["libraries.htmlrenderer.api.ui_HtmlMessageContent_Day_10_en","libraries.htmlrenderer.api.ui_HtmlMessageContent_Night_10_en",0,], @@ -448,8 +452,8 @@ export const screenshots = [ ["appicon.enterprise_Icon_en","",0,], ["libraries.designsystem.icons_IconsOther_Day_0_en","libraries.designsystem.icons_IconsOther_Night_0_en",0,], ["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_0_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_0_en",0,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20722,], -["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20722,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_1_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_1_en",20728,], +["features.messages.impl.crypto.identity_IdentityChangeStateView_Day_2_en","features.messages.impl.crypto.identity_IdentityChangeStateView_Night_2_en",20728,], ["libraries.mediaviewer.impl.gallery.ui_ImageItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_ImageItemView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_0_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_0_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_10_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_10_en",0,], @@ -457,118 +461,118 @@ export const screenshots = [ ["libraries.matrix.ui.messages.reply_InReplyToView_Day_1_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_1_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_2_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_2_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_3_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_3_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20722,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_4_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_4_en",20728,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_5_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_5_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_6_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_6_en",0,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_7_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_7_en",0,], -["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20722,], +["libraries.matrix.ui.messages.reply_InReplyToView_Day_8_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_8_en",20728,], ["libraries.matrix.ui.messages.reply_InReplyToView_Day_9_en","libraries.matrix.ui.messages.reply_InReplyToView_Night_9_en",0,], -["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20722,], -["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20722,], +["features.call.impl.ui_IncomingCallScreen_Day_0_en","features.call.impl.ui_IncomingCallScreen_Night_0_en",20728,], +["features.call.impl.ui_IncomingCallScreen_Day_1_en","features.call.impl.ui_IncomingCallScreen_Night_1_en",20728,], ["features.verifysession.impl.incoming_IncomingVerificationViewA11y_en","",0,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20722,], -["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20722,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_0_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_0_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_10_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_10_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_11_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_11_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_12_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_12_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_13_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_13_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_1_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_1_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_2_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_2_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_3_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_3_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_4_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_4_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_5_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_5_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_6_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_6_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_7_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_7_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_8_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_8_en",20728,], +["features.verifysession.impl.incoming_IncomingVerificationView_Day_9_en","features.verifysession.impl.incoming_IncomingVerificationView_Night_9_en",20728,], ["libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en","libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en",0,], -["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20722,], -["libraries.matrix.ui.media.contentvalidation_InvalidContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_InvalidContentView_Night_0_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20722,], +["features.call.impl.ui_InvalidAudioDeviceDialog_Day_0_en","features.call.impl.ui_InvalidAudioDeviceDialog_Night_0_en",20728,], +["libraries.matrix.ui.media.contentvalidation_InvalidContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_InvalidContentView_Night_0_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_0_en","features.invitepeople.impl_InvitePeopleView_Night_0_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_10_en","features.invitepeople.impl_InvitePeopleView_Night_10_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_11_en","features.invitepeople.impl_InvitePeopleView_Night_11_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_1_en","features.invitepeople.impl_InvitePeopleView_Night_1_en",20728,], ["features.invitepeople.impl_InvitePeopleView_Day_2_en","features.invitepeople.impl_InvitePeopleView_Night_2_en",0,], ["features.invitepeople.impl_InvitePeopleView_Day_3_en","features.invitepeople.impl_InvitePeopleView_Night_3_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20722,], -["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20722,], +["features.invitepeople.impl_InvitePeopleView_Day_4_en","features.invitepeople.impl_InvitePeopleView_Night_4_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_5_en","features.invitepeople.impl_InvitePeopleView_Night_5_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_6_en","features.invitepeople.impl_InvitePeopleView_Night_6_en",20728,], +["features.invitepeople.impl_InvitePeopleView_Day_7_en","features.invitepeople.impl_InvitePeopleView_Night_7_en",20728,], ["features.invitepeople.impl_InvitePeopleView_Day_8_en","features.invitepeople.impl_InvitePeopleView_Night_8_en",0,], -["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20722,], -["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20722,], -["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20722,], +["features.invitepeople.impl_InvitePeopleView_Day_9_en","features.invitepeople.impl_InvitePeopleView_Night_9_en",20728,], +["libraries.matrix.ui.components_InviteSenderView_Day_0_en","libraries.matrix.ui.components_InviteSenderView_Night_0_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_0_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_0_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_1_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_1_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_2_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_2_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_3_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_3_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_4_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_4_en",20728,], +["features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Day_5_en","features.startchat.impl.joinbyaddress_JoinRoomByAddressView_Night_5_en",20728,], ["features.joinroom.impl_JoinRoomView_Day_0_en","features.joinroom.impl_JoinRoomView_Night_0_en",0,], -["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20722,], -["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20722,], -["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20722,], -["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20722,], +["features.joinroom.impl_JoinRoomView_Day_10_en","features.joinroom.impl_JoinRoomView_Night_10_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_11_en","features.joinroom.impl_JoinRoomView_Night_11_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_12_en","features.joinroom.impl_JoinRoomView_Night_12_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_13_en","features.joinroom.impl_JoinRoomView_Night_13_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_14_en","features.joinroom.impl_JoinRoomView_Night_14_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_15_en","features.joinroom.impl_JoinRoomView_Night_15_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_16_en","features.joinroom.impl_JoinRoomView_Night_16_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_1_en","features.joinroom.impl_JoinRoomView_Night_1_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_2_en","features.joinroom.impl_JoinRoomView_Night_2_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_3_en","features.joinroom.impl_JoinRoomView_Night_3_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_4_en","features.joinroom.impl_JoinRoomView_Night_4_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_5_en","features.joinroom.impl_JoinRoomView_Night_5_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_6_en","features.joinroom.impl_JoinRoomView_Night_6_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_7_en","features.joinroom.impl_JoinRoomView_Night_7_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_8_en","features.joinroom.impl_JoinRoomView_Night_8_en",20728,], +["features.joinroom.impl_JoinRoomView_Day_9_en","features.joinroom.impl_JoinRoomView_Night_9_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_0_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_0_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_1_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_1_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_2_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_2_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_3_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_3_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_4_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_4_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_5_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_5_en",20728,], +["features.knockrequests.impl.banner_KnockRequestsBannerView_Day_6_en","features.knockrequests.impl.banner_KnockRequestsBannerView_Night_6_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_0_en","features.knockrequests.impl.list_KnockRequestsListView_Night_0_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_10_en","features.knockrequests.impl.list_KnockRequestsListView_Night_10_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_1_en","features.knockrequests.impl.list_KnockRequestsListView_Night_1_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_2_en","features.knockrequests.impl.list_KnockRequestsListView_Night_2_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_3_en","features.knockrequests.impl.list_KnockRequestsListView_Night_3_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_4_en","features.knockrequests.impl.list_KnockRequestsListView_Night_4_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_5_en","features.knockrequests.impl.list_KnockRequestsListView_Night_5_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_6_en","features.knockrequests.impl.list_KnockRequestsListView_Night_6_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_7_en","features.knockrequests.impl.list_KnockRequestsListView_Night_7_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_8_en","features.knockrequests.impl.list_KnockRequestsListView_Night_8_en",20728,], +["features.knockrequests.impl.list_KnockRequestsListView_Day_9_en","features.knockrequests.impl.list_KnockRequestsListView_Night_9_en",20728,], ["libraries.designsystem.components_LabelledCheckbox_Toggles_en","",0,], -["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20722,], -["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20722,], +["features.preferences.impl.labs_LabsView_Day_0_en","features.preferences.impl.labs_LabsView_Night_0_en",20728,], +["features.preferences.impl.labs_LabsView_Day_1_en","features.preferences.impl.labs_LabsView_Night_1_en",20728,], ["features.leaveroom.impl_LeaveRoomView_Day_0_en","features.leaveroom.impl_LeaveRoomView_Night_0_en",0,], -["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20722,], -["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20722,], -["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20722,], +["features.leaveroom.impl_LeaveRoomView_Day_1_en","features.leaveroom.impl_LeaveRoomView_Night_1_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_2_en","features.leaveroom.impl_LeaveRoomView_Night_2_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_3_en","features.leaveroom.impl_LeaveRoomView_Night_3_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_4_en","features.leaveroom.impl_LeaveRoomView_Night_4_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_5_en","features.leaveroom.impl_LeaveRoomView_Night_5_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_6_en","features.leaveroom.impl_LeaveRoomView_Night_6_en",20728,], +["features.leaveroom.impl_LeaveRoomView_Day_7_en","features.leaveroom.impl_LeaveRoomView_Night_7_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_0_en","features.space.impl.leave_LeaveSpaceView_Night_0_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_10_en","features.space.impl.leave_LeaveSpaceView_Night_10_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_1_en","features.space.impl.leave_LeaveSpaceView_Night_1_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_2_en","features.space.impl.leave_LeaveSpaceView_Night_2_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_3_en","features.space.impl.leave_LeaveSpaceView_Night_3_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_4_en","features.space.impl.leave_LeaveSpaceView_Night_4_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_5_en","features.space.impl.leave_LeaveSpaceView_Night_5_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_6_en","features.space.impl.leave_LeaveSpaceView_Night_6_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_7_en","features.space.impl.leave_LeaveSpaceView_Night_7_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_8_en","features.space.impl.leave_LeaveSpaceView_Night_8_en",20728,], +["features.space.impl.leave_LeaveSpaceView_Day_9_en","features.space.impl.leave_LeaveSpaceView_Night_9_en",20728,], ["libraries.designsystem.background_LightGradientBackground_Day_0_en","libraries.designsystem.background_LightGradientBackground_Night_0_en",0,], ["libraries.designsystem.theme.components_LinearProgressIndicator_Progress_Indicators_en","",0,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20722,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20722,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20722,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20722,], -["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20722,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_0_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_1_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_1_en",20728,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_2_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_2_en",20728,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_3_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_3_en",20728,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_4_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_4_en",20728,], +["features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Day_5_en","features.linknewdevice.impl.screens.root_LinkNewDeviceRootView_Night_5_en",20728,], ["features.messages.impl.link_LinkView_Day_0_en","features.messages.impl.link_LinkView_Night_0_en",0,], -["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20722,], +["features.messages.impl.link_LinkView_Day_1_en","features.messages.impl.link_LinkView_Night_1_en",20728,], ["libraries.designsystem.components.dialogs_ListDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_ListDialog_Day_0_en","libraries.designsystem.components.dialogs_ListDialog_Night_0_en",0,], ["libraries.designsystem.theme.components_ListItemPrimaryActionWithIcon_List_item_-_Primary_action_&_Icon_List_items_en","",0,], @@ -621,53 +625,54 @@ export const screenshots = [ ["libraries.designsystem.theme.components_ListSupportingTextLargePadding_List_supporting_text_-_large_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextNoPadding_List_supporting_text_-_no_padding_List_sections_en","",0,], ["libraries.designsystem.theme.components_ListSupportingTextSmallPadding_List_supporting_text_-_small_padding_List_sections_en","",0,], -["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20722,], +["features.location.api_LiveLocationSharingBanner_Day_0_en","features.location.api_LiveLocationSharingBanner_Night_0_en",20728,], ["libraries.textcomposer.components_LiveWaveformView_Day_0_en","libraries.textcomposer.components_LiveWaveformView_Night_0_en",0,], ["appnav.room.joined_LoadingRoomNodeView_Day_0_en","appnav.room.joined_LoadingRoomNodeView_Night_0_en",0,], -["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20722,], +["appnav.room.joined_LoadingRoomNodeView_Day_1_en","appnav.room.joined_LoadingRoomNodeView_Night_1_en",20728,], ["libraries.designsystem.components_LocationPin_Day_0_en","libraries.designsystem.components_LocationPin_Night_0_en",0,], -["features.preferences.impl.location_LocationSettingsView_0_en","",20724,], -["features.preferences.impl.location_LocationSettingsView_1_en","",20724,], -["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",20722,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20722,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20722,], -["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20722,], +["features.preferences.impl.location_LocationSettingsView_0_en","",20728,], +["features.preferences.impl.location_LocationSettingsView_1_en","",20728,], +["features.location.impl.common.ui_LocationShareRow_Day_0_en","features.location.impl.common.ui_LocationShareRow_Night_0_en",20728,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_0_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_0_en",20728,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_1_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_1_en",20728,], +["features.lockscreen.impl.settings_LockScreenSettingsView_Day_2_en","features.lockscreen.impl.settings_LockScreenSettingsView_Night_2_en",20728,], ["appnav.loggedin_LoggedInView_Day_0_en","appnav.loggedin_LoggedInView_Night_0_en",0,], -["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20722,], -["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20722,], -["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20722,], -["appnav.loggedin_LoggedInView_Day_4_en","appnav.loggedin_LoggedInView_Night_4_en",20722,], -["appnav.loggedin_LoggedInView_Day_5_en","appnav.loggedin_LoggedInView_Night_5_en",20722,], -["appnav.loggedin_LoggedInView_Day_6_en","appnav.loggedin_LoggedInView_Night_6_en",20722,], -["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20722,], -["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20722,], -["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20722,], -["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20722,], -["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20722,], -["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20722,], -["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20722,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20722,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20722,], -["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20722,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20722,], -["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20722,], -["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20722,], -["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20722,], -["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20722,], -["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20722,], -["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20722,], -["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20722,], -["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20722,], -["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20722,], -["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20722,], -["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20722,], -["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20722,], -["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20722,], +["appnav.loggedin_LoggedInView_Day_1_en","appnav.loggedin_LoggedInView_Night_1_en",20728,], +["appnav.loggedin_LoggedInView_Day_2_en","appnav.loggedin_LoggedInView_Night_2_en",20728,], +["appnav.loggedin_LoggedInView_Day_3_en","appnav.loggedin_LoggedInView_Night_3_en",20728,], +["appnav.loggedin_LoggedInView_Day_4_en","appnav.loggedin_LoggedInView_Night_4_en",20728,], +["appnav.loggedin_LoggedInView_Day_5_en","appnav.loggedin_LoggedInView_Night_5_en",20728,], +["appnav.loggedin_LoggedInView_Day_6_en","appnav.loggedin_LoggedInView_Night_6_en",20728,], +["features.login.impl.login_LoginModeView_Day_0_en","features.login.impl.login_LoginModeView_Night_0_en",20728,], +["features.login.impl.login_LoginModeView_Day_1_en","features.login.impl.login_LoginModeView_Night_1_en",20728,], +["features.login.impl.login_LoginModeView_Day_2_en","features.login.impl.login_LoginModeView_Night_2_en",20728,], +["features.login.impl.login_LoginModeView_Day_3_en","features.login.impl.login_LoginModeView_Night_3_en",20728,], +["features.login.impl.login_LoginModeView_Day_4_en","features.login.impl.login_LoginModeView_Night_4_en",20728,], +["features.login.impl.login_LoginModeView_Day_5_en","features.login.impl.login_LoginModeView_Night_5_en",20728,], +["features.login.impl.login_LoginModeView_Day_6_en","features.login.impl.login_LoginModeView_Night_6_en",20728,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_0_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_0_en",20728,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_1_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_1_en",20728,], +["features.login.impl.screens.loginpassword_LoginPasswordView_Day_2_en","features.login.impl.screens.loginpassword_LoginPasswordView_Night_2_en",20728,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_0_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_0_en",20728,], +["features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Day_1_en","features.login.impl.screens.classic.loginwithclassic_LoginWithClassicView_Night_1_en",20728,], +["features.logout.impl_LogoutView_Day_0_en","features.logout.impl_LogoutView_Night_0_en",20728,], +["features.logout.impl_LogoutView_Day_10_en","features.logout.impl_LogoutView_Night_10_en",20728,], +["features.logout.impl_LogoutView_Day_11_en","features.logout.impl_LogoutView_Night_11_en",20728,], +["features.logout.impl_LogoutView_Day_12_en","features.logout.impl_LogoutView_Night_12_en",0,], +["features.logout.impl_LogoutView_Day_1_en","features.logout.impl_LogoutView_Night_1_en",20728,], +["features.logout.impl_LogoutView_Day_2_en","features.logout.impl_LogoutView_Night_2_en",20728,], +["features.logout.impl_LogoutView_Day_3_en","features.logout.impl_LogoutView_Night_3_en",20728,], +["features.logout.impl_LogoutView_Day_4_en","features.logout.impl_LogoutView_Night_4_en",20728,], +["features.logout.impl_LogoutView_Day_5_en","features.logout.impl_LogoutView_Night_5_en",20728,], +["features.logout.impl_LogoutView_Day_6_en","features.logout.impl_LogoutView_Night_6_en",20728,], +["features.logout.impl_LogoutView_Day_7_en","features.logout.impl_LogoutView_Night_7_en",20728,], +["features.logout.impl_LogoutView_Day_8_en","features.logout.impl_LogoutView_Night_8_en",20728,], +["features.logout.impl_LogoutView_Day_9_en","features.logout.impl_LogoutView_Night_9_en",20728,], ["libraries.designsystem.components.button_MainActionButton_Buttons_en","",0,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20722,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20722,], -["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20722,], -["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20722,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_0_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_0_en",20728,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_1_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_1_en",20728,], +["features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Day_2_en","features.securityandprivacy.impl.manageauthorizedspaces_ManageAuthorizedSpacesView_Night_2_en",20728,], +["libraries.textcomposer_MarkdownTextComposerEdit_Day_0_en","libraries.textcomposer_MarkdownTextComposerEdit_Night_0_en",20728,], ["libraries.textcomposer.components.markdown_MarkdownTextInput_Day_0_en","libraries.textcomposer.components.markdown_MarkdownTextInput_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomInfo_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Day_0_en","libraries.designsystem.atomic.atoms_MatrixBadgeAtomNegative_Night_0_en",0,], @@ -682,44 +687,44 @@ export const screenshots = [ ["libraries.matrix.ui.components_MatrixUserRow_Day_2_en","libraries.matrix.ui.components_MatrixUserRow_Night_2_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_0_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.audio_MediaAudioView_Day_1_en","libraries.mediaviewer.impl.local.audio_MediaAudioView_Night_1_en",0,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20722,], -["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20722,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20722,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20722,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20722,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20722,], -["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en",20722,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",20722,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en",20722,], -["libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20722,], -["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20722,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_0_en",20728,], +["libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDeleteConfirmationBottomSheet_Night_1_en",20728,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_0_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_0_en",20728,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_1_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_1_en",20728,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_2_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_2_en",20728,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_3_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_3_en",20728,], +["libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Day_4_en","libraries.mediaviewer.impl.details_MediaDetailsBottomSheet_Night_4_en",20728,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_0_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_0_en",20728,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_1_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_1_en",20728,], +["libraries.mediaviewer.impl.local.file_MediaFileView_Day_2_en","libraries.mediaviewer.impl.local.file_MediaFileView_Night_2_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_0_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_0_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_10_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_10_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_11_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_11_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_12_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_12_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_1_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_1_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_2_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_2_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_3_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_3_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_4_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_4_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_5_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_5_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_6_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_6_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_7_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_7_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_8_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_8_en",20728,], +["libraries.mediaviewer.impl.gallery_MediaGalleryView_Day_9_en","libraries.mediaviewer.impl.gallery_MediaGalleryView_Night_9_en",20728,], ["libraries.mediaviewer.impl.local.image_MediaImageView_Day_0_en","libraries.mediaviewer.impl.local.image_MediaImageView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_0_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_0_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_1_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_1_en",0,], ["libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Day_2_en","libraries.mediaviewer.impl.local.player_MediaPlayerControllerView_Night_2_en",0,], -["features.preferences.impl.media_MediaSettingsView_Day_0_en","features.preferences.impl.media_MediaSettingsView_Night_0_en",20724,], -["features.preferences.impl.media_MediaSettingsView_Day_1_en","features.preferences.impl.media_MediaSettingsView_Night_1_en",20724,], -["features.preferences.impl.media_MediaSettingsView_Day_2_en","features.preferences.impl.media_MediaSettingsView_Night_2_en",20724,], +["features.preferences.impl.media_MediaSettingsView_Day_0_en","features.preferences.impl.media_MediaSettingsView_Night_0_en",20728,], +["features.preferences.impl.media_MediaSettingsView_Day_1_en","features.preferences.impl.media_MediaSettingsView_Night_1_en",20728,], +["features.preferences.impl.media_MediaSettingsView_Day_2_en","features.preferences.impl.media_MediaSettingsView_Night_2_en",20728,], ["libraries.mediaviewer.impl.local.video_MediaVideoView_Day_0_en","libraries.mediaviewer.impl.local.video_MediaVideoView_Night_0_en",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_11_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_12_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_14_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_15_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_17_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_18_en","",0,], @@ -728,23 +733,23 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_23_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_24_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_23_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_24_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_2_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_6_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_7_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerViewLandscape_9_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_0_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_10_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_11_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_12_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_13_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_14_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_15_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_16_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_17_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_18_en","",0,], @@ -753,14 +758,14 @@ export const screenshots = [ ["libraries.mediaviewer.impl.viewer_MediaViewerView_20_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_21_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_22_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_23_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_24_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_23_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_24_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_2_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_3_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_4_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_5_en","",0,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",20722,], -["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",20722,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_6_en","",20728,], +["libraries.mediaviewer.impl.viewer_MediaViewerView_7_en","",20728,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_8_en","",0,], ["libraries.mediaviewer.impl.viewer_MediaViewerView_9_en","",0,], ["libraries.designsystem.theme.components_MediumTopAppBar_App_Bars_en","",0,], @@ -769,7 +774,7 @@ export const screenshots = [ ["libraries.textcomposer.mentions_MentionSpanTheme_Day_0_en","libraries.textcomposer.mentions_MentionSpanTheme_Night_0_en",0,], ["libraries.designsystem.theme.components.previews_Menu_Menus_en","",0,], ["features.messages.impl.messagecomposer_MessageComposerViewVoice_Day_0_en","features.messages.impl.messagecomposer_MessageComposerViewVoice_Night_0_en",0,], -["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20722,], +["features.messages.impl.messagecomposer_MessageComposerView_Day_0_en","features.messages.impl.messagecomposer_MessageComposerView_Night_0_en",20728,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_0_en","features.messages.impl.timeline.components_MessageEventBubble_Night_0_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_1_en","features.messages.impl.timeline.components_MessageEventBubble_Night_1_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_2_en","features.messages.impl.timeline.components_MessageEventBubble_Night_2_en",0,], @@ -778,7 +783,7 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessageEventBubble_Day_5_en","features.messages.impl.timeline.components_MessageEventBubble_Night_5_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_6_en","features.messages.impl.timeline.components_MessageEventBubble_Night_6_en",0,], ["features.messages.impl.timeline.components_MessageEventBubble_Day_7_en","features.messages.impl.timeline.components_MessageEventBubble_Night_7_en",0,], -["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20722,], +["features.messages.impl.timeline.components_MessageShieldView_Day_0_en","features.messages.impl.timeline.components_MessageShieldView_Night_0_en",20728,], ["features.messages.impl.timeline.components_MessageStateEventContainer_Day_0_en","features.messages.impl.timeline.components_MessageStateEventContainer_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonAdd_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonAdd_Night_0_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButtonExtra_Day_0_en","features.messages.impl.timeline.components_MessagesReactionButtonExtra_Night_0_en",0,], @@ -787,49 +792,49 @@ export const screenshots = [ ["features.messages.impl.timeline.components_MessagesReactionButton_Day_2_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_2_en",0,], ["features.messages.impl.timeline.components_MessagesReactionButton_Day_3_en","features.messages.impl.timeline.components_MessagesReactionButton_Night_3_en",0,], ["features.messages.impl_MessagesViewA11y_en","",0,], -["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20722,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20722,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20722,], -["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20722,], -["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20722,], -["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20722,], -["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20722,], -["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20722,], -["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20722,], -["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20722,], -["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20722,], -["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20722,], -["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20722,], -["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20722,], -["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20722,], -["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20722,], -["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20722,], +["features.messages.impl.topbars_MessagesViewTopBar_Day_0_en","features.messages.impl.topbars_MessagesViewTopBar_Night_0_en",20728,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_0_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_0_en",20728,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_1_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_1_en",20728,], +["features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Day_2_en","features.messages.impl.crypto.identity_MessagesViewWithIdentityChange_Night_2_en",20728,], +["features.messages.impl_MessagesView_Day_0_en","features.messages.impl_MessagesView_Night_0_en",20728,], +["features.messages.impl_MessagesView_Day_10_en","features.messages.impl_MessagesView_Night_10_en",20728,], +["features.messages.impl_MessagesView_Day_11_en","features.messages.impl_MessagesView_Night_11_en",20728,], +["features.messages.impl_MessagesView_Day_12_en","features.messages.impl_MessagesView_Night_12_en",20728,], +["features.messages.impl_MessagesView_Day_1_en","features.messages.impl_MessagesView_Night_1_en",20728,], +["features.messages.impl_MessagesView_Day_2_en","features.messages.impl_MessagesView_Night_2_en",20728,], +["features.messages.impl_MessagesView_Day_3_en","features.messages.impl_MessagesView_Night_3_en",20728,], +["features.messages.impl_MessagesView_Day_4_en","features.messages.impl_MessagesView_Night_4_en",20728,], +["features.messages.impl_MessagesView_Day_5_en","features.messages.impl_MessagesView_Night_5_en",20728,], +["features.messages.impl_MessagesView_Day_6_en","features.messages.impl_MessagesView_Night_6_en",20728,], +["features.messages.impl_MessagesView_Day_7_en","features.messages.impl_MessagesView_Night_7_en",20728,], +["features.messages.impl_MessagesView_Day_8_en","features.messages.impl_MessagesView_Night_8_en",20728,], +["features.messages.impl_MessagesView_Day_9_en","features.messages.impl_MessagesView_Night_9_en",20728,], ["features.migration.impl_MigrationView_Day_0_en","features.migration.impl_MigrationView_Night_0_en",0,], -["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20722,], -["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",20722,], +["features.migration.impl_MigrationView_Day_1_en","features.migration.impl_MigrationView_Night_1_en",20728,], +["features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Day_0_en","features.login.impl.screens.classic.missingkeybackup_MissingKeyBackupView_Night_0_en",20728,], ["libraries.designsystem.theme.components_ModalBottomSheetDark_Bottom_Sheets_en","",0,], ["libraries.designsystem.theme.components_ModalBottomSheetLight_Bottom_Sheets_en","",0,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_0_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_1_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_2_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_3_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_4_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_5_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_6_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_0_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_1_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_2_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_3_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_4_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_5_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewDark_6_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_0_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_1_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_2_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_3_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_4_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_5_en","",20724,], -["features.preferences.impl.moderation_ModerationAndSafetyViewLight_6_en","",20724,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_0_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_1_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_2_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_3_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_4_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_5_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewBlack_6_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_0_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_1_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_2_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_3_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_4_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_5_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewDark_6_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_0_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_1_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_2_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_3_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_4_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_5_en","",20728,], +["features.preferences.impl.moderation_ModerationAndSafetyViewLight_6_en","",20728,], ["appicon.element_MonochromeIcon_en","",0,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_MultipleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_MultipleSelectionDialog_Night_0_en",0,], @@ -837,130 +842,130 @@ export const screenshots = [ ["libraries.designsystem.components.list_MutipleSelectionListItemSelected_Multiple_selection_List_item_-_selection_in_supporting_text_List_items_en","",0,], ["libraries.designsystem.components.list_MutipleSelectionListItem_Multiple_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.designsystem.theme.components_NavigationBar_App_Bars_en","",0,], -["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20722,], -["libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Night_0_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20722,], -["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20722,], -["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20722,], +["features.home.impl.components_NewNotificationSoundBanner_Day_0_en","features.home.impl.components_NewNotificationSoundBanner_Night_0_en",20728,], +["libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Day_0_en","libraries.matrix.ui.media.contentvalidation_NotFoundContentView_Night_0_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_0_en","features.preferences.impl.notifications_NotificationSettingsView_Night_0_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_10_en","features.preferences.impl.notifications_NotificationSettingsView_Night_10_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_11_en","features.preferences.impl.notifications_NotificationSettingsView_Night_11_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_12_en","features.preferences.impl.notifications_NotificationSettingsView_Night_12_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_13_en","features.preferences.impl.notifications_NotificationSettingsView_Night_13_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_14_en","features.preferences.impl.notifications_NotificationSettingsView_Night_14_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_15_en","features.preferences.impl.notifications_NotificationSettingsView_Night_15_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_16_en","features.preferences.impl.notifications_NotificationSettingsView_Night_16_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_17_en","features.preferences.impl.notifications_NotificationSettingsView_Night_17_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_18_en","features.preferences.impl.notifications_NotificationSettingsView_Night_18_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_19_en","features.preferences.impl.notifications_NotificationSettingsView_Night_19_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_1_en","features.preferences.impl.notifications_NotificationSettingsView_Night_1_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_20_en","features.preferences.impl.notifications_NotificationSettingsView_Night_20_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_2_en","features.preferences.impl.notifications_NotificationSettingsView_Night_2_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_3_en","features.preferences.impl.notifications_NotificationSettingsView_Night_3_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_4_en","features.preferences.impl.notifications_NotificationSettingsView_Night_4_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_5_en","features.preferences.impl.notifications_NotificationSettingsView_Night_5_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_6_en","features.preferences.impl.notifications_NotificationSettingsView_Night_6_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_7_en","features.preferences.impl.notifications_NotificationSettingsView_Night_7_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_8_en","features.preferences.impl.notifications_NotificationSettingsView_Night_8_en",20728,], +["features.preferences.impl.notifications_NotificationSettingsView_Day_9_en","features.preferences.impl.notifications_NotificationSettingsView_Night_9_en",20728,], +["features.ftue.impl.notifications_NotificationsOptInView_Day_0_en","features.ftue.impl.notifications_NotificationsOptInView_Night_0_en",20728,], ["features.linknewdevice.impl.screens.number.component_NumberTextField_Day_0_en","features.linknewdevice.impl.screens.number.component_NumberTextField_Night_0_en",0,], ["libraries.designsystem.atomic.pages_OnBoardingPage_Day_0_en","libraries.designsystem.atomic.pages_OnBoardingPage_Night_0_en",0,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20722,], -["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20722,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_0_en","features.login.impl.screens.onboarding_OnBoardingView_Night_0_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_1_en","features.login.impl.screens.onboarding_OnBoardingView_Night_1_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_2_en","features.login.impl.screens.onboarding_OnBoardingView_Night_2_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_3_en","features.login.impl.screens.onboarding_OnBoardingView_Night_3_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_4_en","features.login.impl.screens.onboarding_OnBoardingView_Night_4_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_5_en","features.login.impl.screens.onboarding_OnBoardingView_Night_5_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_6_en","features.login.impl.screens.onboarding_OnBoardingView_Night_6_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_7_en","features.login.impl.screens.onboarding_OnBoardingView_Night_7_en",20728,], +["features.login.impl.screens.onboarding_OnBoardingView_Day_8_en","features.login.impl.screens.onboarding_OnBoardingView_Night_8_en",20728,], ["libraries.designsystem.background_OnboardingBackground_Day_0_en","libraries.designsystem.background_OnboardingBackground_Night_0_en",0,], -["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20722,], +["libraries.matrix.ui.components_OrganizationHeader_Day_0_en","libraries.matrix.ui.components_OrganizationHeader_Night_0_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_0_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_0_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_10_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_10_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_11_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_11_en",20728,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_12_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_12_en",0,], ["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_13_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_13_en",0,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20722,], -["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20722,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_1_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_1_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_2_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_2_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_3_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_3_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_4_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_4_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_5_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_5_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_6_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_6_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_7_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_7_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_8_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_8_en",20728,], +["features.verifysession.impl.outgoing_OutgoingVerificationView_Day_9_en","features.verifysession.impl.outgoing_OutgoingVerificationView_Night_9_en",20728,], ["libraries.designsystem.theme.components_OutlinedButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_OutlinedButtonSmall_Buttons_en","",0,], -["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20722,], -["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20722,], -["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20722,], -["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20722,], -["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20722,], -["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20722,], +["libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Day_0_en","libraries.mediaviewer.impl.local.pdf_PdfPagesErrorView_Night_0_en",20728,], +["features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Day_0_en","features.rolesandpermissions.impl.roles_PendingMemberRowWithLongName_Night_0_en",20728,], +["libraries.permissions.api_PermissionsView_Day_0_en","libraries.permissions.api_PermissionsView_Night_0_en",20728,], +["libraries.permissions.api_PermissionsView_Day_1_en","libraries.permissions.api_PermissionsView_Night_1_en",20728,], +["libraries.permissions.api_PermissionsView_Day_2_en","libraries.permissions.api_PermissionsView_Night_2_en",20728,], +["libraries.permissions.api_PermissionsView_Day_3_en","libraries.permissions.api_PermissionsView_Night_3_en",20728,], ["features.lockscreen.impl.components_PinEntryTextField_Day_0_en","features.lockscreen.impl.components_PinEntryTextField_Night_0_en",0,], ["features.lockscreen.impl.unlock.keypad_PinKeypad_Day_0_en","features.lockscreen.impl.unlock.keypad_PinKeypad_Night_0_en",0,], -["features.lockscreen.impl.unlock_PinUnlockViewCompact_0_en","",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewCompact_1_en","",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_10_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_10_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_10_en","features.lockscreen.impl.unlock_PinUnlockView_Night_10_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20722,], -["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20722,], +["features.lockscreen.impl.unlock_PinUnlockViewCompact_0_en","",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewCompact_1_en","",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_0_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_0_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_10_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_10_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_1_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_1_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_2_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_2_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_3_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_3_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_4_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_4_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_5_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_5_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_6_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_6_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_7_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_7_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_8_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_8_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockViewInApp_Day_9_en","features.lockscreen.impl.unlock_PinUnlockViewInApp_Night_9_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_0_en","features.lockscreen.impl.unlock_PinUnlockView_Night_0_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_10_en","features.lockscreen.impl.unlock_PinUnlockView_Night_10_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_1_en","features.lockscreen.impl.unlock_PinUnlockView_Night_1_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_2_en","features.lockscreen.impl.unlock_PinUnlockView_Night_2_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_3_en","features.lockscreen.impl.unlock_PinUnlockView_Night_3_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_4_en","features.lockscreen.impl.unlock_PinUnlockView_Night_4_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_5_en","features.lockscreen.impl.unlock_PinUnlockView_Night_5_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_6_en","features.lockscreen.impl.unlock_PinUnlockView_Night_6_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_7_en","features.lockscreen.impl.unlock_PinUnlockView_Night_7_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_8_en","features.lockscreen.impl.unlock_PinUnlockView_Night_8_en",20728,], +["features.lockscreen.impl.unlock_PinUnlockView_Day_9_en","features.lockscreen.impl.unlock_PinUnlockView_Night_9_en",20728,], ["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_0_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_0_en",0,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20722,], -["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20722,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20722,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20722,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20722,], -["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20722,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_10_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_10_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_1_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_1_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_2_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_2_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_3_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_3_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_4_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_4_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_5_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_5_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_6_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_6_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_7_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_7_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_8_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_8_en",20728,], +["features.messages.impl.pinned.banner_PinnedMessagesBannerView_Day_9_en","features.messages.impl.pinned.banner_PinnedMessagesBannerView_Night_9_en",20728,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_0_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_0_en",20728,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_1_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_1_en",20728,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_2_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_2_en",20728,], +["features.messages.impl.pinned.list_PinnedMessagesListView_Day_3_en","features.messages.impl.pinned.list_PinnedMessagesListView_Night_3_en",20728,], ["libraries.designsystem.atomic.atoms_PlaceholderAtom_Day_0_en","libraries.designsystem.atomic.atoms_PlaceholderAtom_Night_0_en",0,], ["libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Day_0_en","libraries.designsystem.atomic.atoms_PlaybackSpeedButton_Night_0_en",0,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20722,], -["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20722,], -["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20722,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20722,], -["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20722,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedNotSelected_Night_0_en",20728,], +["features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewDisclosedSelected_Night_0_en",20728,], +["features.poll.api.pollcontent_PollAnswerViewEndedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedSelected_Night_0_en",20728,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerNotSelected_Night_0_en",20728,], +["features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewEndedWinnerSelected_Night_0_en",20728,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedNotSelected_Night_0_en",0,], ["features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Day_0_en","features.poll.api.pollcontent_PollAnswerViewUndisclosedSelected_Night_0_en",0,], -["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewMultipleSelection_Day_0_en","features.poll.api.pollcontent_PollContentViewMultipleSelection_Night_0_en",20722,], -["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20722,], -["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20722,], -["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20722,], -["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20722,], -["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20722,], -["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20722,], +["features.poll.api.pollcontent_PollContentViewCreatorEditable_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEditable_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewCreatorEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewCreatorEnded_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewCreator_Day_0_en","features.poll.api.pollcontent_PollContentViewCreator_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewDisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewDisclosed_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewEnded_Day_0_en","features.poll.api.pollcontent_PollContentViewEnded_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewMultipleSelection_Day_0_en","features.poll.api.pollcontent_PollContentViewMultipleSelection_Night_0_en",20728,], +["features.poll.api.pollcontent_PollContentViewUndisclosed_Day_0_en","features.poll.api.pollcontent_PollContentViewUndisclosed_Night_0_en",20728,], +["features.poll.impl.history_PollHistoryView_Day_0_en","features.poll.impl.history_PollHistoryView_Night_0_en",20728,], +["features.poll.impl.history_PollHistoryView_Day_1_en","features.poll.impl.history_PollHistoryView_Night_1_en",20728,], +["features.poll.impl.history_PollHistoryView_Day_2_en","features.poll.impl.history_PollHistoryView_Night_2_en",20728,], +["features.poll.impl.history_PollHistoryView_Day_3_en","features.poll.impl.history_PollHistoryView_Night_3_en",20728,], +["features.poll.impl.history_PollHistoryView_Day_4_en","features.poll.impl.history_PollHistoryView_Night_4_en",20728,], ["features.poll.api.pollcontent_PollTitleView_Day_0_en","features.poll.api.pollcontent_PollTitleView_Night_0_en",0,], ["libraries.designsystem.components.preferences_PreferenceCategory_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceCheckbox_Preferences_en","",0,], @@ -974,235 +979,235 @@ export const screenshots = [ ["libraries.designsystem.components.preferences_PreferenceRow_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSlide_Preferences_en","",0,], ["libraries.designsystem.components.preferences_PreferenceSwitch_Preferences_en","",0,], -["features.preferences.impl.account_PreferencesAccountView_Day_0_en","features.preferences.impl.account_PreferencesAccountView_Night_0_en",20724,], -["features.preferences.impl.account_PreferencesAccountView_Day_1_en","features.preferences.impl.account_PreferencesAccountView_Night_1_en",20724,], -["features.preferences.impl.account_PreferencesAccountView_Day_2_en","features.preferences.impl.account_PreferencesAccountView_Night_2_en",20724,], -["features.preferences.impl.account_PreferencesAccountView_Day_3_en","features.preferences.impl.account_PreferencesAccountView_Night_3_en",20724,], -["features.preferences.impl.account_PreferencesAccountView_Day_4_en","features.preferences.impl.account_PreferencesAccountView_Night_4_en",20724,], -["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewDark_6_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20722,], -["features.preferences.impl.root_PreferencesRootViewLight_6_en","",20722,], +["features.preferences.impl.account_PreferencesAccountView_Day_0_en","features.preferences.impl.account_PreferencesAccountView_Night_0_en",20728,], +["features.preferences.impl.account_PreferencesAccountView_Day_1_en","features.preferences.impl.account_PreferencesAccountView_Night_1_en",20728,], +["features.preferences.impl.account_PreferencesAccountView_Day_2_en","features.preferences.impl.account_PreferencesAccountView_Night_2_en",20728,], +["features.preferences.impl.account_PreferencesAccountView_Day_3_en","features.preferences.impl.account_PreferencesAccountView_Night_3_en",20728,], +["features.preferences.impl.account_PreferencesAccountView_Day_4_en","features.preferences.impl.account_PreferencesAccountView_Night_4_en",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_0_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_1_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_2_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_3_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_4_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_5_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewDark_6_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_0_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_1_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_2_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_3_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_4_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_5_en","",20728,], +["features.preferences.impl.root_PreferencesRootViewLight_6_en","",20728,], ["features.messages.impl.timeline.components.event_ProgressButton_Day_0_en","features.messages.impl.timeline.components.event_ProgressButton_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20722,], -["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20722,], +["libraries.designsystem.components_ProgressDialogContent_Dialogs_en","",20728,], +["libraries.designsystem.components_ProgressDialogWithContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithContent_Night_0_en",20728,], ["libraries.designsystem.components_ProgressDialogWithTextAndContent_Day_0_en","libraries.designsystem.components_ProgressDialogWithTextAndContent_Night_0_en",0,], -["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20722,], -["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20722,], -["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20722,], -["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20722,], -["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20722,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20722,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20722,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20722,], -["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20722,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20722,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20722,], -["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20722,], -["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20722,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20722,], -["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20722,], -["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20722,], +["libraries.designsystem.components_ProgressDialog_Day_0_en","libraries.designsystem.components_ProgressDialog_Night_0_en",20728,], +["features.messages.impl.timeline.protection_ProtectedView_Day_0_en","features.messages.impl.timeline.protection_ProtectedView_Night_0_en",20728,], +["features.messages.impl.timeline.protection_ProtectedView_Day_1_en","features.messages.impl.timeline.protection_ProtectedView_Night_1_en",20728,], +["features.messages.impl.timeline.protection_ProtectedView_Day_2_en","features.messages.impl.timeline.protection_ProtectedView_Night_2_en",20728,], +["features.messages.impl.timeline.protection_ProtectedView_Day_3_en","features.messages.impl.timeline.protection_ProtectedView_Night_3_en",20728,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_0_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_0_en",20728,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_1_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_1_en",20728,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_2_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_2_en",20728,], +["libraries.troubleshoot.impl.history_PushHistoryView_Day_3_en","libraries.troubleshoot.impl.history_PushHistoryView_Night_3_en",20728,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_0_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_0_en",20728,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_1_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_1_en",20728,], +["features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Day_2_en","features.login.impl.screens.qrcode.confirmation_QrCodeConfirmationView_Night_2_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_0_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_0_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_1_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_1_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_2_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_2_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_3_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_3_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_4_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_4_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_5_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_5_en",20728,], +["features.login.impl.screens.qrcode.error_QrCodeErrorView_Day_6_en","features.login.impl.screens.qrcode.error_QrCodeErrorView_Night_6_en",20728,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_0_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_0_en",20728,], +["features.login.impl.screens.qrcode.intro_QrCodeIntroView_Day_1_en","features.login.impl.screens.qrcode.intro_QrCodeIntroView_Night_1_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_0_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_0_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_1_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_1_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_2_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_2_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_3_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_3_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_4_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_4_en",20728,], +["features.login.impl.screens.qrcode.scan_QrCodeScanView_Day_5_en","features.login.impl.screens.qrcode.scan_QrCodeScanView_Night_5_en",20728,], ["libraries.qrcode_QrCodeView_en","",0,], ["libraries.designsystem.theme.components_RadioButton_Toggles_en","",0,], -["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20722,], -["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20722,], +["features.rageshake.api.detection_RageshakeDialogContent_Day_0_en","features.rageshake.api.detection_RageshakeDialogContent_Night_0_en",20728,], +["features.rageshake.api.preferences_RageshakePreferencesView_Day_0_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_0_en",20728,], ["features.rageshake.api.preferences_RageshakePreferencesView_Day_1_en","features.rageshake.api.preferences_RageshakePreferencesView_Night_1_en",0,], ["features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Day_0_en","features.messages.impl.timeline.components.reactionsummary_ReactionSummaryViewContent_Night_0_en",0,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20722,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20722,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20722,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20722,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20722,], -["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20722,], -["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20722,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_0_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_0_en",20728,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_1_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_1_en",20728,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_2_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_2_en",20728,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_3_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_3_en",20728,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_4_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_4_en",20728,], +["features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Day_5_en","features.messages.impl.timeline.components.receipt.bottomsheet_ReadReceiptBottomSheet_Night_5_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_0_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_0_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_10_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_10_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_11_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_11_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_12_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_12_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_13_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_13_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_14_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_14_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_1_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_1_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_2_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_2_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_3_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_3_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_4_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_4_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_5_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_5_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_6_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_6_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_7_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_7_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_8_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_8_en",20728,], +["features.securebackup.impl.setup.views_RecoveryKeyView_Day_9_en","features.securebackup.impl.setup.views_RecoveryKeyView_Night_9_en",20728,], ["libraries.designsystem.atomic.atoms_RedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_RedIndicatorAtom_Night_0_en",0,], -["features.location.api_RenderingMapsNotSupportedDialog_Day_0_en","features.location.api_RenderingMapsNotSupportedDialog_Night_0_en",20722,], +["features.location.api_RenderingMapsNotSupportedDialog_Day_0_en","features.location.api_RenderingMapsNotSupportedDialog_Night_0_en",20728,], ["features.messages.impl.timeline.components_ReplySwipeIndicator_Day_0_en","features.messages.impl.timeline.components_ReplySwipeIndicator_Night_0_en",0,], -["libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Night_0_en",20722,], -["libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Night_0_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20722,], -["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20722,], -["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20722,], -["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20722,], -["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20722,], -["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20722,], -["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20722,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20722,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20722,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20722,], -["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20722,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20722,], -["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20722,], +["libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToInvalidContent_Night_0_en",20728,], +["libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Day_0_en","libraries.matrix.ui.messages.reply_ReplyToNotFoundContent_Night_0_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_0_en","features.messages.impl.report_ReportMessageView_Night_0_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_1_en","features.messages.impl.report_ReportMessageView_Night_1_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_2_en","features.messages.impl.report_ReportMessageView_Night_2_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_3_en","features.messages.impl.report_ReportMessageView_Night_3_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_4_en","features.messages.impl.report_ReportMessageView_Night_4_en",20728,], +["features.messages.impl.report_ReportMessageView_Day_5_en","features.messages.impl.report_ReportMessageView_Night_5_en",20728,], +["features.reportroom.impl_ReportRoomView_Day_0_en","features.reportroom.impl_ReportRoomView_Night_0_en",20728,], +["features.reportroom.impl_ReportRoomView_Day_1_en","features.reportroom.impl_ReportRoomView_Night_1_en",20728,], +["features.reportroom.impl_ReportRoomView_Day_2_en","features.reportroom.impl_ReportRoomView_Night_2_en",20728,], +["features.reportroom.impl_ReportRoomView_Day_3_en","features.reportroom.impl_ReportRoomView_Night_3_en",20728,], +["features.reportroom.impl_ReportRoomView_Day_4_en","features.reportroom.impl_ReportRoomView_Night_4_en",20728,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_0_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_0_en",20728,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_1_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_1_en",20728,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_2_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_2_en",20728,], +["features.securebackup.impl.reset.password_ResetIdentityPasswordView_Day_3_en","features.securebackup.impl.reset.password_ResetIdentityPasswordView_Night_3_en",20728,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_0_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_0_en",20728,], +["features.securebackup.impl.reset.root_ResetIdentityRootView_Day_1_en","features.securebackup.impl.reset.root_ResetIdentityRootView_Night_1_en",20728,], ["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_0_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_0_en",0,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20722,], -["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20722,], -["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20722,], -["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20722,], -["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20722,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_1_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_1_en",20728,], +["features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Day_2_en","features.messages.impl.crypto.sendfailure.resolve_ResolveVerifiedUserSendFailureView_Night_2_en",20728,], +["libraries.designsystem.components.dialogs_RetryDialogContent_Dialogs_en","",20728,], +["libraries.designsystem.components.dialogs_RetryDialog_Day_0_en","libraries.designsystem.components.dialogs_RetryDialog_Night_0_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_0_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_0_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_1_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_1_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_2_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_2_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_3_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_3_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_4_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_4_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_5_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_5_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_6_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_6_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_7_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_7_en",20728,], +["features.rolesandpermissions.impl.root_RolesAndPermissionsView_Day_8_en","features.rolesandpermissions.impl.root_RolesAndPermissionsView_Night_8_en",20728,], ["libraries.matrix.ui.room.address_RoomAddressField_Day_0_en","libraries.matrix.ui.room.address_RoomAddressField_Night_0_en",0,], ["features.roomaliasresolver.impl_RoomAliasResolverView_Day_0_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_0_en",0,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20722,], -["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20722,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_1_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_1_en",20728,], +["features.roomaliasresolver.impl_RoomAliasResolverView_Day_2_en","features.roomaliasresolver.impl_RoomAliasResolverView_Night_2_en",20728,], ["features.roomdetails.impl_RoomDetailsA11y_en","",0,], -["features.roomdetails.impl_RoomDetailsDark_0_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_10_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_11_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_12_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_13_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_14_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_15_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_16_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_17_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_18_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_19_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_1_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_20_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_21_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_22_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_2_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_3_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_4_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_5_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_6_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_7_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_8_en","",20722,], -["features.roomdetails.impl_RoomDetailsDark_9_en","",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20722,], -["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20722,], -["features.roomdetails.impl_RoomDetails_0_en","",20722,], -["features.roomdetails.impl_RoomDetails_10_en","",20722,], -["features.roomdetails.impl_RoomDetails_11_en","",20722,], -["features.roomdetails.impl_RoomDetails_12_en","",20722,], -["features.roomdetails.impl_RoomDetails_13_en","",20722,], -["features.roomdetails.impl_RoomDetails_14_en","",20722,], -["features.roomdetails.impl_RoomDetails_15_en","",20722,], -["features.roomdetails.impl_RoomDetails_16_en","",20722,], -["features.roomdetails.impl_RoomDetails_17_en","",20722,], -["features.roomdetails.impl_RoomDetails_18_en","",20722,], -["features.roomdetails.impl_RoomDetails_19_en","",20722,], -["features.roomdetails.impl_RoomDetails_1_en","",20722,], -["features.roomdetails.impl_RoomDetails_20_en","",20722,], -["features.roomdetails.impl_RoomDetails_21_en","",20722,], -["features.roomdetails.impl_RoomDetails_22_en","",20722,], -["features.roomdetails.impl_RoomDetails_2_en","",20722,], -["features.roomdetails.impl_RoomDetails_3_en","",20722,], -["features.roomdetails.impl_RoomDetails_4_en","",20722,], -["features.roomdetails.impl_RoomDetails_5_en","",20722,], -["features.roomdetails.impl_RoomDetails_6_en","",20722,], -["features.roomdetails.impl_RoomDetails_7_en","",20722,], -["features.roomdetails.impl_RoomDetails_8_en","",20722,], -["features.roomdetails.impl_RoomDetails_9_en","",20722,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20722,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20722,], -["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20722,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20722,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20722,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20722,], -["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20722,], -["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20722,], -["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20722,], +["features.roomdetails.impl_RoomDetailsDark_0_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_10_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_11_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_12_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_13_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_14_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_15_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_16_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_17_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_18_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_19_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_1_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_20_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_21_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_22_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_2_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_3_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_4_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_5_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_6_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_7_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_8_en","",20728,], +["features.roomdetails.impl_RoomDetailsDark_9_en","",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_0_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_0_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_1_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_1_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_2_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_2_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_3_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_3_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_4_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_4_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_5_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_5_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_6_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_6_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_7_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_7_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_8_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_8_en",20728,], +["features.roomdetailsedit.impl_RoomDetailsEditView_Day_9_en","features.roomdetailsedit.impl_RoomDetailsEditView_Night_9_en",20728,], +["features.roomdetails.impl_RoomDetails_0_en","",20728,], +["features.roomdetails.impl_RoomDetails_10_en","",20728,], +["features.roomdetails.impl_RoomDetails_11_en","",20728,], +["features.roomdetails.impl_RoomDetails_12_en","",20728,], +["features.roomdetails.impl_RoomDetails_13_en","",20728,], +["features.roomdetails.impl_RoomDetails_14_en","",20728,], +["features.roomdetails.impl_RoomDetails_15_en","",20728,], +["features.roomdetails.impl_RoomDetails_16_en","",20728,], +["features.roomdetails.impl_RoomDetails_17_en","",20728,], +["features.roomdetails.impl_RoomDetails_18_en","",20728,], +["features.roomdetails.impl_RoomDetails_19_en","",20728,], +["features.roomdetails.impl_RoomDetails_1_en","",20728,], +["features.roomdetails.impl_RoomDetails_20_en","",20728,], +["features.roomdetails.impl_RoomDetails_21_en","",20728,], +["features.roomdetails.impl_RoomDetails_22_en","",20728,], +["features.roomdetails.impl_RoomDetails_2_en","",20728,], +["features.roomdetails.impl_RoomDetails_3_en","",20728,], +["features.roomdetails.impl_RoomDetails_4_en","",20728,], +["features.roomdetails.impl_RoomDetails_5_en","",20728,], +["features.roomdetails.impl_RoomDetails_6_en","",20728,], +["features.roomdetails.impl_RoomDetails_7_en","",20728,], +["features.roomdetails.impl_RoomDetails_8_en","",20728,], +["features.roomdetails.impl_RoomDetails_9_en","",20728,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_0_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_0_en",20728,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_1_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_1_en",20728,], +["features.roomdirectory.impl.root_RoomDirectoryView_Day_2_en","features.roomdirectory.impl.root_RoomDirectoryView_Night_2_en",20728,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_0_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_0_en",20728,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_1_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_1_en",20728,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_2_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_2_en",20728,], +["features.roomdetails.impl.invite_RoomInviteMembersView_Day_3_en","features.roomdetails.impl.invite_RoomInviteMembersView_Night_3_en",20728,], +["features.home.impl.components_RoomListContentView_Day_0_en","features.home.impl.components_RoomListContentView_Night_0_en",20728,], +["features.home.impl.components_RoomListContentView_Day_1_en","features.home.impl.components_RoomListContentView_Night_1_en",20728,], ["features.home.impl.components_RoomListContentView_Day_2_en","features.home.impl.components_RoomListContentView_Night_2_en",0,], -["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20722,], -["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20722,], -["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20722,], -["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20722,], -["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20722,], -["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20722,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20722,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20722,], -["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20722,], -["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20722,], -["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20722,], +["features.home.impl.components_RoomListContentView_Day_3_en","features.home.impl.components_RoomListContentView_Night_3_en",20728,], +["features.home.impl.components_RoomListContentView_Day_4_en","features.home.impl.components_RoomListContentView_Night_4_en",20728,], +["features.home.impl.components_RoomListContentView_Day_5_en","features.home.impl.components_RoomListContentView_Night_5_en",20728,], +["features.home.impl.roomlist_RoomListContextMenu_Day_0_en","features.home.impl.roomlist_RoomListContextMenu_Night_0_en",20728,], +["features.home.impl.roomlist_RoomListContextMenu_Day_1_en","features.home.impl.roomlist_RoomListContextMenu_Night_1_en",20728,], +["features.home.impl.roomlist_RoomListContextMenu_Day_2_en","features.home.impl.roomlist_RoomListContextMenu_Night_2_en",20728,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_0_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_0_en",20728,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_1_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_1_en",20728,], +["features.home.impl.roomlist_RoomListDeclineInviteMenu_Day_2_en","features.home.impl.roomlist_RoomListDeclineInviteMenu_Night_2_en",20728,], +["features.home.impl.filters_RoomListFiltersView_Day_0_en","features.home.impl.filters_RoomListFiltersView_Night_0_en",20728,], +["features.home.impl.filters_RoomListFiltersView_Day_1_en","features.home.impl.filters_RoomListFiltersView_Night_1_en",20728,], ["features.home.impl.search_RoomListSearchContent_Day_0_en","features.home.impl.search_RoomListSearchContent_Night_0_en",0,], -["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20722,], -["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20722,], -["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20722,], +["features.home.impl.search_RoomListSearchContent_Day_1_en","features.home.impl.search_RoomListSearchContent_Night_1_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_0_en","features.roomdetails.impl.members_RoomMemberListView_Night_0_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_1_en","features.roomdetails.impl.members_RoomMemberListView_Night_1_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_2_en","features.roomdetails.impl.members_RoomMemberListView_Night_2_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_3_en","features.roomdetails.impl.members_RoomMemberListView_Night_3_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_4_en","features.roomdetails.impl.members_RoomMemberListView_Night_4_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_5_en","features.roomdetails.impl.members_RoomMemberListView_Night_5_en",20728,], +["features.roomdetails.impl.members_RoomMemberListView_Day_6_en","features.roomdetails.impl.members_RoomMemberListView_Night_6_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_0_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_0_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_1_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_1_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_2_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_2_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_3_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_3_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_4_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_4_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_5_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_5_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_6_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_6_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_7_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_7_en",20728,], +["features.roommembermoderation.impl_RoomMemberModerationView_Day_8_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_8_en",20728,], ["features.roommembermoderation.impl_RoomMemberModerationView_Day_9_en","features.roommembermoderation.impl_RoomMemberModerationView_Night_9_en",0,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20722,], -["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20722,], -["libraries.roomselect.impl_RoomSelectView_Day_6_en","libraries.roomselect.impl_RoomSelectView_Night_6_en",20722,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsOption_Night_0_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_0_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_1_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_1_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_2_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_2_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_3_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_3_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_4_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_4_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_5_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_5_en",20728,], +["features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Day_6_en","features.roomdetails.impl.notificationsettings_RoomNotificationSettingsView_Night_6_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_0_en","libraries.roomselect.impl_RoomSelectView_Night_0_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_1_en","libraries.roomselect.impl_RoomSelectView_Night_1_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_2_en","libraries.roomselect.impl_RoomSelectView_Night_2_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_3_en","libraries.roomselect.impl_RoomSelectView_Night_3_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_4_en","libraries.roomselect.impl_RoomSelectView_Night_4_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_5_en","libraries.roomselect.impl_RoomSelectView_Night_5_en",20728,], +["libraries.roomselect.impl_RoomSelectView_Day_6_en","libraries.roomselect.impl_RoomSelectView_Night_6_en",20728,], ["features.home.impl.components_RoomSummaryPlaceholderRow_Day_0_en","features.home.impl.components_RoomSummaryPlaceholderRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_0_en","features.home.impl.components_RoomSummaryRow_Night_0_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_10_en","features.home.impl.components_RoomSummaryRow_Night_10_en",0,], @@ -1226,7 +1231,7 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_27_en","features.home.impl.components_RoomSummaryRow_Night_27_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_28_en","features.home.impl.components_RoomSummaryRow_Night_28_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_29_en","features.home.impl.components_RoomSummaryRow_Night_29_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20722,], +["features.home.impl.components_RoomSummaryRow_Day_2_en","features.home.impl.components_RoomSummaryRow_Night_2_en",20728,], ["features.home.impl.components_RoomSummaryRow_Day_30_en","features.home.impl.components_RoomSummaryRow_Night_30_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_31_en","features.home.impl.components_RoomSummaryRow_Night_31_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_32_en","features.home.impl.components_RoomSummaryRow_Night_32_en",0,], @@ -1239,15 +1244,15 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_39_en","features.home.impl.components_RoomSummaryRow_Night_39_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_3_en","features.home.impl.components_RoomSummaryRow_Night_3_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_40_en","features.home.impl.components_RoomSummaryRow_Night_40_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_41_en","features.home.impl.components_RoomSummaryRow_Night_41_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_42_en","features.home.impl.components_RoomSummaryRow_Night_42_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_43_en","features.home.impl.components_RoomSummaryRow_Night_43_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_44_en","features.home.impl.components_RoomSummaryRow_Night_44_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_45_en","features.home.impl.components_RoomSummaryRow_Night_45_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_46_en","features.home.impl.components_RoomSummaryRow_Night_46_en",20724,], -["features.home.impl.components_RoomSummaryRow_Day_47_en","features.home.impl.components_RoomSummaryRow_Night_47_en",20724,], +["features.home.impl.components_RoomSummaryRow_Day_41_en","features.home.impl.components_RoomSummaryRow_Night_41_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_42_en","features.home.impl.components_RoomSummaryRow_Night_42_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_43_en","features.home.impl.components_RoomSummaryRow_Night_43_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_44_en","features.home.impl.components_RoomSummaryRow_Night_44_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_45_en","features.home.impl.components_RoomSummaryRow_Night_45_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_46_en","features.home.impl.components_RoomSummaryRow_Night_46_en",20728,], +["features.home.impl.components_RoomSummaryRow_Day_47_en","features.home.impl.components_RoomSummaryRow_Night_47_en",20728,], ["features.home.impl.components_RoomSummaryRow_Day_48_en","features.home.impl.components_RoomSummaryRow_Night_48_en",0,], -["features.home.impl.components_RoomSummaryRow_Day_49_en","features.home.impl.components_RoomSummaryRow_Night_49_en",20724,], +["features.home.impl.components_RoomSummaryRow_Day_49_en","features.home.impl.components_RoomSummaryRow_Night_49_en",20728,], ["features.home.impl.components_RoomSummaryRow_Day_4_en","features.home.impl.components_RoomSummaryRow_Night_4_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_50_en","features.home.impl.components_RoomSummaryRow_Night_50_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_5_en","features.home.impl.components_RoomSummaryRow_Night_5_en",0,], @@ -1255,117 +1260,117 @@ export const screenshots = [ ["features.home.impl.components_RoomSummaryRow_Day_7_en","features.home.impl.components_RoomSummaryRow_Night_7_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_8_en","features.home.impl.components_RoomSummaryRow_Night_8_en",0,], ["features.home.impl.components_RoomSummaryRow_Day_9_en","features.home.impl.components_RoomSummaryRow_Night_9_en",0,], -["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20722,], +["appnav.root_RootView_Day_0_en","appnav.root_RootView_Night_0_en",20728,], ["features.login.impl.screens.classic.root_RootView_Day_0_en","features.login.impl.screens.classic.root_RootView_Night_0_en",0,], -["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20722,], -["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20722,], +["appnav.root_RootView_Day_1_en","appnav.root_RootView_Night_1_en",20728,], +["appnav.root_RootView_Day_2_en","appnav.root_RootView_Night_2_en",20728,], ["appicon.enterprise_RoundIcon_en","",0,], ["appicon.element_RoundIcon_en","",0,], ["libraries.designsystem.atomic.atoms_RoundedIconAtom_Day_0_en","libraries.designsystem.atomic.atoms_RoundedIconAtom_Night_0_en",0,], -["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20722,], -["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20722,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20722,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20722,], -["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20722,], +["features.verifysession.impl.emoji_SasEmojis_Day_0_en","features.verifysession.impl.emoji_SasEmojis_Night_0_en",20728,], +["libraries.designsystem.components.dialogs_SaveChangesDialog_Day_0_en","libraries.designsystem.components.dialogs_SaveChangesDialog_Night_0_en",20728,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_0_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_1_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_1_en",20728,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_2_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_2_en",20728,], +["features.linknewdevice.impl.screens.scan_ScanQrCodeView_Day_3_en","features.linknewdevice.impl.screens.scan_ScanQrCodeView_Night_3_en",20728,], ["libraries.designsystem.theme.components_SearchBarActiveNoneQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithContent_Search_views_en","",0,], -["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20722,], +["libraries.designsystem.theme.components_SearchBarActiveWithNoResults_Search_views_en","",20728,], ["libraries.designsystem.theme.components_SearchBarActiveWithQueryNoBackButton_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarActiveWithQuery_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchBarInactive_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsDark_Search_views_en","",0,], ["libraries.designsystem.theme.components_SearchFieldsLight_Search_views_en","",0,], -["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20722,], -["features.startchat.impl.components_SearchSingleUserResultItem_en","",20722,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20722,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20722,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20722,], -["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20722,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20722,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20722,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20722,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20722,], -["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20722,], -["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20722,], -["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20722,], -["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20722,], -["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20722,], +["features.startchat.impl.components_SearchMultipleUsersResultItem_en","",20728,], +["features.startchat.impl.components_SearchSingleUserResultItem_en","",20728,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_0_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_0_en",20728,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_1_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_1_en",20728,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_2_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_2_en",20728,], +["features.securebackup.impl.disable_SecureBackupDisableView_Day_3_en","features.securebackup.impl.disable_SecureBackupDisableView_Night_3_en",20728,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_0_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_0_en",20728,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_1_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_1_en",20728,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_2_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_2_en",20728,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_3_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_3_en",20728,], +["features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Day_4_en","features.securebackup.impl.enter_SecureBackupEnterRecoveryKeyView_Night_4_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_0_en","features.securebackup.impl.root_SecureBackupRootView_Night_0_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_10_en","features.securebackup.impl.root_SecureBackupRootView_Night_10_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_11_en","features.securebackup.impl.root_SecureBackupRootView_Night_11_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_12_en","features.securebackup.impl.root_SecureBackupRootView_Night_12_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_13_en","features.securebackup.impl.root_SecureBackupRootView_Night_13_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_14_en","features.securebackup.impl.root_SecureBackupRootView_Night_14_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_15_en","features.securebackup.impl.root_SecureBackupRootView_Night_15_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_16_en","features.securebackup.impl.root_SecureBackupRootView_Night_16_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_17_en","features.securebackup.impl.root_SecureBackupRootView_Night_17_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_1_en","features.securebackup.impl.root_SecureBackupRootView_Night_1_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_2_en","features.securebackup.impl.root_SecureBackupRootView_Night_2_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_3_en","features.securebackup.impl.root_SecureBackupRootView_Night_3_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_4_en","features.securebackup.impl.root_SecureBackupRootView_Night_4_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_5_en","features.securebackup.impl.root_SecureBackupRootView_Night_5_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_6_en","features.securebackup.impl.root_SecureBackupRootView_Night_6_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_7_en","features.securebackup.impl.root_SecureBackupRootView_Night_7_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_8_en","features.securebackup.impl.root_SecureBackupRootView_Night_8_en",20728,], +["features.securebackup.impl.root_SecureBackupRootView_Day_9_en","features.securebackup.impl.root_SecureBackupRootView_Night_9_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_0_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_1_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_2_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_3_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_4_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupViewChange_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupViewChange_Night_5_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_0_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_0_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_1_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_1_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_2_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_2_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_3_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_3_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_4_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_4_en",20728,], +["features.securebackup.impl.setup_SecureBackupSetupView_Day_5_en","features.securebackup.impl.setup_SecureBackupSetupView_Night_5_en",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_0_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_10_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_11_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_12_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_13_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_14_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_15_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_16_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_17_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_18_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_19_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_1_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_20_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_21_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_22_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_23_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_2_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_3_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_4_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_5_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_6_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_7_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_8_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewDark_9_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_0_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_10_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_11_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_12_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_13_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_14_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_15_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_16_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_17_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_18_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_19_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_1_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_20_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_21_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_22_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_23_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_2_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_3_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_4_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_5_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_6_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_7_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_8_en","",20728,], +["features.securityandprivacy.impl.root_SecurityAndPrivacyViewLight_9_en","",20728,], +["features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Day_0_en","features.createroom.impl.configureroom_SelectParentSpaceBottomSheet_Night_0_en",20728,], ["libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_SelectedIndicatorAtom_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_0_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_0_en",0,], ["libraries.matrix.ui.components_SelectedRoomRtl_Day_1_en","libraries.matrix.ui.components_SelectedRoomRtl_Night_1_en",0,], @@ -1380,11 +1385,11 @@ export const screenshots = [ ["libraries.matrix.ui.components_SelectedUsersRowList_Day_0_en","libraries.matrix.ui.components_SelectedUsersRowList_Night_0_en",0,], ["libraries.textcomposer.components_SendButtonIcon_Day_0_en","libraries.textcomposer.components_SendButtonIcon_Night_0_en",0,], ["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_0_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_0_en",0,], -["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_1_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_1_en",20722,], -["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_2_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_2_en",20722,], -["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_3_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_3_en",20722,], -["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_4_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_4_en",20722,], -["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_5_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_5_en",20722,], +["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_1_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_1_en",20728,], +["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_2_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_2_en",20728,], +["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_3_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_3_en",20728,], +["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_4_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_4_en",20728,], +["features.messages.impl.timeline.sendfailure_SendFailureDialogView_Day_5_en","features.messages.impl.timeline.sendfailure_SendFailureDialogView_Night_5_en",20728,], ["libraries.matrix.ui.messages.sender_SenderName_Day_0_en","libraries.matrix.ui.messages.sender_SenderName_Night_0_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_1_en","libraries.matrix.ui.messages.sender_SenderName_Night_1_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_2_en","libraries.matrix.ui.messages.sender_SenderName_Night_2_en",0,], @@ -1392,39 +1397,39 @@ export const screenshots = [ ["libraries.matrix.ui.messages.sender_SenderName_Day_4_en","libraries.matrix.ui.messages.sender_SenderName_Night_4_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_5_en","libraries.matrix.ui.messages.sender_SenderName_Night_5_en",0,], ["libraries.matrix.ui.messages.sender_SenderName_Day_6_en","libraries.matrix.ui.messages.sender_SenderName_Night_6_en",0,], -["features.verifysession.impl.incoming.ui_SessionDetailsView_Day_0_en","features.verifysession.impl.incoming.ui_SessionDetailsView_Night_0_en",20722,], -["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20722,], -["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20722,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20722,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20722,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20722,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20722,], -["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20722,], -["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20722,], -["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20722,], -["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20722,], -["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20722,], -["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20722,], -["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20722,], -["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20722,], -["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20722,], -["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20722,], -["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20722,], +["features.home.impl.components_SetUpRecoveryKeyBanner_Day_0_en","features.home.impl.components_SetUpRecoveryKeyBanner_Night_0_en",20728,], +["features.lockscreen.impl.setup.biometric_SetupBiometricView_Day_0_en","features.lockscreen.impl.setup.biometric_SetupBiometricView_Night_0_en",20728,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_0_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_0_en",20728,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_1_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_1_en",20728,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_2_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_2_en",20728,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_3_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_3_en",20728,], +["features.lockscreen.impl.setup.pin_SetupPinView_Day_4_en","features.lockscreen.impl.setup.pin_SetupPinView_Night_4_en",20728,], +["features.location.impl.share_ShareLocationView_Day_0_en","features.location.impl.share_ShareLocationView_Night_0_en",20728,], +["features.location.impl.share_ShareLocationView_Day_1_en","features.location.impl.share_ShareLocationView_Night_1_en",20728,], +["features.location.impl.share_ShareLocationView_Day_2_en","features.location.impl.share_ShareLocationView_Night_2_en",20728,], +["features.location.impl.share_ShareLocationView_Day_3_en","features.location.impl.share_ShareLocationView_Night_3_en",20728,], +["features.location.impl.share_ShareLocationView_Day_4_en","features.location.impl.share_ShareLocationView_Night_4_en",20728,], +["features.location.impl.share_ShareLocationView_Day_5_en","features.location.impl.share_ShareLocationView_Night_5_en",20728,], +["features.location.impl.share_ShareLocationView_Day_6_en","features.location.impl.share_ShareLocationView_Night_6_en",20728,], +["features.location.impl.share_ShareLocationView_Day_7_en","features.location.impl.share_ShareLocationView_Night_7_en",20728,], +["features.location.impl.share_ShareLocationView_Day_8_en","features.location.impl.share_ShareLocationView_Night_8_en",20728,], +["features.location.impl.share_ShareLocationView_Day_9_en","features.location.impl.share_ShareLocationView_Night_9_en",20728,], ["features.share.impl_ShareView_Day_0_en","features.share.impl_ShareView_Night_0_en",0,], ["features.share.impl_ShareView_Day_1_en","features.share.impl_ShareView_Night_1_en",0,], ["features.share.impl_ShareView_Day_2_en","features.share.impl_ShareView_Night_2_en",0,], -["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20722,], -["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20722,], -["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20722,], -["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20722,], -["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20722,], -["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20722,], -["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20722,], -["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20722,], -["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20722,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20722,], -["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20722,], -["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20722,], +["features.share.impl_ShareView_Day_3_en","features.share.impl_ShareView_Night_3_en",20728,], +["features.location.impl.show_ShowLocationView_Day_0_en","features.location.impl.show_ShowLocationView_Night_0_en",20728,], +["features.location.impl.show_ShowLocationView_Day_1_en","features.location.impl.show_ShowLocationView_Night_1_en",20728,], +["features.location.impl.show_ShowLocationView_Day_2_en","features.location.impl.show_ShowLocationView_Night_2_en",20728,], +["features.location.impl.show_ShowLocationView_Day_3_en","features.location.impl.show_ShowLocationView_Night_3_en",20728,], +["features.location.impl.show_ShowLocationView_Day_4_en","features.location.impl.show_ShowLocationView_Night_4_en",20728,], +["features.location.impl.show_ShowLocationView_Day_5_en","features.location.impl.show_ShowLocationView_Night_5_en",20728,], +["features.location.impl.show_ShowLocationView_Day_6_en","features.location.impl.show_ShowLocationView_Night_6_en",20728,], +["features.location.impl.show_ShowLocationView_Day_7_en","features.location.impl.show_ShowLocationView_Night_7_en",20728,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_0_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_0_en",20728,], +["features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Day_1_en","features.linknewdevice.impl.screens.qrcode_ShowQrCodeView_Night_1_en",20728,], +["features.signedout.impl_SignedOutView_Day_0_en","features.signedout.impl_SignedOutView_Night_0_en",20728,], +["features.signedout.impl_SignedOutView_Day_1_en","features.signedout.impl_SignedOutView_Night_1_en",0,], ["libraries.designsystem.components_SimpleModalBottomSheet_Day_0_en","libraries.designsystem.components_SimpleModalBottomSheet_Night_0_en",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialogContent_Dialogs_en","",0,], ["libraries.designsystem.components.dialogs_SingleSelectionDialog_Day_0_en","libraries.designsystem.components.dialogs_SingleSelectionDialog_Night_0_en",0,], @@ -1435,107 +1440,107 @@ export const screenshots = [ ["libraries.designsystem.components.list_SingleSelectionListItem_Single_selection_List_item_-_no_selection_List_items_en","",0,], ["libraries.emoji.impl.picker_SkinTonePicker_Day_0_en","libraries.emoji.impl.picker_SkinTonePicker_Night_0_en",0,], ["libraries.designsystem.theme.components_Sliders_Sliders_en","",0,], -["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20722,], +["features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Day_0_en","features.login.impl.dialogs_SlidingSyncNotSupportedDialog_Night_0_en",20728,], ["libraries.designsystem.theme.components_SnackbarWithActionAndCloseButton_Snackbar_with_action_and_close_button_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLineAndCloseButton_Snackbar_with_action_and_close_button_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithActionOnNewLine_Snackbar_with_action_on_new_line_Snackbars_en","",0,], ["libraries.designsystem.theme.components_SnackbarWithAction_Snackbar_with_action_Snackbars_en","",0,], ["libraries.designsystem.theme.components_Snackbar_Snackbar_Snackbars_en","",0,], ["libraries.designsystem.components.avatar.internal_SpaceAvatar_Avatars_en","",0,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20722,], -["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20722,], -["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20722,], -["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20722,], -["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20722,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_0_en","features.home.impl.spacefilters_SpaceFiltersView_Night_0_en",20728,], +["features.home.impl.spacefilters_SpaceFiltersView_Day_1_en","features.home.impl.spacefilters_SpaceFiltersView_Night_1_en",20728,], +["libraries.matrix.ui.components_SpaceHeaderRootView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderRootView_Night_0_en",20728,], +["libraries.matrix.ui.components_SpaceHeaderView_Day_0_en","libraries.matrix.ui.components_SpaceHeaderView_Night_0_en",20728,], +["libraries.matrix.ui.components_SpaceInfoRow_Day_0_en","libraries.matrix.ui.components_SpaceInfoRow_Night_0_en",20728,], ["libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Day_0_en","libraries.matrix.ui.components_SpaceMembersViewNoHeroes_Night_0_en",0,], ["libraries.matrix.ui.components_SpaceMembersView_Day_0_en","libraries.matrix.ui.components_SpaceMembersView_Night_0_en",0,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20722,], -["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20722,], -["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20722,], -["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20722,], -["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20722,], -["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20722,], -["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20722,], -["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20722,], -["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20722,], -["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20722,], -["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20722,], -["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20722,], -["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20722,], -["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20722,], -["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20722,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_0_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_0_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_1_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_1_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_2_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_2_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_3_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_3_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_4_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_4_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_5_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_5_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_6_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_6_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_7_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_7_en",20728,], +["libraries.matrix.ui.components_SpaceRoomItemView_Day_8_en","libraries.matrix.ui.components_SpaceRoomItemView_Night_8_en",20728,], +["features.space.impl.settings_SpaceSettingsView_Day_0_en","features.space.impl.settings_SpaceSettingsView_Night_0_en",20728,], +["features.space.impl.settings_SpaceSettingsView_Day_1_en","features.space.impl.settings_SpaceSettingsView_Night_1_en",20728,], +["features.space.impl.settings_SpaceSettingsView_Day_2_en","features.space.impl.settings_SpaceSettingsView_Night_2_en",20728,], +["features.space.impl.settings_SpaceSettingsView_Day_3_en","features.space.impl.settings_SpaceSettingsView_Night_3_en",20728,], +["features.space.impl.root_SpaceView_Day_0_en","features.space.impl.root_SpaceView_Night_0_en",20728,], +["features.space.impl.root_SpaceView_Day_1_en","features.space.impl.root_SpaceView_Night_1_en",20728,], +["features.space.impl.root_SpaceView_Day_2_en","features.space.impl.root_SpaceView_Night_2_en",20728,], +["features.space.impl.root_SpaceView_Day_3_en","features.space.impl.root_SpaceView_Night_3_en",20728,], +["features.space.impl.root_SpaceView_Day_4_en","features.space.impl.root_SpaceView_Night_4_en",20728,], +["features.space.impl.root_SpaceView_Day_5_en","features.space.impl.root_SpaceView_Night_5_en",20728,], +["features.space.impl.root_SpaceView_Day_6_en","features.space.impl.root_SpaceView_Night_6_en",20728,], +["features.space.impl.root_SpaceView_Day_7_en","features.space.impl.root_SpaceView_Night_7_en",20728,], +["features.space.impl.root_SpaceView_Day_8_en","features.space.impl.root_SpaceView_Night_8_en",20728,], ["libraries.designsystem.modifiers_SquareSizeModifierInsideSquare_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeHeight_en","",0,], ["libraries.designsystem.modifiers_SquareSizeModifierLargeWidth_en","",0,], -["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20722,], -["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20722,], -["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20722,], -["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20722,], -["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20722,], -["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20722,], +["features.startchat.impl.root_StartChatView_Day_0_en","features.startchat.impl.root_StartChatView_Night_0_en",20728,], +["features.startchat.impl.root_StartChatView_Day_1_en","features.startchat.impl.root_StartChatView_Night_1_en",20728,], +["features.startchat.impl.root_StartChatView_Day_2_en","features.startchat.impl.root_StartChatView_Night_2_en",20728,], +["features.startchat.impl.root_StartChatView_Day_3_en","features.startchat.impl.root_StartChatView_Night_3_en",20728,], +["features.startchat.impl.root_StartChatView_Day_4_en","features.startchat.impl.root_StartChatView_Night_4_en",20728,], +["features.location.api.internal_StaticMapPlaceholder_Day_0_en","features.location.api.internal_StaticMapPlaceholder_Night_0_en",20728,], ["features.location.api_StaticMapView_Day_0_en","features.location.api_StaticMapView_Night_0_en",0,], -["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20722,], +["features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Day_0_en","features.messages.impl.messagecomposer.suggestions_SuggestionsPickerView_Night_0_en",20728,], ["libraries.designsystem.atomic.pages_SunsetPage_Day_0_en","libraries.designsystem.atomic.pages_SunsetPage_Night_0_en",0,], ["libraries.designsystem.components.button_SuperButton_Day_0_en","libraries.designsystem.components.button_SuperButton_Night_0_en",0,], ["libraries.designsystem.theme.components_Surface_en","",0,], ["libraries.designsystem.theme.components_Switch_Toggles_en","",0,], -["appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en","appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en",20722,], -["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20722,], +["appnav.loggedin_SyncStateViewServerUnreachable_Day_0_en","appnav.loggedin_SyncStateViewServerUnreachable_Night_0_en",20728,], +["appnav.loggedin_SyncStateView_Day_0_en","appnav.loggedin_SyncStateView_Night_0_en",20728,], ["libraries.designsystem.components.avatar.internal_TextAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TextButtonLargeLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonLarge_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMediumLowPadding_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonMedium_Buttons_en","",0,], ["libraries.designsystem.theme.components_TextButtonSmall_Buttons_en","",0,], -["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20722,], -["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20722,], -["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20722,], -["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20722,], -["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20722,], -["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20722,], -["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20722,], -["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20722,], -["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20722,], -["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20722,], -["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20722,], -["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20722,], -["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20722,], -["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20722,], -["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20722,], -["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20722,], +["libraries.textcomposer_TextComposerAddCaption_Day_0_en","libraries.textcomposer_TextComposerAddCaption_Night_0_en",20728,], +["libraries.textcomposer_TextComposerCaption_Day_0_en","libraries.textcomposer_TextComposerCaption_Night_0_en",20728,], +["libraries.textcomposer_TextComposerEditCaption_Day_0_en","libraries.textcomposer_TextComposerEditCaption_Night_0_en",20728,], +["libraries.textcomposer_TextComposerEditNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerEditNotEncrypted_Night_0_en",20728,], +["libraries.textcomposer_TextComposerEdit_Day_0_en","libraries.textcomposer_TextComposerEdit_Night_0_en",20728,], +["libraries.textcomposer_TextComposerFormattingNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerFormattingNotEncrypted_Night_0_en",20728,], +["libraries.textcomposer_TextComposerFormatting_Day_0_en","libraries.textcomposer_TextComposerFormatting_Night_0_en",20728,], +["libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLinkWithoutText_Night_0_en",20728,], +["libraries.textcomposer_TextComposerLinkDialogCreateLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogCreateLink_Night_0_en",20728,], +["libraries.textcomposer_TextComposerLinkDialogEditLink_Day_0_en","libraries.textcomposer_TextComposerLinkDialogEditLink_Night_0_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_0_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_10_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_10_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_11_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_11_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_1_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_1_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_2_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_2_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_3_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_3_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_4_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_4_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_5_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_5_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_6_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_6_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_7_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_7_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_8_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_8_en",20728,], +["libraries.textcomposer_TextComposerReplyNotEncrypted_Day_9_en","libraries.textcomposer_TextComposerReplyNotEncrypted_Night_9_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_0_en","libraries.textcomposer_TextComposerReply_Night_0_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_10_en","libraries.textcomposer_TextComposerReply_Night_10_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_11_en","libraries.textcomposer_TextComposerReply_Night_11_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_1_en","libraries.textcomposer_TextComposerReply_Night_1_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_2_en","libraries.textcomposer_TextComposerReply_Night_2_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_3_en","libraries.textcomposer_TextComposerReply_Night_3_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_4_en","libraries.textcomposer_TextComposerReply_Night_4_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_5_en","libraries.textcomposer_TextComposerReply_Night_5_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_6_en","libraries.textcomposer_TextComposerReply_Night_6_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_7_en","libraries.textcomposer_TextComposerReply_Night_7_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_8_en","libraries.textcomposer_TextComposerReply_Night_8_en",20728,], +["libraries.textcomposer_TextComposerReply_Day_9_en","libraries.textcomposer_TextComposerReply_Night_9_en",20728,], +["libraries.textcomposer_TextComposerScaledDensityWithReply_en","",20728,], +["libraries.textcomposer_TextComposerSimpleNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerSimpleNotEncrypted_Night_0_en",20728,], +["libraries.textcomposer_TextComposerSimple_Day_0_en","libraries.textcomposer_TextComposerSimple_Night_0_en",20728,], +["libraries.textcomposer_TextComposerVoiceNotEncrypted_Day_0_en","libraries.textcomposer_TextComposerVoiceNotEncrypted_Night_0_en",20728,], ["libraries.textcomposer_TextComposerVoice_Day_0_en","libraries.textcomposer_TextComposerVoice_Night_0_en",0,], ["libraries.designsystem.theme.components_TextDark_Text_en","",0,], -["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20722,], -["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20722,], +["libraries.designsystem.components.dialogs_TextFieldDialogWithError_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialogWithError_Night_0_en",20728,], +["libraries.designsystem.components.dialogs_TextFieldDialog_Day_0_en","libraries.designsystem.components.dialogs_TextFieldDialog_Night_0_en",20728,], ["libraries.designsystem.components.list_TextFieldListItemEmpty_Text_field_List_item_-_empty_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItemTextFieldValue_Text_field_List_item_-_textfieldvalue_List_items_en","",0,], ["libraries.designsystem.components.list_TextFieldListItem_Text_field_List_item_-_text_List_items_en","",0,], @@ -1548,53 +1553,53 @@ export const screenshots = [ ["libraries.textcomposer.components_TextFormatting_Day_0_en","libraries.textcomposer.components_TextFormatting_Night_0_en",0,], ["libraries.designsystem.theme.components_TextLight_Text_en","",0,], ["features.messages.impl.threads.list_ThreadListItemRow_Day_0_en","features.messages.impl.threads.list_ThreadListItemRow_Night_0_en",0,], -["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20722,], -["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20722,], +["features.messages.impl.timeline.components_ThreadSummaryView_Day_0_en","features.messages.impl.timeline.components_ThreadSummaryView_Night_0_en",20728,], +["features.messages.impl.topbars_ThreadTopBar_Day_0_en","features.messages.impl.topbars_ThreadTopBar_Night_0_en",20728,], ["features.messages.impl.threads.list_ThreadsListView_Day_0_en","features.messages.impl.threads.list_ThreadsListView_Night_0_en",0,], -["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20722,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20722,], -["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20722,], +["libraries.designsystem.theme.components.previews_TimePickerHorizontal_DateTime_pickers_en","",20728,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalDark_DateTime_pickers_en","",20728,], +["libraries.designsystem.theme.components.previews_TimePickerVerticalLight_DateTime_pickers_en","",20728,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_0_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_1_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_2_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20722,], -["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20722,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_3_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_3_en",20728,], +["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_4_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_4_en",20728,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_5_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_6_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineEventTimestampView_Day_7_en","features.messages.impl.timeline.components_TimelineEventTimestampView_Night_7_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAttachmentsListView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemAttachmentsListView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAttachmentsListView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemAttachmentsListView_Night_1_en",0,], -["features.messages.impl.timeline.components_TimelineItemAttachmentsViewScanningContentFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemAttachmentsViewScanningContentFailed_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemAttachmentsViewScanningContentFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemAttachmentsViewScanningContentFailed_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemAudioViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemAudioViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemAudioView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemAudioView_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemCallNotifyView_Day_0_en","features.messages.impl.timeline.components_TimelineItemCallNotifyView_Night_0_en",20728,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_0_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Day_1_en","features.messages.impl.timeline.components.virtual_TimelineItemDaySeparatorView_Night_1_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20722,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_0_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_1_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_2_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_3_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_4_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_5_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_6_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_6_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_7_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemEncryptedView_Night_8_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowDisambiguated_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowForDirectRoom_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowLongSenderName_en","",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowRtlContent_en","",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowRtlContent_en","",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowShield_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowShield_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_3_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_4_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_5_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowTimestamp_Night_7_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowUtd_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowUtd_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_2_en",0,], @@ -1604,29 +1609,29 @@ export const screenshots = [ ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_6_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_7_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithGallery_Night_8_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithManyReactions_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_1_en",0,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithRR_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",20724,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",20724,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyInformative_Night_1_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReplyOther_Night_1_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_10_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_10_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_11_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_11_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_1_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_1_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_2_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_2_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_3_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_3_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_4_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_4_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_5_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_5_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_6_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_6_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",20722,], -["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_7_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_7_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_8_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_8_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Day_9_en","features.messages.impl.timeline.components_TimelineItemEventRowWithReply_Night_9_en",20728,], +["features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRowWithThreadSummary_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemEventRow_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20722,], +["features.messages.impl.timeline.components_TimelineItemEventTimestampBelow_en","",20728,], ["features.messages.impl.timeline.components_TimelineItemFileViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemFileViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemFileView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemFileView_Night_1_en",0,], @@ -1642,31 +1647,31 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_7_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_7_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_8_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemGalleryView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemGalleryView_Night_9_en",0,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentCollapse_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Day_0_en","features.messages.impl.timeline.components_TimelineItemGroupedEventsRowContentExpanded_Night_0_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageViewHideMediaContent_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemImageViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemImageViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_1_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_2_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemImageView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemImageView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemInformativeView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemInformativeView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20722,], +["features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLegacyCallInviteView_Night_0_en",20728,], ["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_0_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20722,], -["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20722,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_1_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_2_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_3_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemLocationView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemLocationView_Night_4_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_0_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_1_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_2_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_2_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemPollView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemPollView_Night_3_en",20728,], +["features.messages.impl.timeline.components_TimelineItemReactionsLayout_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsLayout_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewFew_Night_0_en",0,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewIncoming_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsViewOutgoing_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemReactionsView_Day_0_en","features.messages.impl.timeline.components_TimelineItemReactionsView_Night_0_en",0,], -["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20722,], +["features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemReadMarkerView_Night_0_en",20728,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_0_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_0_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_1_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_1_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_2_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_2_en",0,], @@ -1675,13 +1680,13 @@ export const screenshots = [ ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_5_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_5_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_6_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_6_en",0,], ["features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Day_7_en","features.messages.impl.timeline.components.receipt_TimelineItemReadReceiptView_Night_7_en",0,], -["features.messages.impl.timeline.components_TimelineItemRedactedMessagesGroup_Day_0_en","features.messages.impl.timeline.components_TimelineItemRedactedMessagesGroup_Night_0_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20722,], -["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Night_0_en",20722,], -["features.messages.impl.timeline.components_TimelineItemScanningContentWithRepliesFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentWithRepliesFailed_Night_0_en",20722,], +["features.messages.impl.timeline.components_TimelineItemRedactedMessagesGroup_Day_0_en","features.messages.impl.timeline.components_TimelineItemRedactedMessagesGroup_Night_0_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemRedactedView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemRedactedView_Night_0_en",20728,], +["features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineItemRoomBeginningView_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentFailed_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentNotFound_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentWithInvalidReplies_Night_0_en",20728,], +["features.messages.impl.timeline.components_TimelineItemScanningContentWithRepliesFailed_Day_0_en","features.messages.impl.timeline.components_TimelineItemScanningContentWithRepliesFailed_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemStateEventRow_Day_0_en","features.messages.impl.timeline.components_TimelineItemStateEventRow_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemStateView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemStateView_Night_0_en",0,], ["features.messages.impl.timeline.components_TimelineItemStickerViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemStickerViewScanningContent_Night_0_en",0,], @@ -1697,8 +1702,8 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_3_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_3_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_4_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_4_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemTextView_Day_5_en","features.messages.impl.timeline.components.event_TimelineItemTextView_Night_5_en",0,], -["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20722,], -["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20722,], +["features.messages.impl.timeline.components.event_TimelineItemUnknownView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemUnknownView_Night_0_en",20728,], +["features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoViewHideMediaContent_Night_0_en",20728,], ["features.messages.impl.timeline.components_TimelineItemVideoViewScanningContent_Day_0_en","features.messages.impl.timeline.components_TimelineItemVideoViewScanningContent_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_0_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_0_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVideoView_Day_1_en","features.messages.impl.timeline.components.event_TimelineItemVideoView_Night_1_en",0,], @@ -1723,109 +1728,108 @@ export const screenshots = [ ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_8_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_8_en",0,], ["features.messages.impl.timeline.components.event_TimelineItemVoiceView_Day_9_en","features.messages.impl.timeline.components.event_TimelineItemVoiceView_Night_9_en",0,], ["features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Day_0_en","features.messages.impl.timeline.components.virtual_TimelineLoadingMoreIndicator_Night_0_en",0,], -["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20722,], +["features.messages.impl.timeline_TimelineViewMessageShield_Day_0_en","features.messages.impl.timeline_TimelineViewMessageShield_Night_0_en",20728,], ["features.messages.impl.timeline_TimelineViewWithReadMarkerBothIndicators_Day_0_en","features.messages.impl.timeline_TimelineViewWithReadMarkerBothIndicators_Night_0_en",0,], ["features.messages.impl.timeline_TimelineViewWithReadMarkerJumpToUnreadIndicatorOnly_Day_0_en","features.messages.impl.timeline_TimelineViewWithReadMarkerJumpToUnreadIndicatorOnly_Night_0_en",0,], -["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20722,], -["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20722,], +["features.messages.impl.timeline_TimelineView_Day_0_en","features.messages.impl.timeline_TimelineView_Night_0_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_10_en","features.messages.impl.timeline_TimelineView_Night_10_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_11_en","features.messages.impl.timeline_TimelineView_Night_11_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_12_en","features.messages.impl.timeline_TimelineView_Night_12_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_13_en","features.messages.impl.timeline_TimelineView_Night_13_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_14_en","features.messages.impl.timeline_TimelineView_Night_14_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_15_en","features.messages.impl.timeline_TimelineView_Night_15_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_16_en","features.messages.impl.timeline_TimelineView_Night_16_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_17_en","features.messages.impl.timeline_TimelineView_Night_17_en",20728,], +["features.messages.impl.timeline_TimelineView_Day_1_en","features.messages.impl.timeline_TimelineView_Night_1_en",20728,], ["features.messages.impl.timeline_TimelineView_Day_2_en","features.messages.impl.timeline_TimelineView_Night_2_en",0,], ["features.messages.impl.timeline_TimelineView_Day_3_en","features.messages.impl.timeline_TimelineView_Night_3_en",0,], -["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20722,], +["features.messages.impl.timeline_TimelineView_Day_4_en","features.messages.impl.timeline_TimelineView_Night_4_en",20728,], ["features.messages.impl.timeline_TimelineView_Day_5_en","features.messages.impl.timeline_TimelineView_Night_5_en",0,], -["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20722,], +["features.messages.impl.timeline_TimelineView_Day_6_en","features.messages.impl.timeline_TimelineView_Night_6_en",20728,], ["features.messages.impl.timeline_TimelineView_Day_7_en","features.messages.impl.timeline_TimelineView_Night_7_en",0,], ["features.messages.impl.timeline_TimelineView_Day_8_en","features.messages.impl.timeline_TimelineView_Night_8_en",0,], ["features.messages.impl.timeline_TimelineView_Day_9_en","features.messages.impl.timeline_TimelineView_Night_9_en",0,], ["libraries.designsystem.components.avatar.internal_TombstonedRoomAvatar_Avatars_en","",0,], ["libraries.designsystem.theme.components_TopAppBarStr_App_Bars_en","",0,], ["libraries.designsystem.theme.components_TopAppBar_App_Bars_en","",0,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20722,], -["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20722,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_0_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_0_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_1_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_1_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_2_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_2_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_3_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_3_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_4_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_4_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_5_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_5_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_6_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_6_en",20728,], +["libraries.troubleshoot.impl_TroubleshootNotificationsView_Day_7_en","libraries.troubleshoot.impl_TroubleshootNotificationsView_Night_7_en",20728,], ["features.messages.impl.typing_TypingNotificationView_Day_0_en","features.messages.impl.typing_TypingNotificationView_Night_0_en",0,], -["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20722,], -["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20722,], -["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20722,], -["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20722,], -["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20722,], -["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20722,], +["features.messages.impl.typing_TypingNotificationView_Day_1_en","features.messages.impl.typing_TypingNotificationView_Night_1_en",20728,], +["features.messages.impl.typing_TypingNotificationView_Day_2_en","features.messages.impl.typing_TypingNotificationView_Night_2_en",20728,], +["features.messages.impl.typing_TypingNotificationView_Day_3_en","features.messages.impl.typing_TypingNotificationView_Night_3_en",20728,], +["features.messages.impl.typing_TypingNotificationView_Day_4_en","features.messages.impl.typing_TypingNotificationView_Night_4_en",20728,], +["features.messages.impl.typing_TypingNotificationView_Day_5_en","features.messages.impl.typing_TypingNotificationView_Night_5_en",20728,], +["features.messages.impl.typing_TypingNotificationView_Day_6_en","features.messages.impl.typing_TypingNotificationView_Night_6_en",20728,], ["features.messages.impl.typing_TypingNotificationView_Day_7_en","features.messages.impl.typing_TypingNotificationView_Night_7_en",0,], ["features.messages.impl.typing_TypingNotificationView_Day_8_en","features.messages.impl.typing_TypingNotificationView_Night_8_en",0,], ["libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Day_0_en","libraries.designsystem.atomic.atoms_UnreadIndicatorAtom_Night_0_en",0,], -["libraries.matrix.ui.components_UnresolvedUserRow_en","",20722,], +["libraries.matrix.ui.components_UnresolvedUserRow_en","",20728,], ["libraries.designsystem.components.avatar.internal_UserAvatarColors_Day_0_en","libraries.designsystem.components.avatar.internal_UserAvatarColors_Night_0_en",0,], -["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20722,], -["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20722,], -["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20722,], -["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20722,], +["features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Day_0_en","features.roomdetails.impl.notificationsettings_UserDefinedRoomNotificationSettingsView_Night_0_en",20728,], +["features.startchat.impl.components_UserListView_Day_0_en","features.startchat.impl.components_UserListView_Night_0_en",20728,], +["features.startchat.impl.components_UserListView_Day_1_en","features.startchat.impl.components_UserListView_Night_1_en",20728,], +["features.startchat.impl.components_UserListView_Day_2_en","features.startchat.impl.components_UserListView_Night_2_en",20728,], ["features.startchat.impl.components_UserListView_Day_3_en","features.startchat.impl.components_UserListView_Night_3_en",0,], ["features.startchat.impl.components_UserListView_Day_4_en","features.startchat.impl.components_UserListView_Night_4_en",0,], ["features.startchat.impl.components_UserListView_Day_5_en","features.startchat.impl.components_UserListView_Night_5_en",0,], ["features.startchat.impl.components_UserListView_Day_6_en","features.startchat.impl.components_UserListView_Night_6_en",0,], -["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20722,], +["features.startchat.impl.components_UserListView_Day_7_en","features.startchat.impl.components_UserListView_Night_7_en",20728,], ["features.startchat.impl.components_UserListView_Day_8_en","features.startchat.impl.components_UserListView_Night_8_en",0,], -["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20722,], +["features.startchat.impl.components_UserListView_Day_9_en","features.startchat.impl.components_UserListView_Night_9_en",20728,], ["features.preferences.impl.user_UserPreferences_Day_0_en","features.preferences.impl.user_UserPreferences_Night_0_en",0,], ["features.preferences.impl.user_UserPreferences_Day_1_en","features.preferences.impl.user_UserPreferences_Night_1_en",0,], ["features.preferences.impl.user_UserPreferences_Day_2_en","features.preferences.impl.user_UserPreferences_Night_2_en",0,], -["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20722,], -["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20722,], -["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20722,], -["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20722,], -["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20722,], -["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20722,], -["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20722,], -["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20722,], -["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20722,], -["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20722,], -["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20722,], -["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20722,], -["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20722,], -["features.preferences.impl.userstatus_UserStatusView_Day_0_en","features.preferences.impl.userstatus_UserStatusView_Night_0_en",20722,], +["features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Day_0_en","features.userprofile.shared_UserProfileHeaderSectionWithVerificationViolation_Night_0_en",20728,], +["features.userprofile.shared_UserProfileHeaderSection_Day_0_en","features.userprofile.shared_UserProfileHeaderSection_Night_0_en",20728,], +["features.userprofile.shared_UserProfileMainActionsSection_Day_0_en","features.userprofile.shared_UserProfileMainActionsSection_Night_0_en",20728,], +["features.userprofile.shared_UserProfileView_Day_0_en","features.userprofile.shared_UserProfileView_Night_0_en",20728,], +["features.userprofile.shared_UserProfileView_Day_1_en","features.userprofile.shared_UserProfileView_Night_1_en",20728,], +["features.userprofile.shared_UserProfileView_Day_2_en","features.userprofile.shared_UserProfileView_Night_2_en",20728,], +["features.userprofile.shared_UserProfileView_Day_3_en","features.userprofile.shared_UserProfileView_Night_3_en",20728,], +["features.userprofile.shared_UserProfileView_Day_4_en","features.userprofile.shared_UserProfileView_Night_4_en",20728,], +["features.userprofile.shared_UserProfileView_Day_5_en","features.userprofile.shared_UserProfileView_Night_5_en",20728,], +["features.userprofile.shared_UserProfileView_Day_6_en","features.userprofile.shared_UserProfileView_Night_6_en",20728,], +["features.userprofile.shared_UserProfileView_Day_7_en","features.userprofile.shared_UserProfileView_Night_7_en",20728,], +["features.userprofile.shared_UserProfileView_Day_8_en","features.userprofile.shared_UserProfileView_Night_8_en",20728,], +["features.userprofile.shared_UserProfileView_Day_9_en","features.userprofile.shared_UserProfileView_Night_9_en",20728,], +["features.preferences.impl.userstatus_UserStatusView_Day_0_en","features.preferences.impl.userstatus_UserStatusView_Night_0_en",20728,], ["features.preferences.impl.userstatus_UserStatusView_Day_1_en","features.preferences.impl.userstatus_UserStatusView_Night_1_en",0,], -["features.preferences.impl.userstatus_UserStatusView_Day_2_en","features.preferences.impl.userstatus_UserStatusView_Night_2_en",20722,], -["features.preferences.impl.userstatus_UserStatusView_Day_3_en","features.preferences.impl.userstatus_UserStatusView_Night_3_en",20722,], -["features.preferences.impl.userstatus_UserStatusView_Day_4_en","features.preferences.impl.userstatus_UserStatusView_Night_4_en",20722,], -["features.preferences.impl.userstatus_UserStatusView_Day_5_en","features.preferences.impl.userstatus_UserStatusView_Night_5_en",20722,], -["features.preferences.impl.userstatus_UserStatusView_Day_6_en","features.preferences.impl.userstatus_UserStatusView_Night_6_en",20722,], -["features.verifysession.impl.ui_VerificationUserProfileContent_Day_0_en","features.verifysession.impl.ui_VerificationUserProfileContent_Night_0_en",0,], +["features.preferences.impl.userstatus_UserStatusView_Day_2_en","features.preferences.impl.userstatus_UserStatusView_Night_2_en",20728,], +["features.preferences.impl.userstatus_UserStatusView_Day_3_en","features.preferences.impl.userstatus_UserStatusView_Night_3_en",20728,], +["features.preferences.impl.userstatus_UserStatusView_Day_4_en","features.preferences.impl.userstatus_UserStatusView_Night_4_en",20728,], +["features.preferences.impl.userstatus_UserStatusView_Day_5_en","features.preferences.impl.userstatus_UserStatusView_Night_5_en",20728,], +["features.preferences.impl.userstatus_UserStatusView_Day_6_en","features.preferences.impl.userstatus_UserStatusView_Night_6_en",20728,], ["libraries.designsystem.ruler_VerticalRuler_Day_0_en","libraries.designsystem.ruler_VerticalRuler_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_0_en",0,], ["libraries.mediaviewer.impl.gallery.ui_VideoItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VideoItemView_Night_1_en",0,], -["features.preferences.impl.media_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.media_VideoQualitySelectorDialog_Night_0_en",20724,], -["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20722,], +["features.preferences.impl.media_VideoQualitySelectorDialog_Day_0_en","features.preferences.impl.media_VideoQualitySelectorDialog_Night_0_en",20728,], +["features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Day_0_en","features.messages.impl.attachments.preview_VideoQualitySelectorDialog_Night_0_en",20728,], ["features.viewfolder.impl.file_ViewFileView_Day_0_en","features.viewfolder.impl.file_ViewFileView_Night_0_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_1_en","features.viewfolder.impl.file_ViewFileView_Night_1_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_2_en","features.viewfolder.impl.file_ViewFileView_Night_2_en",0,], -["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20722,], +["features.viewfolder.impl.file_ViewFileView_Day_3_en","features.viewfolder.impl.file_ViewFileView_Night_3_en",20728,], ["features.viewfolder.impl.file_ViewFileView_Day_4_en","features.viewfolder.impl.file_ViewFileView_Night_4_en",0,], ["features.viewfolder.impl.file_ViewFileView_Day_5_en","features.viewfolder.impl.file_ViewFileView_Night_5_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_0_en","features.viewfolder.impl.folder_ViewFolderView_Night_0_en",0,], ["features.viewfolder.impl.folder_ViewFolderView_Day_1_en","features.viewfolder.impl.folder_ViewFolderView_Night_1_en",0,], ["libraries.designsystem.components.visuallist_VisualListItem_Day_0_en","libraries.designsystem.components.visuallist_VisualListItem_Night_0_en",0,], ["libraries.designsystem.components.visuallist_VisualList_Day_0_en","libraries.designsystem.components.visuallist_VisualList_Night_0_en",0,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_0_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_1_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_2_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_2_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_3_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_3_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_4_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_4_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_0_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_1_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_2_en",20722,], -["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_3_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_3_en",20722,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_0_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_1_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_2_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_2_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_3_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_3_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Day_4_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemViewPlay_Night_4_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_0_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_0_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_1_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_1_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_2_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_2_en",20728,], +["libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Day_3_en","libraries.mediaviewer.impl.gallery.ui_VoiceItemView_Night_3_en",20728,], ["libraries.textcomposer.components_VoiceMessageDeleteButtonIcon_Day_0_en","libraries.textcomposer.components_VoiceMessageDeleteButtonIcon_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessagePreview_Day_0_en","libraries.textcomposer.components_VoiceMessagePreview_Night_0_en",0,], ["libraries.textcomposer.components_VoiceMessageRecorderButtonIcon_Day_0_en","libraries.textcomposer.components_VoiceMessageRecorderButtonIcon_Night_0_en",0,], From 48d6b5cadf162132d3f28cd2a352bd9f3d38277f Mon Sep 17 00:00:00 2001 From: Bardi Harborow Date: Mon, 5 Oct 2026 18:38:32 +1100 Subject: [PATCH 28/92] Improve the reset identity password field (#7850) Use the password keyboard type, mark the field as a password for autofill, and submit the reset with the IME "Done" action, matching the login password field. --- .../password/ResetIdentityPasswordView.kt | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt index 27612a7b4d..79cadbd132 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/reset/password/ResetIdentityPasswordView.kt @@ -9,14 +9,21 @@ package io.element.android.features.securebackup.impl.reset.password import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions 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 androidx.compose.ui.Modifier +import androidx.compose.ui.autofill.ContentType import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentType +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.PreviewParameter @@ -42,6 +49,11 @@ fun ResetIdentityPasswordView( modifier: Modifier = Modifier, ) { val passwordState = textFieldState(stateValue = "") + val submit = { + if (passwordState.value.isNotEmpty()) { + state.eventSink(ResetIdentityPasswordEvent.Reset(passwordState.value)) + } + } FlowStepPage( modifier = modifier, iconStyle = BigIcon.Style.Default(CompoundIcons.LockSolid()), @@ -58,13 +70,14 @@ fun ResetIdentityPasswordView( passwordState.value = newText }, hasError = state.resetAction.isFailure(), + onSubmit = submit, ) }, buttons = { Button( modifier = Modifier.fillMaxWidth(), text = stringResource(CommonStrings.action_reset_identity), - onClick = { state.eventSink(ResetIdentityPasswordEvent.Reset(passwordState.value)) }, + onClick = submit, destructive = true, enabled = passwordState.value.isNotEmpty(), ) @@ -78,12 +91,20 @@ fun ResetIdentityPasswordView( } @Composable -private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Boolean) { +private fun Content( + text: String, + onTextChange: (String) -> Unit, + hasError: Boolean, + onSubmit: () -> Unit, +) { var showPassword by remember { mutableStateOf(false) } TextField( modifier = Modifier .fillMaxWidth() - .onTabOrEnterKeyFocusNext(LocalFocusManager.current), + .onTabOrEnterKeyFocusNext(LocalFocusManager.current) + .semantics { + contentType = ContentType.Password + }, value = text, onValueChange = onTextChange, placeholder = stringResource(CommonStrings.common_password), @@ -95,6 +116,13 @@ private fun Content(text: String, onTextChange: (String) -> Unit, hasError: Bool onToggle = { showPassword = !showPassword }, ) }, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Password, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions( + onDone = { onSubmit() } + ), validity = if (hasError) TextFieldValidity.Invalid else TextFieldValidity.None, supportingText = if (hasError) { stringResource(R.string.screen_reset_encryption_password_error) From b038e30d288160da39026bea250d26b979cb0826 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 5 Oct 2026 11:12:12 +0200 Subject: [PATCH 29/92] Exclude custom state events from the timeline (#7854) Don't display custom state events not supported by the SDK --- .../matrix/impl/room/TimelineEventFilterFactory.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/TimelineEventFilterFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/TimelineEventFilterFactory.kt index 1d2f465b64..e75d6dc0da 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/TimelineEventFilterFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/TimelineEventFilterFactory.kt @@ -46,10 +46,13 @@ class RustTimelineEventFilterFactory : TimelineEventFilterFactory { } else { emptyList() } + + // Exclude any custom state events not supported by the SDK + val customStateEventsFilter = listOf(TimelineEventCondition.AnyCustomStateEvent) return if (excludedEventTypes.isNotEmpty() || excludedMembershipChanges.isNotEmpty()) { - TimelineEventFilter.Exclude(excludedEventTypes + excludedMembershipChanges) + TimelineEventFilter.Exclude(customStateEventsFilter + excludedEventTypes + excludedMembershipChanges) } else { - null + TimelineEventFilter.Exclude(customStateEventsFilter) } } } From b9ed09040036b663c9c91a6535dec04563891ac2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 11:40:08 +0200 Subject: [PATCH 30/92] Use only ClipboardHelper to copy to the clipboard, and do not render feedback on Android 13+ Since Android 13, the system displays its own confirmation when content is copied to the clipboard, so the application does not render its own Toast or Snackbar anymore on those versions. Context.copyToClipboard, CopyToClipboardUseCase and the direct ClipboardManager usage in EventDebugInfoView are removed. ClipboardHelper.copyPlainText(text, isSensitive, onSuccessOnOldDevice) is now the only entry point, and AndroidClipboardHelper is the only class using ClipboardManager. Copy actions from the views are moved to their Presenter through new events, or to the Node when there is no Presenter. A ToastHelper is introduced so that Toasts are kept where they were used before. Snackbars are kept where they were already used. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../messages/impl/MessagesPresenter.kt | 12 ++-- .../pinned/list/PinnedMessagesListNode.kt | 14 ++--- .../messages/impl/timeline/TimelineEvent.kt | 2 + .../impl/timeline/TimelinePresenter.kt | 10 ++++ .../messages/impl/timeline/TimelineView.kt | 9 +-- .../impl/timeline/debug/EventDebugInfoNode.kt | 14 ++++- .../impl/timeline/debug/EventDebugInfoView.kt | 34 +++++++---- .../impl/timeline/TimelinePresenterTest.kt | 41 +++++++++++++ .../blockedusers/BlockedUsersPresenter.kt | 5 +- .../impl/developer/DeveloperSettingsEvent.kt | 1 + .../developer/DeveloperSettingsPresenter.kt | 10 ++++ .../impl/developer/DeveloperSettingsView.kt | 21 +++---- .../appsettings/AppDeveloperSettingsEvent.kt | 1 + .../AppDeveloperSettingsPresenter.kt | 10 ++++ .../appsettings/AppDeveloperSettingsView.kt | 20 ++----- .../DeveloperSettingsPresenterTest.kt | 41 +++++++++++++ .../AppDeveloperSettingsPresenterTest.kt | 43 +++++++++++++- .../roomdetails/impl/RoomDetailsEvent.kt | 1 + .../roomdetails/impl/RoomDetailsPresenter.kt | 12 +++- .../roomdetails/impl/RoomDetailsView.kt | 13 +--- .../details/RoomMemberDetailsPresenter.kt | 5 +- .../impl/RoomDetailsPresenterTest.kt | 45 ++++++++++++++ .../impl/setup/SecureBackupSetupEvent.kt | 1 + .../impl/setup/SecureBackupSetupPresenter.kt | 12 ++++ .../impl/setup/SecureBackupSetupView.kt | 10 +--- .../DefaultSecureBackupSetupEntryPointTest.kt | 4 ++ .../setup/SecureBackupSetupPresenterTest.kt | 59 +++++++++++++++++++ .../viewfolder/impl/DefaultTextFileViewer.kt | 13 +++- .../viewfolder/impl/file/FileContent.kt | 14 ++--- .../viewfolder/impl/file/ViewFileEvent.kt | 1 + .../viewfolder/impl/file/ViewFilePresenter.kt | 8 +++ .../viewfolder/impl/file/ViewFileView.kt | 1 + .../test/file/ViewFilePresenterTest.kt | 39 ++++++++++++ .../clipboard/AndroidClipboardHelper.kt | 30 +++++++++- .../androidutils/clipboard/ClipboardHelper.kt | 17 ++++-- .../clipboard/FakeClipboardHelper.kt | 15 ++++- .../system/CopyToClipboardUseCase.kt | 43 -------------- .../androidutils/system/SystemUtils.kt | 20 ------- .../androidutils/toast/AndroidToastHelper.kt | 23 ++++++++ .../androidutils/toast/FakeToastHelper.kt | 16 +++++ .../androidutils/toast/ToastHelper.kt | 14 +++++ 41 files changed, 532 insertions(+), 172 deletions(-) delete mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/AndroidToastHelper.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/FakeToastHelper.kt create mode 100644 libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/ToastHelper.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 98e595c110..89556bb1e1 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 @@ -8,7 +8,6 @@ package io.element.android.features.messages.impl -import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState @@ -634,8 +633,9 @@ class MessagesPresenter( event.eventId ?: return room.getPermalinkFor(event.eventId).fold( onSuccess = { permalink -> - clipboardHelper.copyPlainText(permalink) - snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_link_copied_to_clipboard)) + clipboardHelper.copyPlainText(permalink) { + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_link_copied_to_clipboard)) + } }, onFailure = { Timber.e(it, "Failed to get permalink for event ${event.eventId}") @@ -650,16 +650,14 @@ class MessagesPresenter( is TimelineItemStateContent -> event.content.body else -> return } - clipboardHelper.copyPlainText(content) - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + clipboardHelper.copyPlainText(content) { snackbarDispatcher.post(SnackbarMessage(R.string.screen_room_timeline_message_copied)) } } private fun handleCopyCaption(event: TimelineItem.Event) { val content = event.content.captionOrNull() ?: return - clipboardHelper.copyPlainText(content) - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + clipboardHelper.copyPlainText(content) { snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt index 5c6980c8bb..2391766203 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt @@ -15,7 +15,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringResource import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin @@ -26,8 +25,9 @@ import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.libraries.androidutils.system.copyToClipboard +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.system.openUrlInExternalApp +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.callback import io.element.android.libraries.di.RoomScope import io.element.android.libraries.emoji.api.picker.EmojiPickerRenderer @@ -53,6 +53,8 @@ class PinnedMessagesListNode( private val timelineItemPresenterFactories: TimelineItemPresenterFactories, private val permalinkParser: PermalinkParser, private val emojiPickerRenderer: EmojiPickerRenderer, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Node(buildContext, plugins = plugins), PinnedMessagesListNavigator { interface Callback : Plugin { fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean) @@ -114,7 +116,6 @@ class PinnedMessagesListNode( LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories, ) { val context = LocalContext.current - val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) val view = LocalView.current val state = presenter.present() PinnedMessagesListView( @@ -132,10 +133,9 @@ class PinnedMessagesListNode( view.performHapticFeedback( HapticFeedbackConstants.LONG_PRESS ) - context.copyToClipboard( - text = it.url, - toastMessage = toastMessage, - ) + clipboardHelper.copyPlainText(it.url) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } }, emojiPickerRenderer = emojiPickerRenderer, modifier = modifier diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt index 66807b74f2..82f13a6dae 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvent.kt @@ -29,6 +29,8 @@ sealed interface TimelineEvent { data object MarkAllAsRead : TimelineEvent + data class CopyToClipboard(val text: String) : TimelineEvent + /** * Events coming from a timeline item. */ 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 4580f77d59..516abcdfd8 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 @@ -44,6 +44,8 @@ import io.element.android.features.messages.impl.voicemessages.timeline.Redacted import io.element.android.features.poll.api.actions.EndPollAction import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.roomcall.api.RoomCallState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.annotations.SessionCoroutineScope @@ -60,6 +62,7 @@ import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.item.event.LocalEventSendState import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.preferences.api.store.SessionPreferencesStore +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.DisplayFirstTimelineItems import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationToMessage import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom @@ -104,6 +107,8 @@ class TimelinePresenter( private val liveLocationShareManager: ActiveLiveLocationShareManager, private val markAsFullyRead: MarkAsFullyRead, private val timelineProtectionPresenter: Presenter, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Presenter { private val tag = "TimelinePresenter" @@ -315,6 +320,11 @@ class TimelinePresenter( is TimelineEvent.ValidateMedia -> { timelineProtectionState.eventSink(TimelineProtectionEvent.ValidateContent(event.mediaSources, event.validationState)) } + is TimelineEvent.CopyToClipboard -> { + clipboardHelper.copyPlainText(event.text) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } + } } } 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 c9d9d5f630..0dbe6c315f 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 @@ -63,7 +63,6 @@ import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.rememberNestedScrollInteropConnection @@ -93,7 +92,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentPreviewParam import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState -import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.designsystem.atomic.atoms.UnreadIndicatorAtom import io.element.android.libraries.designsystem.components.dialogs.AlertDialog import io.element.android.libraries.designsystem.preview.ElementPreview @@ -167,8 +165,6 @@ fun TimelineView( state.eventSink(TimelineEvent.FocusOnEvent(eventId)) } - val context = LocalContext.current - val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) val view = LocalView.current fun inReplyToClick(eventId: EventId) { state.eventSink(TimelineEvent.FocusOnEvent(eventId)) @@ -178,10 +174,7 @@ fun TimelineView( view.performHapticFeedback( HapticFeedbackConstants.LONG_PRESS ) - context.copyToClipboard( - text = link.url, - toastMessage = toastMessage, - ) + state.eventSink(TimelineEvent.CopyToClipboard(link.url)) } fun prefetchMoreItems() { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt index 6288f3f159..3c80cea7f7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoNode.kt @@ -16,17 +16,22 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo +import io.element.android.libraries.ui.strings.CommonStrings @ContributesNode(RoomScope::class) @AssistedInject class EventDebugInfoNode( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Node(buildContext, plugins = plugins) { data class Inputs( val eventId: EventId?, @@ -39,6 +44,12 @@ class EventDebugInfoNode( navigateUp() } + private fun onCopyText(text: String) { + clipboardHelper.copyPlainText(text) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } + } + @Composable override fun View(modifier: Modifier) = with(inputs) { EventDebugInfoView( @@ -46,7 +57,8 @@ class EventDebugInfoNode( model = timelineItemDebugInfo.model, originalJson = timelineItemDebugInfo.originalJson, latestEditedJson = timelineItemDebugInfo.latestEditedJson, - onBackClick = ::onBackClick + onBackClick = ::onBackClick, + onCopyText = ::onCopyText, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt index 073bb427c9..f836bc5d68 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/debug/EventDebugInfoView.kt @@ -8,8 +8,6 @@ package io.element.android.features.messages.impl.timeline.debug -import android.content.ClipData -import android.content.ClipboardManager import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically @@ -34,11 +32,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp -import androidx.core.content.getSystemService import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.libraries.designsystem.components.button.BackButton @@ -63,6 +59,7 @@ fun EventDebugInfoView( originalJson: String?, latestEditedJson: String?, onBackClick: () -> Unit, + onCopyText: (String) -> Unit, modifier: Modifier = Modifier, isTest: Boolean = false, ) { @@ -87,20 +84,30 @@ fun EventDebugInfoView( item { Column(Modifier.padding(vertical = 10.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { Text(text = "Event ID:") - CopyableText(text = eventId?.value ?: "-", modifier = Modifier.fillMaxWidth()) + CopyableText(text = eventId?.value ?: "-", onClick = onCopyText, modifier = Modifier.fillMaxWidth()) } } item { - CollapsibleSection(title = "Model:", text = model, initiallyExpanded = sectionsInitiallyExpanded) + CollapsibleSection(title = "Model:", text = model, onCopyText = onCopyText, initiallyExpanded = sectionsInitiallyExpanded) } if (originalJson != null) { item { - CollapsibleSection(title = "Original JSON:", text = prettyJSON(originalJson), initiallyExpanded = sectionsInitiallyExpanded) + CollapsibleSection( + title = "Original JSON:", + text = prettyJSON(originalJson), + onCopyText = onCopyText, + initiallyExpanded = sectionsInitiallyExpanded, + ) } } if (latestEditedJson != null) { item { - CollapsibleSection(title = "Latest edited JSON:", text = prettyJSON(latestEditedJson), initiallyExpanded = sectionsInitiallyExpanded) + CollapsibleSection( + title = "Latest edited JSON:", + text = prettyJSON(latestEditedJson), + onCopyText = onCopyText, + initiallyExpanded = sectionsInitiallyExpanded, + ) } } } @@ -120,6 +127,7 @@ private fun prettyJSON(maybeJSON: String): String { private fun CollapsibleSection( title: String, text: String, + onCopyText: (String) -> Unit, initiallyExpanded: Boolean = false, ) { var isExpanded by remember { mutableStateOf(initiallyExpanded) } @@ -139,7 +147,7 @@ private fun CollapsibleSection( ) } AnimatedVisibility(visible = isExpanded, enter = expandVertically(), exit = shrinkVertically()) { - CopyableText(text = text, modifier = Modifier.fillMaxWidth()) + CopyableText(text = text, onClick = onCopyText, modifier = Modifier.fillMaxWidth()) } } } @@ -147,16 +155,15 @@ private fun CollapsibleSection( @Composable private fun CopyableText( text: String, + onClick: (String) -> Unit, modifier: Modifier = Modifier, ) { - val context = LocalContext.current - val clipboardManager = remember { requireNotNull(context.getSystemService()) } Box( modifier .clip(RoundedCornerShape(4.dp)) .background(MaterialTheme.colorScheme.surfaceVariant) .padding(6.dp) - .clickable { clipboardManager.setPrimaryClip(ClipData.newPlainText("JSON", text)) } + .clickable { onClick(text) } ) { Text( text = text, @@ -174,6 +181,7 @@ internal fun EventDebugInfoViewPreview() = ElementPreview { model = "Rust(\n\tModel()\n)", originalJson = "{\"name\": \"original\"}", latestEditedJson = "{\"name\": \"edited\"}", - onBackClick = { } + onBackClick = { }, + onCopyText = { }, ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index aa4198d156..f09e595e7c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -33,6 +33,10 @@ import io.element.android.features.poll.api.actions.SendPollResponseAction import io.element.android.features.poll.test.actions.FakeEndPollAction import io.element.android.features.poll.test.actions.FakeSendPollResponseAction import io.element.android.features.roomcall.api.aStandByCallState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService @@ -73,6 +77,7 @@ import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.item.event.aRoomMembershipContent import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.awaitLastSequentialItem @@ -1541,6 +1546,38 @@ class TimelinePresenterTest { } } + @Test + fun `present - copy to clipboard on old device shows a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createTimelinePresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitFirstItem().eventSink(TimelineEvent.CopyToClipboard("https://element.io")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("https://element.io") + assertThat(toastHelper.shownToasts).containsExactly(CommonStrings.common_copied_to_clipboard) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - copy to clipboard on new device does not show a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createTimelinePresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitFirstItem().eventSink(TimelineEvent.CopyToClipboard("https://element.io")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("https://element.io") + assertThat(toastHelper.shownToasts).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - show shield hide shield`() = runTest { val presenter = createTimelinePresenter() @@ -1897,6 +1934,8 @@ class TimelinePresenterTest { markAsFullyRead: MarkAsFullyRead = FakeMarkAsFullyRead { _, _ -> }, timelineProtectionPresenter: Presenter = { aTimelineProtectionState() }, resolveVerifiedUserSendFailurePresenter: Presenter = { aResolveVerifiedUserSendFailureState() }, + clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), ): TimelinePresenter { return TimelinePresenter( timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), @@ -1923,6 +1962,8 @@ class TimelinePresenterTest { liveLocationShareManager = liveLocationShareManager, markAsFullyRead = markAsFullyRead, timelineProtectionPresenter = timelineProtectionPresenter, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt index 8f20694d81..ebb671e6d0 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/blockedusers/BlockedUsersPresenter.kt @@ -81,8 +81,9 @@ class BlockedUsersPresenter( unblockUserAction.value = AsyncAction.ConfirmingNoParams } is BlockedUsersEvent.CopyToClipboard -> { - clipboardHelper.copyPlainText(event.userId.value) - snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + clipboardHelper.copyPlainText(event.userId.value) { + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + } } BlockedUsersEvent.ConfirmUnblock -> { pendingUserToUnblock?.let { diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt index 34ccba2abe..a4ca35e50d 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsEvent.kt @@ -19,4 +19,5 @@ sealed interface DeveloperSettingsEvent { data object DismissMarkAllRoomsAsReadConfirmation : DeveloperSettingsEvent data object OpenPushRules : DeveloperSettingsEvent data object DismissPushRulesError : DeveloperSettingsEvent + data class CopyToClipboard(val text: String) : DeveloperSettingsEvent } 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 74bf595ce4..d531bb1cb1 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 @@ -26,7 +26,9 @@ import io.element.android.features.preferences.impl.tasks.ClearCacheUseCase import io.element.android.features.preferences.impl.tasks.ComputeCacheSizeUseCase import io.element.android.features.preferences.impl.tasks.MarkAllRoomsAsRead import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.filesize.FileSizeFormatter +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter @@ -38,6 +40,7 @@ import io.element.android.libraries.matrix.api.analytics.GetDatabaseSizesUseCase import io.element.android.libraries.matrix.api.core.DeviceId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.CoroutineScope @@ -61,6 +64,8 @@ class DeveloperSettingsPresenter( private val markAllRoomsAsRead: MarkAllRoomsAsRead, private val buildMeta: BuildMeta, private val notificationSettingsService: NotificationSettingsService, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Presenter { @AssistedFactory fun interface Factory { @@ -130,6 +135,11 @@ class DeveloperSettingsPresenter( DeveloperSettingsEvent.DismissPushRulesError -> { pushRulesAction.value = AsyncAction.Uninitialized } + is DeveloperSettingsEvent.CopyToClipboard -> { + clipboardHelper.copyPlainText(event.text) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } + } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt index 992a7e6212..b36521ef0c 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/DeveloperSettingsView.kt @@ -15,14 +15,12 @@ import androidx.compose.foundation.progressSemantics import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.developer.appsettings.AppDeveloperSettingsView -import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog @@ -84,7 +82,10 @@ fun DeveloperSettingsView( AppDeveloperSettingsView( state = state.appDeveloperSettingsState, ) - SessionCategory(deviceId = state.deviceId) + SessionCategory( + deviceId = state.deviceId, + onCopyToClipboard = { state.eventSink(DeveloperSettingsEvent.CopyToClipboard(it)) }, + ) NotificationCategory( onPushRulesClick = { state.eventSink(DeveloperSettingsEvent.OpenPushRules) }, onPushHistoryClick = onPushHistoryClick, @@ -182,19 +183,15 @@ fun DeveloperSettingsView( } @Composable -private fun SessionCategory(deviceId: DeviceId) { +private fun SessionCategory( + deviceId: DeviceId, + onCopyToClipboard: (String) -> Unit, +) { PreferenceCategory(title = "Session") { - val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) - val context = LocalContext.current ListItem( content = { Text("DeviceId") }, supportingContent = { Text(text = deviceId.value) }, - onClick = { - context.copyToClipboard( - text = deviceId.value, - toastMessage = toastMessage, - ) - } + onClick = { onCopyToClipboard(deviceId.value) } ) } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsEvent.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsEvent.kt index 9f328fdd9b..b3d777c4e7 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsEvent.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsEvent.kt @@ -17,4 +17,5 @@ sealed interface AppDeveloperSettingsEvent { data class SetCustomElementCallBaseUrl(val baseUrl: String?) : AppDeveloperSettingsEvent data class SetTracingLogLevel(val logLevel: LogLevelItem) : AppDeveloperSettingsEvent data class ToggleTracingLogPack(val logPack: TraceLogPack, val enabled: Boolean) : AppDeveloperSettingsEvent + data class CopyToClipboard(val text: String) : AppDeveloperSettingsEvent } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt index 1bf162d3ea..75519079ed 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenter.kt @@ -22,6 +22,8 @@ import io.element.android.features.preferences.impl.developer.tracing.toLogLevel import io.element.android.features.preferences.impl.developer.tracing.toLogLevelItem import io.element.android.features.preferences.impl.model.EnabledFeature import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.extensions.runCatchingExceptions @@ -29,6 +31,7 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.ui.model.FeatureUiModel import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -44,6 +47,8 @@ class AppDeveloperSettingsPresenter( private val rageshakePresenter: Presenter, private val appPreferencesStore: AppPreferencesStore, private val buildMeta: BuildMeta, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Presenter { @Composable override fun present(): AppDeveloperSettingsState { @@ -106,6 +111,11 @@ class AppDeveloperSettingsPresenter( } appPreferencesStore.setTracingLogPacks(currentPacks) } + is AppDeveloperSettingsEvent.CopyToClipboard -> { + clipboardHelper.copyPlainText(event.text) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } + } } } diff --git a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt index 7f10745872..4f6f8234fa 100644 --- a/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt +++ b/features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsView.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.PreviewParameter @@ -23,7 +22,6 @@ import io.element.android.compound.theme.ElementTheme import io.element.android.features.preferences.impl.R import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem import io.element.android.features.rageshake.api.preferences.RageshakePreferencesView -import io.element.android.libraries.androidutils.system.copyToClipboard 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.PreferenceDropdown @@ -108,6 +106,7 @@ fun AppDeveloperSettingsView( GitCategory( gitBranch = state.gitBranch, gitSha = state.gitSha, + onCopyToClipboard = { state.eventSink(AppDeveloperSettingsEvent.CopyToClipboard(it)) }, ) } } @@ -116,29 +115,18 @@ fun AppDeveloperSettingsView( private fun GitCategory( gitBranch: String, gitSha: String, + onCopyToClipboard: (String) -> Unit, ) { PreferenceCategory(title = "Git") { - val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) - val context = LocalContext.current ListItem( content = { Text("Git branch") }, supportingContent = { Text(text = gitBranch) }, - onClick = { - context.copyToClipboard( - text = gitBranch, - toastMessage = toastMessage, - ) - } + onClick = { onCopyToClipboard(gitBranch) } ) ListItem( content = { Text("Git SHA") }, supportingContent = { Text(text = gitSha) }, - onClick = { - context.copyToClipboard( - text = gitSha, - toastMessage = toastMessage, - ) - } + onClick = { onCopyToClipboard(gitSha) } ) } } 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 b76939f72c..c3c08602cd 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 @@ -19,7 +19,11 @@ import io.element.android.features.preferences.impl.tasks.FakeClearCacheUseCase import io.element.android.features.preferences.impl.tasks.FakeComputeCacheSizeUseCase import io.element.android.features.preferences.impl.tasks.FakeMarkAllRoomsAsRead import io.element.android.features.preferences.impl.tasks.VacuumStoresUseCase +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.androidutils.filesize.FakeFileSizeFormatter +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.data.megaBytes @@ -33,6 +37,7 @@ import io.element.android.libraries.matrix.test.A_DEVICE_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.test.notificationsettings.FakeNotificationSettingsService +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaRecorder @@ -250,6 +255,38 @@ class DeveloperSettingsPresenterTest { } } + @Test + fun `present - copy to clipboard on old device shows a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createDeveloperSettingsPresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitItem().eventSink(DeveloperSettingsEvent.CopyToClipboard("text")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("text") + assertThat(toastHelper.shownToasts).containsExactly(CommonStrings.common_copied_to_clipboard) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - copy to clipboard on new device does not show a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createDeveloperSettingsPresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitItem().eventSink(DeveloperSettingsEvent.CopyToClipboard("text")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("text") + assertThat(toastHelper.shownToasts).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + private fun createDeveloperSettingsPresenter( navigator: DeveloperSettingsNavigator = DeveloperSettingsNavigator { _, _ -> lambdaError() }, sessionId: SessionId = A_SESSION_ID, @@ -262,6 +299,8 @@ class DeveloperSettingsPresenterTest { markAllRoomsAsRead: FakeMarkAllRoomsAsRead = FakeMarkAllRoomsAsRead(), buildMeta: BuildMeta = aBuildMeta(), notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), + clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), ): DeveloperSettingsPresenter { return DeveloperSettingsPresenter( navigator = navigator, @@ -277,6 +316,8 @@ class DeveloperSettingsPresenterTest { markAllRoomsAsRead = markAllRoomsAsRead, buildMeta = buildMeta, notificationSettingsService = notificationSettingsService, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, ) } } diff --git a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt index 9300236293..3ec56a64fd 100644 --- a/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt +++ b/features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/developer/appsettings/AppDeveloperSettingsPresenterTest.kt @@ -12,12 +12,17 @@ package io.element.android.features.preferences.impl.developer.appsettings import com.google.common.truth.Truth.assertThat import io.element.android.features.preferences.impl.developer.tracing.LogLevelItem import io.element.android.features.rageshake.api.preferences.aRageshakePreferencesState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.featureflag.api.Feature import io.element.android.libraries.featureflag.test.FakeFeature import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -151,6 +156,38 @@ class AppDeveloperSettingsPresenterTest { } } + @Test + fun `present - copy to clipboard on old device shows a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createAppDeveloperSettingsPresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitItem().eventSink(AppDeveloperSettingsEvent.CopyToClipboard("text")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("text") + assertThat(toastHelper.shownToasts).containsExactly(CommonStrings.common_copied_to_clipboard) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - copy to clipboard on new device does not show a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createAppDeveloperSettingsPresenter( + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.test { + awaitItem().eventSink(AppDeveloperSettingsEvent.CopyToClipboard("text")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("text") + assertThat(toastHelper.shownToasts).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + private fun createAppDeveloperSettingsPresenter( featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService( getAvailableFeaturesResult = { _, _ -> @@ -164,6 +201,8 @@ class AppDeveloperSettingsPresenterTest { } ), preferencesStore: InMemoryAppPreferencesStore = InMemoryAppPreferencesStore(), + clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), ): AppDeveloperSettingsPresenter { return AppDeveloperSettingsPresenter( featureFlagService = featureFlagService, @@ -172,7 +211,9 @@ class AppDeveloperSettingsPresenterTest { buildMeta = aBuildMeta( gitRevision = "1234567890", gitBranchName = "feature/awesome-feature" - ) + ), + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, ) } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt index a82766175d..9d06db371f 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsEvent.kt @@ -13,6 +13,7 @@ sealed interface RoomDetailsEvent { data object MuteNotification : RoomDetailsEvent data object UnmuteNotification : RoomDetailsEvent data class CopyToClipboard(val text: String) : RoomDetailsEvent + data object CopyRoomId : RoomDetailsEvent data class SetFavorite(val isFavorite: Boolean) : RoomDetailsEvent data object MarkAsRead : RoomDetailsEvent data object MarkAsUnread : RoomDetailsEvent diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 9a602d06c0..e8a8079a54 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -32,6 +32,7 @@ import io.element.android.features.roomdetailsedit.api.roomDetailsEditPermission import io.element.android.features.securityandprivacy.api.SecurityAndPrivacyPermissions import io.element.android.features.securityandprivacy.api.securityAndPrivacyPermissions import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.designsystem.utils.snackbar.LocalSnackbarDispatcher @@ -74,6 +75,7 @@ class RoomDetailsPresenter( private val dispatchers: CoroutineDispatchers, private val analyticsService: AnalyticsService, private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, private val appPreferencesStore: AppPreferencesStore, private val sessionPreferencesStore: SessionPreferencesStore, private val notificationCleaner: NotificationCleaner, @@ -167,8 +169,14 @@ class RoomDetailsPresenter( } is RoomDetailsEvent.SetFavorite -> scope.setFavorite(event.isFavorite) is RoomDetailsEvent.CopyToClipboard -> { - clipboardHelper.copyPlainText(event.text) - snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + clipboardHelper.copyPlainText(event.text) { + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + } + } + is RoomDetailsEvent.CopyRoomId -> { + clipboardHelper.copyPlainText(room.roomId.value) { + toastHelper.show(CommonStrings.common_copied_to_clipboard) + } } is RoomDetailsEvent.MarkAsRead -> scope.markAsRead() is RoomDetailsEvent.MarkAsUnread -> scope.markAsUnread() 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 2d76ca5e78..626977007e 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 @@ -34,7 +34,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow @@ -48,7 +47,6 @@ import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.shared.blockuser.BlockUserDialogs import io.element.android.features.userprofile.shared.blockuser.BlockUserSection -import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.architecture.coverage.ExcludeFromCoverage import io.element.android.libraries.designsystem.atomic.atoms.MatrixBadgeAtom import io.element.android.libraries.designsystem.atomic.molecules.MatrixBadgeRowMolecule @@ -339,6 +337,7 @@ fun RoomDetailsView( DebugInfoSection( roomId = state.roomId, roomVersion = state.roomVersion, + onCopyRoomIdClick = { state.eventSink(RoomDetailsEvent.CopyRoomId) }, ) } } @@ -865,10 +864,9 @@ private fun OtherActionsSection( private fun DebugInfoSection( roomId: RoomId, roomVersion: String?, + onCopyRoomIdClick: () -> Unit, ) { - val context = LocalContext.current PreferenceCategory { - val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) ListItem( content = { Text("Internal room ID") @@ -882,12 +880,7 @@ private fun DebugInfoSection( }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Code())), trailingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Copy())), - onClick = { - context.copyToClipboard( - text = roomId.value, - toastMessage = toastMessage, - ) - }, + onClick = onCopyRoomIdClick, ) ListItem( content = { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 85eca4fbae..6428bd097a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -118,8 +118,9 @@ class RoomMemberDetailsPresenter( encryptionService.withdrawVerification(roomMemberId) } is UserProfileEvent.CopyToClipboard -> { - clipboardHelper.copyPlainText(event.text) - snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + clipboardHelper.copyPlainText(event.text) { + snackbarDispatcher.post(SnackbarMessage(CommonStrings.common_copied_to_clipboard)) + } } else -> userProfileState.eventSink(event) } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt index 8d6ef8d518..da79d436e0 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenterTest.kt @@ -19,6 +19,8 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe import io.element.android.features.userprofile.shared.aUserProfileState import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.core.RoomId @@ -49,6 +51,7 @@ import io.element.android.libraries.preferences.test.InMemoryAppPreferencesStore import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.EventsRecorder @@ -85,6 +88,7 @@ class RoomDetailsPresenterTest { analyticsService: AnalyticsService = FakeAnalyticsService(), encryptionService: FakeEncryptionService = FakeEncryptionService(), clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), appPreferencesStore: AppPreferencesStore = InMemoryAppPreferencesStore(), navigator: RoomDetailsNavigator = FakeRoomDetailsNavigator(), notificationCleaner: NotificationCleaner = FakeNotificationCleaner(), @@ -115,12 +119,53 @@ class RoomDetailsPresenterTest { dispatchers = dispatchers, analyticsService = analyticsService, clipboardHelper = clipboardHelper, + toastHelper = toastHelper, appPreferencesStore = appPreferencesStore, notificationCleaner = notificationCleaner, sessionPreferencesStore = sessionPreferencesStore, ) } + @Test + fun `present - copy room id on old device shows a toast`() = runTest { + val room = aJoinedRoom( + roomPermissions = roomPermissions(), + ) + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createRoomDetailsPresenter( + room = room, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { + awaitItem().eventSink(RoomDetailsEvent.CopyRoomId) + assertThat(clipboardHelper.clipboardContents).isEqualTo(room.roomId.value) + assertThat(toastHelper.shownToasts).containsExactly(CommonStrings.common_copied_to_clipboard) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - copy room id on new device does not show a toast`() = runTest { + val room = aJoinedRoom( + roomPermissions = roomPermissions(), + ) + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createRoomDetailsPresenter( + room = room, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + presenter.testWithLifecycleOwner(lifecycleOwner = fakeLifecycleOwner) { + awaitItem().eventSink(RoomDetailsEvent.CopyRoomId) + assertThat(clipboardHelper.clipboardContents).isEqualTo(room.roomId.value) + assertThat(toastHelper.shownToasts).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - initial state is created from initial room info`() = runTest { val room = aJoinedRoom( diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt index 68221a29e4..a71c235857 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupEvent.kt @@ -11,6 +11,7 @@ package io.element.android.features.securebackup.impl.setup sealed interface SecureBackupSetupEvent { data object CreateRecoveryKey : SecureBackupSetupEvent data object RecoveryKeyHasBeenSaved : SecureBackupSetupEvent + data object CopyRecoveryKey : SecureBackupSetupEvent data object Done : SecureBackupSetupEvent data object DismissDialog : SecureBackupSetupEvent } diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt index 538128a0d5..88338c6af4 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenter.kt @@ -22,9 +22,12 @@ import com.freeletics.flowredux.compose.rememberStateAndDispatch import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.loggerTagSetup import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUserStory import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -38,6 +41,8 @@ class SecureBackupSetupPresenter( @Assisted private val isChangeRecoveryKeyUserStory: Boolean, private val stateMachine: SecureBackupSetupStateMachine, private val encryptionService: EncryptionService, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Presenter { @AssistedFactory interface Factory { @@ -60,6 +65,13 @@ class SecureBackupSetupPresenter( } SecureBackupSetupEvent.RecoveryKeyHasBeenSaved -> stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserSavedKey) + SecureBackupSetupEvent.CopyRecoveryKey -> { + val recoveryKey = setupState.recoveryKey() ?: return + clipboardHelper.copyPlainText(recoveryKey, isSensitive = true) { + toastHelper.show(R.string.screen_recovery_key_copied_to_clipboard) + } + stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.UserSavedKey) + } SecureBackupSetupEvent.DismissDialog -> { showSaveConfirmationDialog = false stateAndDispatch.dispatchAction(SecureBackupSetupStateMachine.Event.ClearError) diff --git a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt index 802b9ae070..d7c63e3a39 100644 --- a/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt +++ b/features/securebackup/impl/src/main/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupView.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyView -import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.androidutils.system.startSharePlainTextIntent import io.element.android.libraries.designsystem.atomic.pages.FlowStepPage import io.element.android.libraries.designsystem.components.BigIcon @@ -114,17 +113,10 @@ private fun subtitle(state: SecureBackupSetupState): String { private fun Content( state: SecureBackupSetupState, ) { - val context = LocalContext.current val formattedRecoveryKey = state.recoveryKeyViewState.formattedRecoveryKey - val toastMessage = stringResource(R.string.screen_recovery_key_copied_to_clipboard) val clickLambda = if (formattedRecoveryKey != null) { { - context.copyToClipboard( - text = formattedRecoveryKey, - toastMessage = toastMessage, - isSensitive = true, - ) - state.eventSink.invoke(SecureBackupSetupEvent.RecoveryKeyHasBeenSaved) + state.eventSink.invoke(SecureBackupSetupEvent.CopyRecoveryKey) } } else { if (!state.recoveryKeyViewState.inProgress) { diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupSetupEntryPointTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupSetupEntryPointTest.kt index e37dab748f..3b029b2ba2 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupSetupEntryPointTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/DefaultSecureBackupSetupEntryPointTest.kt @@ -15,6 +15,8 @@ import io.element.android.features.securebackup.api.SecureBackupSetupEntryPoint import io.element.android.features.securebackup.impl.setup.SecureBackupSetupNode import io.element.android.features.securebackup.impl.setup.SecureBackupSetupPresenter import io.element.android.features.securebackup.impl.setup.SecureBackupSetupStateMachine +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService import io.element.android.tests.testutils.node.TestParentNode @@ -40,6 +42,8 @@ class DefaultSecureBackupSetupEntryPointTest { isChangeRecoveryKeyUserStory = isChangeRecoveryKeyUserStory, stateMachine = SecureBackupSetupStateMachine(), encryptionService = FakeEncryptionService(), + clipboardHelper = FakeClipboardHelper(), + toastHelper = FakeToastHelper(), ) }, snackbarDispatcher = SnackbarDispatcher(), diff --git a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt index 6ab0b257a2..2b79863953 100644 --- a/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt +++ b/features/securebackup/impl/src/test/kotlin/io/element/android/features/securebackup/impl/setup/SecureBackupSetupPresenterTest.kt @@ -12,8 +12,13 @@ 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.securebackup.impl.R import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyUserStory import io.element.android.features.securebackup.impl.setup.views.RecoveryKeyViewState +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.matrix.api.encryption.EnableRecoveryProgress import io.element.android.libraries.matrix.api.encryption.EncryptionService import io.element.android.libraries.matrix.test.A_RECOVERY_KEY @@ -186,16 +191,70 @@ class SecureBackupSetupPresenterTest { } } + @Test + fun `present - copy recovery key on old device shows a toast`() = runTest { + val encryptionService = FakeEncryptionService() + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink.invoke(SecureBackupSetupEvent.CreateRecoveryKey) + skipItems(1) + encryptionService.emitEnableRecoveryProgress(EnableRecoveryProgress.Done(A_RECOVERY_KEY)) + val createdState = awaitItem() + createdState.eventSink.invoke(SecureBackupSetupEvent.CopyRecoveryKey) + assertThat(clipboardHelper.clipboardContents).isEqualTo(A_RECOVERY_KEY) + assertThat(clipboardHelper.isSensitive).isTrue() + assertThat(toastHelper.shownToasts).containsExactly(R.string.screen_recovery_key_copied_to_clipboard) + assertThat(awaitItem().setupState).isInstanceOf(SetupState.CreatedAndSaved::class.java) + } + } + + @Test + fun `present - copy recovery key on new device does not show a toast`() = runTest { + val encryptionService = FakeEncryptionService() + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createSecureBackupSetupPresenter( + encryptionService = encryptionService, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink.invoke(SecureBackupSetupEvent.CreateRecoveryKey) + skipItems(1) + encryptionService.emitEnableRecoveryProgress(EnableRecoveryProgress.Done(A_RECOVERY_KEY)) + val createdState = awaitItem() + createdState.eventSink.invoke(SecureBackupSetupEvent.CopyRecoveryKey) + assertThat(clipboardHelper.clipboardContents).isEqualTo(A_RECOVERY_KEY) + assertThat(clipboardHelper.isSensitive).isTrue() + assertThat(toastHelper.shownToasts).isEmpty() + assertThat(awaitItem().setupState).isInstanceOf(SetupState.CreatedAndSaved::class.java) + } + } + private fun createSecureBackupSetupPresenter( isChangeRecoveryKeyUserStory: Boolean = false, encryptionService: EncryptionService = FakeEncryptionService( enableRecoveryLambda = { _, _ -> Result.success("") }, ), + clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), ): SecureBackupSetupPresenter { return SecureBackupSetupPresenter( isChangeRecoveryKeyUserStory = isChangeRecoveryKeyUserStory, stateMachine = SecureBackupSetupStateMachine(), encryptionService = encryptionService, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, ) } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt index ae3e6932ac..bcbacf00fa 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/DefaultTextFileViewer.kt @@ -15,10 +15,16 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.features.viewfolder.api.TextFileViewer import io.element.android.features.viewfolder.impl.file.ColorationMode import io.element.android.features.viewfolder.impl.file.FileContent +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @ContributesBinding(AppScope::class) -class DefaultTextFileViewer : TextFileViewer { +class DefaultTextFileViewer( + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, +) : TextFileViewer { @Composable override fun Render( lines: ImmutableList, @@ -27,6 +33,11 @@ class DefaultTextFileViewer : TextFileViewer { FileContent( lines = lines, colorationMode = ColorationMode.None, + onLineClick = { line -> + clipboardHelper.copyPlainText(line) { + toastHelper.show(CommonStrings.common_line_copied_to_clipboard) + } + }, modifier = modifier ) } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt index c68d630869..a0afd75a28 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/FileContent.kt @@ -23,12 +23,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme -import io.element.android.libraries.androidutils.system.copyToClipboard import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableList @@ -37,6 +35,7 @@ import kotlinx.collections.immutable.ImmutableList internal fun FileContent( lines: ImmutableList, colorationMode: ColorationMode, + onLineClick: (String) -> Unit, modifier: Modifier = Modifier, ) { LazyColumn( @@ -60,6 +59,7 @@ internal fun FileContent( lineNumber = index + 1, line = line, colorationMode = colorationMode, + onClick = { onLineClick(line) }, ) } } @@ -71,18 +71,12 @@ private fun LineRow( lineNumber: Int, line: String, colorationMode: ColorationMode, + onClick: () -> Unit, ) { - val context = LocalContext.current - val toastMessage = stringResource(CommonStrings.common_line_copied_to_clipboard) Row( modifier = Modifier .fillMaxWidth() - .clickable(onClick = { - context.copyToClipboard( - text = line, - toastMessage = toastMessage, - ) - }) + .clickable(onClick = onClick) ) { Text( modifier = Modifier diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt index 2ff6faf4ef..7ba5338fdf 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileEvent.kt @@ -11,4 +11,5 @@ package io.element.android.features.viewfolder.impl.file sealed interface ViewFileEvent { data object SaveOnDisk : ViewFileEvent data object Share : ViewFileEvent + data class CopyToClipboard(val text: String) : ViewFileEvent } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt index 571f3eb053..0ffea1dc08 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenter.kt @@ -18,8 +18,11 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -30,6 +33,8 @@ class ViewFilePresenter( private val fileContentReader: FileContentReader, private val fileShare: FileShare, private val fileSave: FileSave, + private val clipboardHelper: ClipboardHelper, + private val toastHelper: ToastHelper, ) : Presenter { @AssistedFactory interface Factory { @@ -48,6 +53,9 @@ class ViewFilePresenter( when (event) { ViewFileEvent.Share -> coroutineScope.share(path) ViewFileEvent.SaveOnDisk -> coroutineScope.save(path) + is ViewFileEvent.CopyToClipboard -> clipboardHelper.copyPlainText(event.text) { + toastHelper.show(CommonStrings.common_line_copied_to_clipboard) + } } } diff --git a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt index abe7840718..ee35614a52 100644 --- a/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt +++ b/features/viewfolder/impl/src/main/kotlin/io/element/android/features/viewfolder/impl/file/ViewFileView.kt @@ -80,6 +80,7 @@ fun ViewFileView( modifier = Modifier.weight(1f), lines = state.lines.data.toImmutableList(), colorationMode = state.colorationMode, + onLineClick = { state.eventSink(ViewFileEvent.CopyToClipboard(it)) }, ) is AsyncData.Failure -> AsyncFailure(throwable = state.lines.error, onRetry = null) } diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt index c3be8d84b9..7e47d0064d 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt @@ -18,8 +18,13 @@ import io.element.android.features.viewfolder.impl.file.FileSave import io.element.android.features.viewfolder.impl.file.FileShare import io.element.android.features.viewfolder.impl.file.ViewFileEvent import io.element.android.features.viewfolder.impl.file.ViewFilePresenter +import io.element.android.libraries.androidutils.clipboard.ClipboardHelper +import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper +import io.element.android.libraries.androidutils.toast.FakeToastHelper +import io.element.android.libraries.androidutils.toast.ToastHelper import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.matrix.test.AN_EXCEPTION +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.WarmUpRule import kotlinx.coroutines.test.runTest import org.junit.Rule @@ -126,17 +131,51 @@ class ViewFilePresenterTest { } } + @Test + fun `present - copy line on old device shows a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = true) + val toastHelper = FakeToastHelper() + val presenter = createPresenter(clipboardHelper = clipboardHelper, toastHelper = toastHelper) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink(ViewFileEvent.CopyToClipboard("aLine")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("aLine") + assertThat(toastHelper.shownToasts).containsExactly(CommonStrings.common_line_copied_to_clipboard) + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - copy line on new device does not show a toast`() = runTest { + val clipboardHelper = FakeClipboardHelper(isOldDevice = false) + val toastHelper = FakeToastHelper() + val presenter = createPresenter(clipboardHelper = clipboardHelper, toastHelper = toastHelper) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + awaitItem().eventSink(ViewFileEvent.CopyToClipboard("aLine")) + assertThat(clipboardHelper.clipboardContents).isEqualTo("aLine") + assertThat(toastHelper.shownToasts).isEmpty() + cancelAndIgnoreRemainingEvents() + } + } + private fun createPresenter( path: String = "aPath", name: String = "aName", fileContentReader: FileContentReader = FakeFileContentReader(), fileShare: FileShare = FakeFileShare(), fileSave: FileSave = FakeFileSave(), + clipboardHelper: ClipboardHelper = FakeClipboardHelper(), + toastHelper: ToastHelper = FakeToastHelper(), ) = ViewFilePresenter( path = path, name = name, fileContentReader = fileContentReader, fileShare = fileShare, fileSave = fileSave, + clipboardHelper = clipboardHelper, + toastHelper = toastHelper, ) } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/AndroidClipboardHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/AndroidClipboardHelper.kt index ce6f61f280..c452902404 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/AndroidClipboardHelper.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/AndroidClipboardHelper.kt @@ -9,8 +9,11 @@ package io.element.android.libraries.androidutils.clipboard import android.content.ClipData +import android.content.ClipDescription import android.content.ClipboardManager import android.content.Context +import android.os.Build +import android.os.PersistableBundle import androidx.core.content.getSystemService import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding @@ -24,7 +27,30 @@ class AndroidClipboardHelper( ) : ClipboardHelper { private val clipboardManager = requireNotNull(context.getSystemService()) - override fun copyPlainText(text: String) { - clipboardManager.setPrimaryClip(ClipData.newPlainText("", text)) + override fun copyPlainText( + text: String, + isSensitive: Boolean, + onSuccessOnOldDevice: () -> Unit, + ) { + val clipData = ClipData.newPlainText("", text) + if (isSensitive) { + clipData.description.extras = PersistableBundle().apply { + putBoolean(EXTRA_IS_SENSITIVE, true) + } + } + clipboardManager.setPrimaryClip(clipData) + // On Android 13+, the system already displays a confirmation when content is copied to the clipboard. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + onSuccessOnOldDevice() + } + } + + private companion object { + // The constant only exists from API 33, but the documentation recommends using its value on older versions too. + val EXTRA_IS_SENSITIVE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ClipDescription.EXTRA_IS_SENSITIVE + } else { + "android.content.extra.IS_SENSITIVE" + } } } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt index 1d0ab8e3c4..0add39fe76 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/ClipboardHelper.kt @@ -8,9 +8,18 @@ package io.element.android.libraries.androidutils.clipboard -/** - * Wrapper class for handling clipboard operations so it can be used in JVM environments. - */ interface ClipboardHelper { - fun copyPlainText(text: String) + /** + * Copy a text to the clipboard. + * + * @param text the text to copy + * @param isSensitive if true, the clip is flagged so the system clipboard preview (Android 13+) and keyboards hide its content. + * @param onSuccessOnOldDevice invoked after the copy only on Android versions older than 13, since newer versions already + * display a confirmation. Can be used to render a Toast or a Snackbar. + */ + fun copyPlainText( + text: String, + isSensitive: Boolean = false, + onSuccessOnOldDevice: () -> Unit, + ) } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/FakeClipboardHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/FakeClipboardHelper.kt index 9f91b3a4ce..a46053662e 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/FakeClipboardHelper.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/clipboard/FakeClipboardHelper.kt @@ -8,10 +8,21 @@ package io.element.android.libraries.androidutils.clipboard -class FakeClipboardHelper : ClipboardHelper { +class FakeClipboardHelper( + private val isOldDevice: Boolean = true, +) : ClipboardHelper { var clipboardContents: Any? = null + var isSensitive: Boolean? = null - override fun copyPlainText(text: String) { + override fun copyPlainText( + text: String, + isSensitive: Boolean, + onSuccessOnOldDevice: () -> Unit, + ) { clipboardContents = text + this.isSensitive = isSensitive + if (isOldDevice) { + onSuccessOnOldDevice() + } } } diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt deleted file mode 100644 index e20e80884f..0000000000 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/CopyToClipboardUseCase.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2025 Element Creations Ltd. - * Copyright 2022-2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.androidutils.system - -import android.content.ClipData -import android.content.ClipDescription -import android.content.ClipboardManager -import android.content.Context -import android.os.Build -import android.os.PersistableBundle -import androidx.core.content.getSystemService - -class CopyToClipboardUseCase( - private val context: Context, -) { - /** - * @param isSensitive if true, the clip is flagged so the system clipboard preview (Android 13+) and keyboards hide its content. - */ - fun execute(text: CharSequence, isSensitive: Boolean = false) { - val clipData = ClipData.newPlainText("", text) - if (isSensitive) { - clipData.description.extras = PersistableBundle().apply { - putBoolean(EXTRA_IS_SENSITIVE, true) - } - } - context.getSystemService()?.setPrimaryClip(clipData) - } - - private companion object { - // The constant only exists from API 33, but the documentation recommends using its value on older versions too. - val EXTRA_IS_SENSITIVE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - ClipDescription.EXTRA_IS_SENSITIVE - } else { - "android.content.extra.IS_SENSITIVE" - } - } -} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt index 076cda5704..234697226f 100644 --- a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/system/SystemUtils.kt @@ -50,26 +50,6 @@ fun Context.getVersionCodeFromManifest(): Long { ) } -// ============================================================================================================== -// Clipboard helper -// ============================================================================================================== - -/** - * Copy a text to the clipboard, and display a Toast when done. - * - * @receiver the context - * @param text the text to copy - * @param toastMessage content of the toast message as a String resource. Null for no toast - */ -fun Context.copyToClipboard( - text: CharSequence, - toastMessage: String? = null, - isSensitive: Boolean = false, -) { - CopyToClipboardUseCase(this).execute(text, isSensitive) - toastMessage?.let { toast(it) } -} - /** * Shows notification settings for the current app. * In android O will directly opens the notification settings, in lower version it will show the App settings diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/AndroidToastHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/AndroidToastHelper.kt new file mode 100644 index 0000000000..d2ba027f19 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/AndroidToastHelper.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.toast + +import android.content.Context +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesBinding +import io.element.android.libraries.androidutils.system.toast +import io.element.android.libraries.di.annotations.ApplicationContext + +@ContributesBinding(AppScope::class) +class AndroidToastHelper( + @ApplicationContext private val context: Context, +) : ToastHelper { + override fun show(resId: Int) { + context.toast(resId) + } +} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/FakeToastHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/FakeToastHelper.kt new file mode 100644 index 0000000000..a4b26d1343 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/FakeToastHelper.kt @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.toast + +class FakeToastHelper : ToastHelper { + val shownToasts = mutableListOf() + + override fun show(resId: Int) { + shownToasts.add(resId) + } +} diff --git a/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/ToastHelper.kt b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/ToastHelper.kt new file mode 100644 index 0000000000..e48022a455 --- /dev/null +++ b/libraries/androidutils/src/main/kotlin/io/element/android/libraries/androidutils/toast/ToastHelper.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.libraries.androidutils.toast + +import androidx.annotation.StringRes + +interface ToastHelper { + fun show(@StringRes resId: Int) +} From ab20281b260f14fe46c5013f4f230aedf16cd99d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:40:38 +0200 Subject: [PATCH 31/92] Fix wrong package names and namespaces in several modules Some modules were using package names or namespaces inherited from copy-paste or from older module names (e.g. `features.logout.impl` in the deactivation module, `features.api` in the migration api module, `sessionstorage` in the cachestore module), or containing typos (`roomaliasesolver`, `roomdetetailsedit`). Move the sources to the package matching their module, update the imports, and rename the impacted screenshots accordingly. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/src/main/kotlin/io/element/android/x/di/AppBindings.kt | 2 +- .../kotlin/io/element/android/appnav/room/RoomFlowNode.kt | 4 ++-- .../impl/AccountDeactivationEvent.kt | 2 +- .../{logout => deactivation}/impl/AccountDeactivationNode.kt | 2 +- .../impl/AccountDeactivationPresenter.kt | 2 +- .../impl/AccountDeactivationState.kt | 2 +- .../impl/AccountDeactivationStatePreviewParam.kt | 2 +- .../{logout => deactivation}/impl/AccountDeactivationView.kt | 5 ++--- .../impl/DefaultAccountDeactivationEntryPoint.kt | 2 +- .../impl/ui/AccountDeactivationActionDialog.kt | 2 +- .../impl/ui/AccountDeactivationConfirmationDialog.kt | 2 +- .../impl/AccountDeactivationPresenterTest.kt | 2 +- .../impl/AccountDeactivationViewTest.kt | 3 +-- .../impl/DefaultAccountDeactivationEntryPointTest.kt | 2 +- .../composer/DefaultVoiceMessageComposerPresenterTest.kt | 2 +- .../FakeDefaultVoiceMessageComposerPresenterFactory.kt | 2 +- .../features/{ => migration}/api/MigrationEntryPoint.kt | 2 +- .../android/features/{ => migration}/api/MigrationState.kt | 2 +- .../features/migration/impl/DefaultMigrationEntryPoint.kt | 4 ++-- .../android/features/migration/impl/MigrationPresenter.kt | 2 +- .../features/migration/impl/MigrationStatePreviewParam.kt | 2 +- .../element/android/features/migration/impl/MigrationView.kt | 2 +- .../test/FakeChangeRoomMemberRolesEntryPoint.kt | 2 +- .../test/FakeRolesAndPermissionsEntryPoint.kt | 2 +- .../api/RoomAliasResolverEntryPoint.kt | 2 +- .../impl/DefaultRoomAliasResolverEntryPoint.kt | 2 +- .../features/roomaliasresolver/impl/RoomAliasResolverNode.kt | 2 +- .../impl/DefaultRoomAliasResolverEntryPointTest.kt | 2 +- .../roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt | 4 ++-- features/roomdetailsedit/test/build.gradle.kts | 2 +- .../features/space/impl/DefaultSpaceEntryPointTest.kt | 2 +- .../features/startchat/impl/root/StartChatPresenterTest.kt | 2 +- features/startchat/test/build.gradle.kts | 2 +- .../{invitepeople => startchat}/test/FakeStartDMAction.kt | 2 +- .../features/userprofile/impl/UserProfilePresenterTest.kt | 2 +- .../libraries/{mediaplayer => audio}/test/FakeAudioFocus.kt | 2 +- .../impl/DatabaseCacheStoreTest.kt | 4 +--- .../{sessionstorage => cachestore}/impl/Fixtures.kt | 2 +- .../{sessionstorage => cachestore}/test/CacheData.kt | 2 +- .../test/InMemoryCacheStore.kt | 2 +- .../libraries/mediaplayer/impl/DefaultMediaPlayerTest.kt | 2 +- .../mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt | 2 +- libraries/wellknown/test/build.gradle.kts | 2 +- ...s.deactivation.impl_AccountDeactivationView_Day_0_en.png} | 0 ...s.deactivation.impl_AccountDeactivationView_Day_1_en.png} | 0 ...s.deactivation.impl_AccountDeactivationView_Day_2_en.png} | 0 ...s.deactivation.impl_AccountDeactivationView_Day_3_en.png} | 0 ...s.deactivation.impl_AccountDeactivationView_Day_4_en.png} | 0 ...deactivation.impl_AccountDeactivationView_Night_0_en.png} | 0 ...deactivation.impl_AccountDeactivationView_Night_1_en.png} | 0 ...deactivation.impl_AccountDeactivationView_Night_2_en.png} | 0 ...deactivation.impl_AccountDeactivationView_Night_3_en.png} | 0 ...deactivation.impl_AccountDeactivationView_Night_4_en.png} | 0 53 files changed, 47 insertions(+), 51 deletions(-) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationEvent.kt (91%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationNode.kt (95%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationPresenter.kt (98%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationState.kt (94%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationStatePreviewParam.kt (97%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationView.kt (98%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/DefaultAccountDeactivationEntryPoint.kt (93%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/ui/AccountDeactivationActionDialog.kt (96%) rename features/deactivation/impl/src/main/kotlin/io/element/android/features/{logout => deactivation}/impl/ui/AccountDeactivationConfirmationDialog.kt (94%) rename features/deactivation/impl/src/test/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationPresenterTest.kt (99%) rename features/deactivation/impl/src/test/kotlin/io/element/android/features/{logout => deactivation}/impl/AccountDeactivationViewTest.kt (98%) rename features/deactivation/impl/src/test/kotlin/io/element/android/features/{logout => deactivation}/impl/DefaultAccountDeactivationEntryPointTest.kt (95%) rename features/migration/api/src/main/kotlin/io/element/android/features/{ => migration}/api/MigrationEntryPoint.kt (90%) rename features/migration/api/src/main/kotlin/io/element/android/features/{ => migration}/api/MigrationState.kt (88%) rename features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/{changeroommemberroles => rolesandpermissions}/test/FakeChangeRoomMemberRolesEntryPoint.kt (93%) rename features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/{changeroommemberroles => rolesandpermissions}/test/FakeRolesAndPermissionsEntryPoint.kt (91%) rename features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/{roomaliasesolver => roomaliasresolver}/api/RoomAliasResolverEntryPoint.kt (94%) rename features/startchat/test/src/main/kotlin/io/element/android/features/{invitepeople => startchat}/test/FakeStartDMAction.kt (95%) rename libraries/audio/test/src/main/kotlin/io/element/android/libraries/{mediaplayer => audio}/test/FakeAudioFocus.kt (94%) rename libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/{sessionstorage => cachestore}/impl/DatabaseCacheStoreTest.kt (93%) rename libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/{sessionstorage => cachestore}/impl/Fixtures.kt (89%) rename libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/{sessionstorage => cachestore}/test/CacheData.kt (87%) rename libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/{sessionstorage => cachestore}/test/InMemoryCacheStore.kt (93%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Day_0_en.png => features.deactivation.impl_AccountDeactivationView_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Day_1_en.png => features.deactivation.impl_AccountDeactivationView_Day_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Day_2_en.png => features.deactivation.impl_AccountDeactivationView_Day_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Day_3_en.png => features.deactivation.impl_AccountDeactivationView_Day_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Day_4_en.png => features.deactivation.impl_AccountDeactivationView_Day_4_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Night_0_en.png => features.deactivation.impl_AccountDeactivationView_Night_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Night_1_en.png => features.deactivation.impl_AccountDeactivationView_Night_1_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Night_2_en.png => features.deactivation.impl_AccountDeactivationView_Night_2_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Night_3_en.png => features.deactivation.impl_AccountDeactivationView_Night_3_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.logout.impl_AccountDeactivationView_Night_4_en.png => features.deactivation.impl_AccountDeactivationView_Night_4_en.png} (100%) 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 abdb6b1394..e5411e739c 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 @@ -11,12 +11,12 @@ package io.element.android.x.di import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesTo import dev.zacsweers.metro.Multibinds -import io.element.android.features.api.MigrationEntryPoint import io.element.android.features.callnative.api.NativeCallPip import io.element.android.features.enterprise.api.AppStartupHook import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.lockscreen.api.LockScreenEntryPoint import io.element.android.features.lockscreen.api.LockScreenService +import io.element.android.features.migration.api.MigrationEntryPoint import io.element.android.features.rageshake.api.reporter.BugReporter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 7080b34f8e..201a4adab0 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -28,8 +28,8 @@ import io.element.android.appnav.room.joined.JoinedRoomFlowNode import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode import io.element.android.appnav.room.joined.LoadingRoomNodeView import io.element.android.features.joinroom.api.JoinRoomEntryPoint -import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint -import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint.Params +import io.element.android.features.roomaliasresolver.api.RoomAliasResolverEntryPoint +import io.element.android.features.roomaliasresolver.api.RoomAliasResolverEntryPoint.Params import io.element.android.features.roomdirectory.api.RoomDescription import io.element.android.libraries.architecture.BackstackView import io.element.android.libraries.architecture.BaseFlowNode diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationEvent.kt similarity index 91% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationEvent.kt index cc1f5093fd..16bed4f04a 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationEvent.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationEvent.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl sealed interface AccountDeactivationEvent { data class SetEraseData(val eraseData: Boolean) : AccountDeactivationEvent diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationNode.kt similarity index 95% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationNode.kt index 44be7b5d53..a3b648a03d 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationNode.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationNode.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenter.kt similarity index 98% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenter.kt index 11e658aa7f..3dd527bc9e 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenter.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenter.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationState.kt similarity index 94% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationState.kt index ebf9fec3c4..73ddf6857e 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationState.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationState.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import android.os.Parcelable import io.element.android.libraries.architecture.AsyncAction diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationStatePreviewParam.kt similarity index 97% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationStatePreviewParam.kt index b5ab948b05..49bb01f71e 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationStatePreviewParam.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationStatePreviewParam.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncAction diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationView.kt similarity index 98% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationView.kt index 0d2bbefd89..4c256bf3b4 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/AccountDeactivationView.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationView.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -44,8 +44,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons -import io.element.android.features.deactivation.impl.R -import io.element.android.features.logout.impl.ui.AccountDeactivationActionDialog +import io.element.android.features.deactivation.impl.ui.AccountDeactivationActionDialog import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.form.textFieldState diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPoint.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPoint.kt similarity index 93% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPoint.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPoint.kt index c93d519e67..c0c62cf6cc 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPoint.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPoint.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationActionDialog.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationActionDialog.kt similarity index 96% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationActionDialog.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationActionDialog.kt index 84037d7eb1..f4862d0df7 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationActionDialog.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationActionDialog.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl.ui +package io.element.android.features.deactivation.impl.ui import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource diff --git a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationConfirmationDialog.kt b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationConfirmationDialog.kt similarity index 94% rename from features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationConfirmationDialog.kt rename to features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationConfirmationDialog.kt index ab9d87c543..7e255b0b5b 100644 --- a/features/deactivation/impl/src/main/kotlin/io/element/android/features/logout/impl/ui/AccountDeactivationConfirmationDialog.kt +++ b/features/deactivation/impl/src/main/kotlin/io/element/android/features/deactivation/impl/ui/AccountDeactivationConfirmationDialog.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl.ui +package io.element.android.features.deactivation.impl.ui import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenterTest.kt similarity index 99% rename from features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt rename to features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenterTest.kt index d6402c2f59..8a9aac510b 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationPresenterTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationPresenterTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationViewTest.kt similarity index 98% rename from features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt rename to features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationViewTest.kt index 5c650fea9d..4da3f236a9 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/AccountDeactivationViewTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/AccountDeactivationViewTest.kt @@ -8,7 +8,7 @@ @file:OptIn(ExperimentalTestApi::class) -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.activity.ComponentActivity import androidx.compose.ui.test.AndroidComposeUiTest @@ -16,7 +16,6 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.v2.runAndroidComposeUiTest -import io.element.android.features.deactivation.impl.R import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.A_PASSWORD diff --git a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPointTest.kt b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPointTest.kt similarity index 95% rename from features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPointTest.kt rename to features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPointTest.kt index a9fdf3ff36..0a6fbb801c 100644 --- a/features/deactivation/impl/src/test/kotlin/io/element/android/features/logout/impl/DefaultAccountDeactivationEntryPointTest.kt +++ b/features/deactivation/impl/src/test/kotlin/io/element/android/features/deactivation/impl/DefaultAccountDeactivationEntryPointTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.logout.impl +package io.element.android.features.deactivation.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt index e4c3310089..8fac783581 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt @@ -21,6 +21,7 @@ import io.element.android.features.messages.impl.messagecomposer.aReplyMode import io.element.android.features.messages.test.FakeMessageComposerContext import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.audio.api.AudioFocusRequester +import io.element.android.libraries.audio.test.FakeAudioFocus import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.timeline.Timeline @@ -28,7 +29,6 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.timeline.FakeTimeline -import io.element.android.libraries.mediaplayer.test.FakeAudioFocus import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaupload.api.MediaOptimizationConfig import io.element.android.libraries.mediaupload.impl.DefaultMediaSender diff --git a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/voicemessages/composer/FakeDefaultVoiceMessageComposerPresenterFactory.kt b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/voicemessages/composer/FakeDefaultVoiceMessageComposerPresenterFactory.kt index 466bfba4fd..7f608fe283 100644 --- a/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/voicemessages/composer/FakeDefaultVoiceMessageComposerPresenterFactory.kt +++ b/features/messages/test/src/main/kotlin/io/element/android/features/messages/test/timeline/voicemessages/composer/FakeDefaultVoiceMessageComposerPresenterFactory.kt @@ -11,8 +11,8 @@ package io.element.android.features.messages.test.timeline.voicemessages.compose import io.element.android.features.messages.impl.voicemessages.composer.DefaultVoiceMessageComposerPresenter import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer import io.element.android.features.messages.test.FakeMessageComposerContext +import io.element.android.libraries.audio.test.FakeAudioFocus import io.element.android.libraries.matrix.api.timeline.Timeline -import io.element.android.libraries.mediaplayer.test.FakeAudioFocus import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaSender diff --git a/features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationEntryPoint.kt b/features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationEntryPoint.kt similarity index 90% rename from features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationEntryPoint.kt rename to features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationEntryPoint.kt index 471ee30d22..57faede46e 100644 --- a/features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationEntryPoint.kt +++ b/features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationEntryPoint.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.api +package io.element.android.features.migration.api import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationState.kt b/features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationState.kt similarity index 88% rename from features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationState.kt rename to features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationState.kt index 000d898a6a..ca5b9a4882 100644 --- a/features/migration/api/src/main/kotlin/io/element/android/features/api/MigrationState.kt +++ b/features/migration/api/src/main/kotlin/io/element/android/features/migration/api/MigrationState.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.api +package io.element.android.features.migration.api import io.element.android.libraries.architecture.AsyncData diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/DefaultMigrationEntryPoint.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/DefaultMigrationEntryPoint.kt index edefa7ce75..df5f38a797 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/DefaultMigrationEntryPoint.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/DefaultMigrationEntryPoint.kt @@ -12,8 +12,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.api.MigrationEntryPoint -import io.element.android.features.api.MigrationState +import io.element.android.features.migration.api.MigrationEntryPoint +import io.element.android.features.migration.api.MigrationState @ContributesBinding(AppScope::class) class DefaultMigrationEntryPoint( diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt index 2d78e5f706..1301c0e309 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationPresenter.kt @@ -18,7 +18,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn -import io.element.android.features.api.MigrationState +import io.element.android.features.migration.api.MigrationState import io.element.android.features.migration.impl.migrations.AppMigration import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.architecture.Presenter diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationStatePreviewParam.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationStatePreviewParam.kt index de25284867..b43681427e 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationStatePreviewParam.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationStatePreviewParam.kt @@ -9,7 +9,7 @@ package io.element.android.features.migration.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import io.element.android.features.api.MigrationState +import io.element.android.features.migration.api.MigrationState import io.element.android.libraries.architecture.AsyncData internal class MigrationStatePreviewParam : PreviewParameterProvider { diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt index c7f4dd678a..c3f32eb06b 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/MigrationView.kt @@ -18,7 +18,7 @@ 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.api.MigrationState +import io.element.android.features.migration.api.MigrationState import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeChangeRoomMemberRolesEntryPoint.kt similarity index 93% rename from features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt rename to features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeChangeRoomMemberRolesEntryPoint.kt index 0526afc938..d50d872a1d 100644 --- a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeChangeRoomMemberRolesEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeChangeRoomMemberRolesEntryPoint.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.test +package io.element.android.features.rolesandpermissions.test import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeRolesAndPermissionsEntryPoint.kt similarity index 91% rename from features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt rename to features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeRolesAndPermissionsEntryPoint.kt index 62cf3285f3..00d9059a3d 100644 --- a/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/changeroommemberroles/test/FakeRolesAndPermissionsEntryPoint.kt +++ b/features/rolesandpermissions/test/src/main/kotlin/io/element/android/features/rolesandpermissions/test/FakeRolesAndPermissionsEntryPoint.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.changeroommemberroles.test +package io.element.android.features.rolesandpermissions.test import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasresolver/api/RoomAliasResolverEntryPoint.kt similarity index 94% rename from features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt rename to features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasresolver/api/RoomAliasResolverEntryPoint.kt index ce0270033c..a2a1d9c163 100644 --- a/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasesolver/api/RoomAliasResolverEntryPoint.kt +++ b/features/roomaliasresolver/api/src/main/kotlin/io/element/android/features/roomaliasresolver/api/RoomAliasResolverEntryPoint.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.roomaliasesolver.api +package io.element.android.features.roomaliasresolver.api import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt index c6f8966131..2eb83a1382 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPoint.kt @@ -12,7 +12,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding -import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.features.roomaliasresolver.api.RoomAliasResolverEntryPoint import io.element.android.libraries.architecture.createNode @ContributesBinding(AppScope::class) diff --git a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt index 041d43cb26..bff41efb82 100644 --- a/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt +++ b/features/roomaliasresolver/impl/src/main/kotlin/io/element/android/features/roomaliasresolver/impl/RoomAliasResolverNode.kt @@ -16,7 +16,7 @@ import com.bumble.appyx.core.plugin.Plugin import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.features.roomaliasresolver.api.RoomAliasResolverEntryPoint import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope diff --git a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt index a519032ed2..f261e56adc 100644 --- a/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt +++ b/features/roomaliasresolver/impl/src/test/kotlin/io/element/android/features/roomaliasresolver/impl/DefaultRoomAliasResolverEntryPointTest.kt @@ -11,7 +11,7 @@ package io.element.android.features.roomaliasresolver.impl import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat -import io.element.android.features.roomaliasesolver.api.RoomAliasResolverEntryPoint +import io.element.android.features.roomaliasresolver.api.RoomAliasResolverEntryPoint import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.test.A_ROOM_ALIAS import io.element.android.tests.testutils.lambda.lambdaError diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt index 9a97380d21..4baf43c477 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPointTest.kt @@ -13,12 +13,12 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat import io.element.android.features.call.test.FakeElementCallEntryPoint -import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint -import io.element.android.features.changeroommemberroles.test.FakeRolesAndPermissionsEntryPoint import io.element.android.features.knockrequests.test.FakeKnockRequestsListEntryPoint import io.element.android.features.messages.test.FakeMessagesEntryPoint import io.element.android.features.poll.test.history.FakePollHistoryEntryPoint import io.element.android.features.reportroom.test.FakeReportRoomEntryPoint +import io.element.android.features.rolesandpermissions.test.FakeChangeRoomMemberRolesEntryPoint +import io.element.android.features.rolesandpermissions.test.FakeRolesAndPermissionsEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetailsedit.test.FakeRoomDetailsEditEntryPoint import io.element.android.features.securityandprivacy.test.FakeSecurityAndPrivacyEntryPoint diff --git a/features/roomdetailsedit/test/build.gradle.kts b/features/roomdetailsedit/test/build.gradle.kts index ff9110eb49..352d3eb5cf 100644 --- a/features/roomdetailsedit/test/build.gradle.kts +++ b/features/roomdetailsedit/test/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.features.roomdetetailsedit.test" + namespace = "io.element.android.features.roomdetailsedit.test" } dependencies { diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt index c0ec58d267..bef7053c66 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/DefaultSpaceEntryPointTest.kt @@ -12,8 +12,8 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.google.common.truth.Truth.assertThat -import io.element.android.features.changeroommemberroles.test.FakeChangeRoomMemberRolesEntryPoint import io.element.android.features.createroom.api.FakeCreateRoomEntryPoint +import io.element.android.features.rolesandpermissions.test.FakeChangeRoomMemberRolesEntryPoint import io.element.android.features.space.api.SpaceEntryPoint import io.element.android.features.space.impl.di.FakeSpaceFlowGraph import io.element.android.libraries.matrix.api.core.RoomId diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt index 39d6f34b21..c656e83858 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/root/StartChatPresenterTest.kt @@ -10,12 +10,12 @@ package io.element.android.features.startchat.impl.root import androidx.compose.runtime.MutableState import com.google.common.truth.Truth.assertThat -import io.element.android.features.invitepeople.test.FakeStartDMAction import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.StartDMAction import io.element.android.features.startchat.impl.userlist.FakeUserListPresenter import io.element.android.features.startchat.impl.userlist.FakeUserListPresenterFactory import io.element.android.features.startchat.impl.userlist.UserListDataStore +import io.element.android.features.startchat.test.FakeStartDMAction import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId diff --git a/features/startchat/test/build.gradle.kts b/features/startchat/test/build.gradle.kts index d6d1471832..2032738fe3 100644 --- a/features/startchat/test/build.gradle.kts +++ b/features/startchat/test/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.features.invitepeople.test" + namespace = "io.element.android.features.startchat.test" } dependencies { diff --git a/features/startchat/test/src/main/kotlin/io/element/android/features/invitepeople/test/FakeStartDMAction.kt b/features/startchat/test/src/main/kotlin/io/element/android/features/startchat/test/FakeStartDMAction.kt similarity index 95% rename from features/startchat/test/src/main/kotlin/io/element/android/features/invitepeople/test/FakeStartDMAction.kt rename to features/startchat/test/src/main/kotlin/io/element/android/features/startchat/test/FakeStartDMAction.kt index c4969df4d9..70cbe1883c 100644 --- a/features/startchat/test/src/main/kotlin/io/element/android/features/invitepeople/test/FakeStartDMAction.kt +++ b/features/startchat/test/src/main/kotlin/io/element/android/features/startchat/test/FakeStartDMAction.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.invitepeople.test +package io.element.android.features.startchat.test import androidx.compose.runtime.MutableState import io.element.android.features.startchat.api.StartDMAction diff --git a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt index 57a7ae273f..b163b04ce3 100644 --- a/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt +++ b/features/userprofile/impl/src/test/kotlin/io/element/android/features/userprofile/impl/UserProfilePresenterTest.kt @@ -15,9 +15,9 @@ import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.test.FakeSessionEnterpriseService -import io.element.android.features.invitepeople.test.FakeStartDMAction import io.element.android.features.startchat.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.startchat.api.StartDMAction +import io.element.android.features.startchat.test.FakeStartDMAction import io.element.android.features.userprofile.api.UserProfileEvent import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileVerificationState diff --git a/libraries/audio/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeAudioFocus.kt b/libraries/audio/test/src/main/kotlin/io/element/android/libraries/audio/test/FakeAudioFocus.kt similarity index 94% rename from libraries/audio/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeAudioFocus.kt rename to libraries/audio/test/src/main/kotlin/io/element/android/libraries/audio/test/FakeAudioFocus.kt index ecb263dbb3..a75a71fc57 100644 --- a/libraries/audio/test/src/main/kotlin/io/element/android/libraries/mediaplayer/test/FakeAudioFocus.kt +++ b/libraries/audio/test/src/main/kotlin/io/element/android/libraries/audio/test/FakeAudioFocus.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.mediaplayer.test +package io.element.android.libraries.audio.test import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.audio.api.AudioFocusRequester diff --git a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStoreTest.kt similarity index 93% rename from libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt rename to libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStoreTest.kt index 36d7e05532..d3581f9686 100644 --- a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseCacheStoreTest.kt +++ b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/DatabaseCacheStoreTest.kt @@ -5,13 +5,11 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.sessionstorage.impl +package io.element.android.libraries.cachestore.impl import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver import com.google.common.truth.Truth.assertThat import io.element.android.libraries.cachestore.api.CacheData -import io.element.android.libraries.cachestore.impl.CacheDatabase -import io.element.android.libraries.cachestore.impl.DatabaseCacheStore import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before diff --git a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/Fixtures.kt similarity index 89% rename from libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt rename to libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/Fixtures.kt index 3dca9efaf3..149f21ecfb 100644 --- a/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt +++ b/libraries/cachestore/impl/src/test/kotlin/io/element/android/libraries/cachestore/impl/Fixtures.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.sessionstorage.impl +package io.element.android.libraries.cachestore.impl import io.element.android.libraries.cachestore.CacheData import java.util.Date diff --git a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/CacheData.kt similarity index 87% rename from libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt rename to libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/CacheData.kt index 30633e8ff9..b7800bdf86 100644 --- a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/CacheData.kt +++ b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/CacheData.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.sessionstorage.test +package io.element.android.libraries.cachestore.test import io.element.android.libraries.cachestore.api.CacheData diff --git a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/InMemoryCacheStore.kt similarity index 93% rename from libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt rename to libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/InMemoryCacheStore.kt index f029c8bcde..132063541e 100644 --- a/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/sessionstorage/test/InMemoryCacheStore.kt +++ b/libraries/cachestore/test/src/main/kotlin/io/element/android/libraries/cachestore/test/InMemoryCacheStore.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.sessionstorage.test +package io.element.android.libraries.cachestore.test import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.cachestore.api.CacheStore diff --git a/libraries/mediaplayer/impl/src/test/kotlin/io/element/android/libraries/mediaplayer/impl/DefaultMediaPlayerTest.kt b/libraries/mediaplayer/impl/src/test/kotlin/io/element/android/libraries/mediaplayer/impl/DefaultMediaPlayerTest.kt index f7a748b636..7807b3ac65 100644 --- a/libraries/mediaplayer/impl/src/test/kotlin/io/element/android/libraries/mediaplayer/impl/DefaultMediaPlayerTest.kt +++ b/libraries/mediaplayer/impl/src/test/kotlin/io/element/android/libraries/mediaplayer/impl/DefaultMediaPlayerTest.kt @@ -14,8 +14,8 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.libraries.audio.api.AudioFocus import io.element.android.libraries.audio.api.AudioFocusRequester +import io.element.android.libraries.audio.test.FakeAudioFocus import io.element.android.libraries.mediaplayer.api.MediaPlayer -import io.element.android.libraries.mediaplayer.test.FakeAudioFocus import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value import kotlinx.coroutines.TimeoutCancellationException diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt index 3ca702a47f..bd25229237 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/DefaultMediaViewerEntryPointTest.kt @@ -14,12 +14,12 @@ import com.bumble.appyx.core.modality.BuildContext import com.google.common.truth.Truth.assertThat import io.element.android.features.contentscanner.api.ContentScannerService import io.element.android.features.enterprise.test.FakeEnterpriseService +import io.element.android.libraries.audio.test.FakeAudioFocus import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.media.FakeMatrixMediaLoader import io.element.android.libraries.matrix.ui.media.contentvalidation.NoopEventContentValidationCache -import io.element.android.libraries.mediaplayer.test.FakeAudioFocus import io.element.android.libraries.mediaviewer.api.AvatarInfo import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint import io.element.android.libraries.mediaviewer.impl.datasource.createTimelineMediaGalleryDataSource diff --git a/libraries/wellknown/test/build.gradle.kts b/libraries/wellknown/test/build.gradle.kts index 7f12f380c7..5f5e94a2ff 100644 --- a/libraries/wellknown/test/build.gradle.kts +++ b/libraries/wellknown/test/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.features.wellknown.test" + namespace = "io.element.android.libraries.wellknown.test" } dependencies { diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_1_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_2_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_3_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Day_4_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Day_4_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_1_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_1_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_1_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_2_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_2_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_2_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_3_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_3_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_4_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.logout.impl_AccountDeactivationView_Night_4_en.png rename to tests/uitests/src/test/snapshots/images/features.deactivation.impl_AccountDeactivationView_Night_4_en.png From 4464c311ad62cc4e73a2d9c492e3ef5377d77331 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 14:36:56 +0200 Subject: [PATCH 32/92] Bump enterprise module Follow the package rename of `InMemoryCacheStore`, see https://github.com/element-hq/element-android-enterprise/pull/66 Co-Authored-By: Claude Opus 5.5 (1M context) --- enterprise | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enterprise b/enterprise index 073abe3b44..819746a734 160000 --- a/enterprise +++ b/enterprise @@ -1 +1 @@ -Subproject commit 073abe3b44598a072a87a9c92d15c6e5bc4a88af +Subproject commit 819746a734fbbeb4ffcbf3e842f7cc694cccc8a4 From 2864e867fa31a4ad8e808c934d37e5172b443b7a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 15:00:37 +0200 Subject: [PATCH 33/92] End all the remaining sessions of the Maestro account after the tests Each Maestro run logs in to @maestroelement:matrix.org, and the sessions pile up on the account. Run tools/mas/delete_all_sessions.py at the end of the Maestro job, even if the tests failed, to end them all. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/maestro-local.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/maestro-local.yml b/.github/workflows/maestro-local.yml index 1195c7b53d..01f0a9acb7 100644 --- a/.github/workflows/maestro-local.yml +++ b/.github/workflows/maestro-local.yml @@ -124,6 +124,19 @@ jobs: disk-size: 3G script: | .github/workflows/scripts/maestro/maestro-local-with-screen-recording.sh app-gplay-x86_64-debug.apk + - name: Set up Python + if: always() + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: 3.14 + - name: End all the remaining sessions of the Maestro account + if: always() + continue-on-error: true + env: + MAS_PASSWORD: ${{ secrets.MATRIX_MAESTRO_ACCOUNT_PASSWORD }} + run: | + pip install requests + python3 ./tools/mas/delete_all_sessions.py --homeserver matrix.org --username maestroelement - name: Upload test results uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: From 56f8925ff3fb4eb7a2de5d257beb77df63e4e6b3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 23 Sep 2026 15:06:34 +0200 Subject: [PATCH 34/92] Update Chat icon and text font for Empty room list. Closes #7739 --- .../impl/components/RoomListContentView.kt | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt index 1a2573e963..5fc6d4c85e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomListContentView.kt @@ -8,7 +8,6 @@ package io.element.android.features.home.impl.components -import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -23,7 +22,6 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -129,6 +127,9 @@ private fun SkeletonView( } } +/** + * Ref: https://www.figma.com/design/pDlJZGBsri47FNTXMnEdXB/Compound-Android-Templates?node-id=8-4656 + */ @Composable private fun EmptyView( state: RoomListContentState.Empty, @@ -139,13 +140,19 @@ private fun EmptyView( modifier: Modifier = Modifier, ) { Box(modifier.fillMaxSize()) { + val text = buildString { + append(stringResource(R.string.screen_roomlist_empty_title)) + append("\n") + append(stringResource(R.string.screen_roomlist_empty_message)) + } EmptyScaffold( - title = R.string.screen_roomlist_empty_title, - subtitle = R.string.screen_roomlist_empty_message, - action = { + title = null, + subtitle = text, + bottomPart = { + Spacer(modifier = Modifier.height(16.dp)) Button( text = stringResource(CommonStrings.action_start_chat), - leadingIcon = IconSource.Vector(CompoundIcons.Compose()), + leadingIcon = IconSource.Vector(CompoundIcons.Chat()), onClick = onCreateRoomClick, ) }, @@ -299,39 +306,44 @@ private fun EmptyViewForFilterStates( ) { val emptyStateResources = RoomListFiltersEmptyStateResources.fromSelectedFilters(selectedFilters, isSpaceFilterSelected) ?: return EmptyScaffold( - title = emptyStateResources.title, - subtitle = emptyStateResources.subtitle, + title = stringResource(emptyStateResources.title), + subtitle = stringResource(emptyStateResources.subtitle), modifier = modifier, + bottomPart = { + // Keep a spacer so that the content is not too low on the screen + Spacer(modifier = Modifier.height(32.dp)) + } ) } @Composable private fun EmptyScaffold( - @StringRes title: Int, - @StringRes subtitle: Int, + title: String?, + subtitle: String, modifier: Modifier = Modifier, - action: @Composable (ColumnScope.() -> Unit)? = null, + bottomPart: @Composable ColumnScope.() -> Unit, ) { Column( modifier = modifier.padding(horizontal = 60.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { + title?.let { + Text( + text = title, + style = ElementTheme.typography.fontHeadingMdBold, + color = ElementTheme.colors.textPrimary, + textAlign = TextAlign.Center, + ) + Spacer(modifier = Modifier.height(16.dp)) + } Text( - text = stringResource(title), - style = ElementTheme.typography.fontHeadingMdBold, - color = ElementTheme.colors.textPrimary, - textAlign = TextAlign.Center, - ) - Spacer(modifier = Modifier.height(16.dp)) - Text( - text = stringResource(subtitle), + text = subtitle, style = ElementTheme.typography.fontBodyLgRegular, color = ElementTheme.colors.textSecondary, textAlign = TextAlign.Center, ) - Spacer(modifier = Modifier.height(32.dp)) - action?.invoke(this) + bottomPart() } } From 19a0b3d517f251c95f91608e1f750e63528f808f Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 5 Oct 2026 14:26:03 +0000 Subject: [PATCH 35/92] Update screenshots --- ...ures.home.impl.components_RoomListContentView_Day_3_en.png | 4 ++-- ...ures.home.impl.components_RoomListContentView_Day_4_en.png | 4 ++-- ...es.home.impl.components_RoomListContentView_Night_3_en.png | 4 ++-- ...es.home.impl.components_RoomListContentView_Night_4_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_10_en.png | 4 ++-- .../images/features.home.impl_HomeView_Day_15_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_10_en.png | 4 ++-- .../images/features.home.impl_HomeView_Night_15_en.png | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_3_en.png index 2d80edf071..aca1e3053d 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:87192218045f827298d8fad97502c5f39a7c516363d15d8bd427716d1f649665 -size 17801 +oid sha256:c146d3637f2bb8ad004242fc8cebfca9a59ef7c97dc77981a27a0edbeed1e565 +size 17040 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_4_en.png index a02660abd2..ea4352dad1 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:040424fb8f73d8141ac72858c8b624de47523539dd5b8f48e7268bc62e458ef4 -size 52116 +oid sha256:a8f7d35d1be3ca4a54ce43509dd16620d923262789b90404ffc830773738bbf6 +size 51485 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_3_en.png index 709c59c1f3..71ff386d8d 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d83489b20abdcb63b53a489b0bab562f2c96d8e2095377ccaa570aabe03e73c -size 17402 +oid sha256:0c48c2e697184dbc332a345f8d818467d667181a824b33318117252761949483 +size 16624 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_4_en.png index cfd3b22664..4f0397ce69 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_RoomListContentView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63a5d6791df9f8ce7228ca686e3037d53c668b54332fa91c69ff111c18f32fd1 -size 50361 +oid sha256:6908459791a80bf925022fb2fe6ae284a61297f89ac6605f3406d9e8f231baad +size 49605 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png index e2b3622d86..b2399efa7b 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3c3e85ad3d267db2fa0964b4d28ef9fd50ce4989259646d5d07bdea7e24c7a4c -size 36872 +oid sha256:b6c473776629ba75c619cd463eab93cd44c46969bf6d663dbf79500c4ccd222d +size 36017 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png index 0f310c78a8..3ce2afa3ae 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f2280adb5d99a20fbe98b8d097e97c2de0c05a083382a905075bfb5795a2290 -size 55890 +oid sha256:3da5234aa3bba55b138dc452aec909ad03d3df2be546042d811c94cc61693970 +size 56639 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png index fc6b74a2f6..0889da985c 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_10_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:588859e742ca76818510abb9326969da5c227d0946ea6aac9de216e1a62a7d6a -size 31657 +oid sha256:d247ca3cce68ac6ec8e17f6f10f4b3a9a579e0e06b6585bb6248258b8a39e619 +size 30968 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png index 2e8b7314f6..9c0b5cb174 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_15_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:422c10006716ea99ae5d3222334f4fe74a002e42fa2ab8b7890dff31a60ca8f7 -size 50444 +oid sha256:aa07a0aed5fd533407928608c61ffdf23084ca1d8692f2cb0f4b8b95940df7f4 +size 51223 From b76548f9a2e933a889dfaed7fbff7a679bfcd94f Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Mon, 5 Oct 2026 17:56:16 +0200 Subject: [PATCH 36/92] Dismiss notifications for rooms fully read in other devices (#7846) * Dismiss notifications for rooms fully read in other devices --- .../android/appnav/LoggedInEventProcessor.kt | 49 ++++- .../android/appnav/LoggedInFlowNode.kt | 6 +- .../appnav/LoggedInEventProcessorTest.kt | 197 ++++++++++++++++++ .../api/notifications/NotificationCleaner.kt | 7 + .../DefaultNotificationDrawerManager.kt | 28 ++- .../DefaultNotificationDrawerManagerTest.kt | 125 +++++++++++ .../notifications/FakeNotificationCleaner.kt | 7 +- 7 files changed, 404 insertions(+), 15 deletions(-) create mode 100644 appnav/src/test/kotlin/io/element/android/appnav/LoggedInEventProcessorTest.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt index cee2296d43..ac08795f67 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInEventProcessor.kt @@ -8,28 +8,51 @@ package io.element.android.appnav -import dev.zacsweers.metro.Inject +import androidx.annotation.VisibleForTesting +import dev.zacsweers.metro.Assisted +import dev.zacsweers.metro.AssistedFactory +import dev.zacsweers.metro.AssistedInject +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage +import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange +import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -@Inject +@AssistedInject class LoggedInEventProcessor( - private val snackbarDispatcher: SnackbarDispatcher, + @Assisted private val snackbarDispatcher: SnackbarDispatcher, private val roomMembershipObserver: RoomMembershipObserver, + private val sessionId: SessionId, + private val roomListService: RoomListService, + private val notificationCleaner: NotificationCleaner, + private val dispatchers: CoroutineDispatchers ) { - private var observingJob: Job? = null + @AssistedFactory + interface Factory { + fun create(snackbarDispatcher: SnackbarDispatcher): LoggedInEventProcessor + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + internal var currentChildScope: CoroutineScope? = null fun observeEvents(coroutineScope: CoroutineScope) { - observingJob = roomMembershipObserver.updates + if (currentChildScope != null) return + + val childScope = coroutineScope.childScope(dispatchers.computation, "LoggedInEventProcessor") + .also { currentChildScope = it } + + roomMembershipObserver.updates .filter { !it.isUserInRoom } .distinctUntilChanged() .onEach { roomMemberShipUpdate -> @@ -48,12 +71,20 @@ class LoggedInEventProcessor( else -> Unit } } - .launchIn(coroutineScope) + .launchIn(childScope) + + // Use the room list service state as a 'heartbeat' update to check existing notifications + roomListService.state + .filter { it == RoomListService.State.Running } + .onEach { + notificationCleaner.clearReadRoomsNotifications(sessionId) + } + .launchIn(childScope) } fun stopObserving() { - observingJob?.cancel() - observingJob = null + currentChildScope?.cancel() + currentChildScope = null } private fun displayMessage(message: Int) { 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 731c26f684..370c0db2f1 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -157,6 +157,7 @@ class LoggedInFlowNode( private val createRoomEntryPoint: CreateRoomEntryPoint, private val activeLiveLocationShareManager: ActiveLiveLocationShareManager, private val customMapTilerConfigProvider: CustomMapTilerConfigProvider, + private val loggedInEventProcessorFactory: LoggedInEventProcessor.Factory, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Placeholder, @@ -175,10 +176,7 @@ class LoggedInFlowNode( } private val callback: Callback = callback() - private val loggedInFlowProcessor = LoggedInEventProcessor( - snackbarDispatcher = snackbarDispatcher, - roomMembershipObserver = matrixClient.roomMembershipObserver, - ) + private val loggedInFlowProcessor = loggedInEventProcessorFactory.create(snackbarDispatcher) /** * Display the incoming verification request, the app is expected to be in foreground. diff --git a/appnav/src/test/kotlin/io/element/android/appnav/LoggedInEventProcessorTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/LoggedInEventProcessorTest.kt new file mode 100644 index 0000000000..4d5cfe06f3 --- /dev/null +++ b/appnav/src/test/kotlin/io/element/android/appnav/LoggedInEventProcessorTest.kt @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.appnav + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.room.CurrentUserMembership +import io.element.android.libraries.matrix.api.room.RoomMembershipObserver +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.test.A_ROOM_ID +import io.element.android.libraries.matrix.test.A_SESSION_ID +import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService +import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.isActive +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class LoggedInEventProcessorTest { + @Test + fun `test observeEvents with left membership change`() = runTest { + val snackbarDispatcher = SnackbarDispatcher() + val roomMembershipObserver = RoomMembershipObserver() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + testScheduler.runCurrent() + + roomMembershipObserver.notifyUserLeftRoom( + roomId = A_ROOM_ID, + isSpace = false, + membershipBeforeLeft = CurrentUserMembership.JOINED, + ) + + testScheduler.runCurrent() + + // Verify that the snackbar message was displayed + assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull() + } + + @Test + fun `test observeEvents with left membership change for space`() = runTest { + val snackbarDispatcher = SnackbarDispatcher() + val roomMembershipObserver = RoomMembershipObserver() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + testScheduler.runCurrent() + + roomMembershipObserver.notifyUserLeftRoom( + roomId = A_ROOM_ID, + isSpace = true, + membershipBeforeLeft = CurrentUserMembership.JOINED, + ) + + testScheduler.runCurrent() + + // Verify that the snackbar message was displayed + assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull() + } + + @Test + fun `test observeEvents with invitation rejected membership change`() = runTest { + val snackbarDispatcher = SnackbarDispatcher() + val roomMembershipObserver = RoomMembershipObserver() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + testScheduler.runCurrent() + + roomMembershipObserver.notifyUserLeftRoom( + roomId = A_ROOM_ID, + isSpace = false, + membershipBeforeLeft = CurrentUserMembership.INVITED, + ) + + testScheduler.runCurrent() + + // Verify that the snackbar message was displayed + assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull() + } + + @Test + fun `test observeEvents with knock retracted membership change`() = runTest { + val snackbarDispatcher = SnackbarDispatcher() + val roomMembershipObserver = RoomMembershipObserver() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + testScheduler.runCurrent() + + roomMembershipObserver.notifyUserLeftRoom( + roomId = A_ROOM_ID, + isSpace = false, + membershipBeforeLeft = CurrentUserMembership.KNOCKED, + ) + + testScheduler.runCurrent() + + // Verify that the snackbar message was displayed + assertThat(snackbarDispatcher.snackbarMessage.first()).isNotNull() + } + + @Test + fun `test stopObserving cancels the child scope`() = runTest { + val snackbarDispatcher = SnackbarDispatcher() + val roomMembershipObserver = RoomMembershipObserver() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + assertThat(loggedInEventProcessor.currentChildScope?.isActive).isTrue() + + testScheduler.runCurrent() + + // Stop observing events + loggedInEventProcessor.stopObserving() + + // Verify that the child scope is cancelled (the scope is null) + assertThat(loggedInEventProcessor.currentChildScope).isNull() + } + + @Test + fun `test notifications are cleared when room list service state is running`() = runTest { + val clearReadNotificationsLambda = lambdaRecorder { _: SessionId -> } + val snackbarDispatcher = SnackbarDispatcher() + val notificationCleaner = FakeNotificationCleaner(clearReadRoomsNotificationsLambda = clearReadNotificationsLambda) + val roomListService = FakeRoomListService() + + val loggedInEventProcessor = createLoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + notificationCleaner = notificationCleaner, + roomListService = roomListService, + ) + + loggedInEventProcessor.observeEvents(backgroundScope) + + testScheduler.runCurrent() + + // Simulate the room list service state changing to Running + roomListService.postState(RoomListService.State.Running) + + testScheduler.runCurrent() + + // Verify that the notifications were cleared + clearReadNotificationsLambda.assertions().isCalledOnce().with(value(A_SESSION_ID)) + } + + private fun TestScope.createLoggedInEventProcessor( + snackbarDispatcher: SnackbarDispatcher, + roomMembershipObserver: RoomMembershipObserver = RoomMembershipObserver(), + sessionId: SessionId = A_SESSION_ID, + roomListService: FakeRoomListService = FakeRoomListService(), + notificationCleaner: FakeNotificationCleaner = FakeNotificationCleaner(), + ) = LoggedInEventProcessor( + snackbarDispatcher = snackbarDispatcher, + roomMembershipObserver = roomMembershipObserver, + sessionId = sessionId, + roomListService = roomListService, + notificationCleaner = notificationCleaner, + dispatchers = testCoroutineDispatchers(), + ) +} diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt index 95966f8eb1..0e41d63e26 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationCleaner.kt @@ -65,4 +65,11 @@ interface NotificationCleaner { * @param roomId the room whose notifications are dismissed. */ fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) + + /** + * Dismisses the notifications of every read room. + * + * @param sessionId the session whose notifications should be dismissed. + */ + fun clearReadRoomsNotifications(sessionId: SessionId) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 6834f404ed..82ae7ba233 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -12,6 +12,7 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn import io.element.android.features.lockscreen.api.LockScreenService +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.EventId @@ -53,7 +54,7 @@ class DefaultNotificationDrawerManager( private val notificationRenderer: NotificationRenderer, private val appNavigationStateService: AppNavigationStateService, @AppCoroutineScope - coroutineScope: CoroutineScope, + private val coroutineScope: CoroutineScope, private val matrixClientProvider: MatrixClientProvider, private val imageLoaderHolder: ImageLoaderHolder, private val activeNotificationsProvider: ActiveNotificationsProvider, @@ -187,6 +188,31 @@ class DefaultNotificationDrawerManager( clearSummaryNotificationIfNeeded(sessionId) } + override fun clearReadRoomsNotifications(sessionId: SessionId) { + val notifications = activeNotificationsProvider.getNotificationsForSession(sessionId) + val notificationsGroupedByRoomId = notifications.mapNotNull { + tryOrNull { RoomId(it.tag) }?.let { roomId -> roomId to it } + } + + if (notificationsGroupedByRoomId.isEmpty()) return + + coroutineScope.launch { + val client = matrixClientProvider.getOrRestore(sessionId).getOrElse { return@launch } + + notificationsGroupedByRoomId.forEach { (roomId, notification) -> + val roomInfo = client.getRoomInfo(roomId).getOrNull() ?: return@forEach + + if (roomInfo.numUnreadNotifications > 0L || roomInfo.numUnreadMessages > 0 || roomInfo.numUnreadMentions > 0) { + return@forEach + } + + notificationDisplayer.cancelNotification(notification.tag, notification.id) + } + + clearSummaryNotificationIfNeeded(sessionId) + } + } + private fun clearSummaryNotificationIfNeeded(sessionId: SessionId) { val summaryNotification = activeNotificationsProvider.getSummaryNotification(sessionId) if (summaryNotification != null && activeNotificationsProvider.count(sessionId) == 1) { diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index cdc6a01fad..3a655d2cce 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import android.service.notification.StatusBarNotification import androidx.compose.ui.graphics.Color import com.google.common.truth.Truth.assertThat import io.element.android.features.enterprise.api.EnterpriseService @@ -16,6 +17,8 @@ import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.lockscreen.api.LockScreenService import io.element.android.features.lockscreen.test.FakeLockScreenService 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.RoomInfo 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_ROOM_ID @@ -27,6 +30,7 @@ import io.element.android.libraries.matrix.test.A_THREAD_ID_2 import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationIdProvider @@ -57,6 +61,7 @@ import io.element.android.services.appnavstate.test.aNavigationState import io.element.android.services.appnavstate.test.anAppNavigationState import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP import io.element.android.tests.testutils.lambda.LambdaThreeParamsRecorder +import io.element.android.tests.testutils.lambda.LambdaTwoParamsRecorder import io.element.android.tests.testutils.lambda.any import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -569,6 +574,126 @@ class DefaultNotificationDrawerManagerTest { createFallbackNotificationResult.assertions().isNeverCalled() } + @Test + fun `clearReadRoomsNotifications cancels notifications of rooms without unread counts`() = runTest { + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val sut = createClearReadRoomsManager( + cancelNotificationResult = cancelNotificationResult, + notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)), + roomInfo = aRoomInfo(id = A_ROOM_ID), + ) + sut.clearReadRoomsNotifications(A_SESSION_ID) + runCurrent() + cancelNotificationResult.assertions().isCalledOnce().with(value(A_ROOM_ID.value), value(1)) + } + + @Test + fun `clearReadRoomsNotifications keeps notifications of rooms with unread messages`() = testKeepsNotification( + aRoomInfo(id = A_ROOM_ID, numUnreadMessages = 1) + ) + + @Test + fun `clearReadRoomsNotifications keeps notifications of rooms with unread notifications`() = + testKeepsNotification(aRoomInfo(id = A_ROOM_ID, numUnreadNotifications = 1)) + + @Test + fun `clearReadRoomsNotifications keeps notifications of rooms with unread mentions`() = testKeepsNotification( + aRoomInfo(id = A_ROOM_ID, numUnreadMentions = 1) + ) + + @Test + fun `clearReadRoomsNotifications keeps notifications when the room info is not found`() = testKeepsNotification(null) + + @Test + fun `clearReadRoomsNotifications does nothing when the client cannot be restored`() = runTest { + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val sut = createClearReadRoomsManager( + cancelNotificationResult = cancelNotificationResult, + notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)), + roomInfo = aRoomInfo(id = A_ROOM_ID), + clientResult = Result.failure(IllegalStateException("No client")), + ) + sut.clearReadRoomsNotifications(A_SESSION_ID) + runCurrent() + cancelNotificationResult.assertions().isNeverCalled() + } + + @Test + fun `clearReadRoomsNotifications ignores notifications not linked to a room`() = runTest { + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val sut = createClearReadRoomsManager( + cancelNotificationResult = cancelNotificationResult, + notifications = listOf( + mockk { + every { id } returns 1 + every { tag } returns null + }, + ), + roomInfo = aRoomInfo(id = A_ROOM_ID), + ) + sut.clearReadRoomsNotifications(A_SESSION_ID) + runCurrent() + cancelNotificationResult.assertions().isNeverCalled() + } + + @Test + fun `clearReadRoomsNotifications clears the summary when it is the only notification left`() = runTest { + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val summaryId = NotificationIdProvider.getSummaryNotificationId(A_SESSION_ID) + val sut = createClearReadRoomsManager( + cancelNotificationResult = cancelNotificationResult, + notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)), + roomInfo = aRoomInfo(id = A_ROOM_ID), + summaryNotification = mockk { every { id } returns summaryId }, + count = 1, + ) + sut.clearReadRoomsNotifications(A_SESSION_ID) + runCurrent() + cancelNotificationResult.assertions().isCalledExactly(2).withSequence( + listOf(value(A_ROOM_ID.value), value(1)), + listOf(value(null), value(summaryId)), + ) + } + + private fun testKeepsNotification(roomInfo: RoomInfo?) = runTest { + val cancelNotificationResult = lambdaRecorder { _, _ -> } + val sut = createClearReadRoomsManager( + cancelNotificationResult = cancelNotificationResult, + notifications = listOf(aRoomStatusBarNotification(A_ROOM_ID, 1)), + roomInfo = roomInfo, + ) + sut.clearReadRoomsNotifications(A_SESSION_ID) + runCurrent() + cancelNotificationResult.assertions().isNeverCalled() + } + + private fun aRoomStatusBarNotification(roomId: RoomId, notificationId: Int): StatusBarNotification = mockk { + every { id } returns notificationId + every { tag } returns roomId.value + } + + private fun TestScope.createClearReadRoomsManager( + cancelNotificationResult: LambdaTwoParamsRecorder, + notifications: List, + roomInfo: RoomInfo?, + clientResult: Result? = null, + summaryNotification: StatusBarNotification? = null, + count: Int = notifications.size, + ): DefaultNotificationDrawerManager { + val matrixClient = FakeMatrixClient().apply { + getRoomInfoLambda = { Result.success(roomInfo) } + } + return createDefaultNotificationDrawerManager( + notificationDisplayer = FakeNotificationDisplayer(cancelNotificationResult = cancelNotificationResult), + activeNotificationsProvider = FakeActiveNotificationsProvider( + getNotificationsForSessionResult = { notifications }, + getSummaryNotificationResult = { summaryNotification }, + countResult = { count }, + ), + matrixClientProvider = FakeMatrixClientProvider(getClient = { clientResult ?: Result.success(matrixClient) }), + ) + } + /** * The content of the original event must be stripped, only the noisy flag, the ids and the timestamp are kept. */ diff --git a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationCleaner.kt b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationCleaner.kt index 28a249dc9b..82202111dd 100644 --- a/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationCleaner.kt +++ b/libraries/push/test/src/main/kotlin/io/element/android/libraries/push/test/notifications/FakeNotificationCleaner.kt @@ -21,7 +21,8 @@ class FakeNotificationCleaner( private val clearMessagesForThreadLambda: (SessionId, RoomId, ThreadId) -> Unit = { _, _, _ -> lambdaError() }, private val clearEventLambda: (SessionId, EventId) -> Unit = { _, _ -> lambdaError() }, private val clearMembershipNotificationForSessionLambda: (SessionId) -> Unit = { lambdaError() }, - private val clearMembershipNotificationForRoomLambda: (SessionId, RoomId) -> Unit = { _, _ -> lambdaError() } + private val clearMembershipNotificationForRoomLambda: (SessionId, RoomId) -> Unit = { _, _ -> lambdaError() }, + private val clearReadRoomsNotificationsLambda: (SessionId) -> Unit = { lambdaError() }, ) : NotificationCleaner { override fun clearAllMessagesEvents(sessionId: SessionId) { clearAllMessagesEventsLambda(sessionId) @@ -46,4 +47,8 @@ class FakeNotificationCleaner( override fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId) { clearMembershipNotificationForRoomLambda(sessionId, roomId) } + + override fun clearReadRoomsNotifications(sessionId: SessionId) { + clearReadRoomsNotificationsLambda(sessionId) + } } From 721cedbc459947eb7a5c8a516bf54b6d1d65da6c Mon Sep 17 00:00:00 2001 From: ElementBot Date: Mon, 5 Oct 2026 16:25:38 +0000 Subject: [PATCH 37/92] Update screenshots --- ...imeline.components_SelectionIndicatorSelected_Day_0_en.png | 3 +++ ...eline.components_SelectionIndicatorSelected_Night_0_en.png | 3 +++ ...s.impl.timeline.components_SelectionIndicator_Day_0_en.png | 3 +++ ...impl.timeline.components_SelectionIndicator_Night_0_en.png | 3 +++ ...sages.impl.timeline_TimelineViewSelectionMode_Day_0_en.png | 3 +++ ...ges.impl.timeline_TimelineViewSelectionMode_Night_0_en.png | 3 +++ ...res.messages.impl.topbars_SelectionModeTopBar_Day_0_en.png | 3 +++ ...res.messages.impl.topbars_SelectionModeTopBar_Day_1_en.png | 3 +++ ...res.messages.impl.topbars_SelectionModeTopBar_Day_2_en.png | 3 +++ ...s.messages.impl.topbars_SelectionModeTopBar_Night_0_en.png | 3 +++ ...s.messages.impl.topbars_SelectionModeTopBar_Night_1_en.png | 3 +++ ...s.messages.impl.topbars_SelectionModeTopBar_Night_2_en.png | 3 +++ ...tures.messages.impl_MessagesViewSelectionMode_Day_0_en.png | 3 +++ ...res.messages.impl_MessagesViewSelectionMode_Night_0_en.png | 3 +++ .../libraries.roomselect.impl_RoomSelectView_Day_0_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_1_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Day_4_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_0_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_1_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_2_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_3_en.png | 4 ++-- .../libraries.roomselect.impl_RoomSelectView_Night_4_en.png | 4 ++-- 24 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_1_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_2_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Night_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Day_0_en.png new file mode 100644 index 0000000000..daa500f1ca --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bc29e05e6f4bd8f8e9278c94b4f25e5154ed8c11f7c733aac16960e047599d0 +size 4309 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Night_0_en.png new file mode 100644 index 0000000000..6ff7e570d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicatorSelected_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6243cdd7d3199a355c1ff5f5332bfd29c38f3b0ccd45dc0af6efdbc81c564e5 +size 4194 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Day_0_en.png new file mode 100644 index 0000000000..89451164a3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0665afc8ecb2119b0472adc7b5aaf5ff1726dc3538bab5ccdc37e4710569d1e +size 3894 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Night_0_en.png new file mode 100644 index 0000000000..1da717df5c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline.components_SelectionIndicator_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a61a03abf31e3143f1e02e4dd978e35b63bf2df0a634333edd08f0ee1fc8f1e +size 3881 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Day_0_en.png new file mode 100644 index 0000000000..cac3d8f820 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd3ea7b8a04d58c1b3a825eeeeb798e3cf5ef333531d07456f654266706a8304 +size 44353 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Night_0_en.png new file mode 100644 index 0000000000..b54609e4c9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.timeline_TimelineViewSelectionMode_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3aa79e39645f154961fac99f4a58f3f0393225ba898e155f5a87acdbaa1df146 +size 45998 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_0_en.png new file mode 100644 index 0000000000..7b034776af --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86162a6692bab6684d7ca14c69085d6c3da52cc5b2efca76da307c3df626342a +size 8095 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_1_en.png new file mode 100644 index 0000000000..9f1e06b15c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca18b30acb9cca3d6c7baa204a847efdee0ad7dac55659cae6f5085b62bd878e +size 7619 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_2_en.png new file mode 100644 index 0000000000..2c244facd5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Day_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbb3853157d790596a1ee73dee89967efcd0a2dadaa004d745e70f79ebb9910b +size 7922 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_0_en.png new file mode 100644 index 0000000000..d96bcd8dcd --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d4c0ae289da14f583717c16394e48a36f89e49d509a5f2547e9815178562799c +size 7567 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_1_en.png new file mode 100644 index 0000000000..fd64fcd38b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_1_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97f9c238900766dde4e7a2db1810cda11d453bb13b41213e5cf2b60548ebf47c +size 7205 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_2_en.png new file mode 100644 index 0000000000..2deddffe3a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl.topbars_SelectionModeTopBar_Night_2_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2721dc2ee00dce017522144f9ab7643817fc4abcd49e8d41f62fda04d2cec1f9 +size 7448 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Day_0_en.png new file mode 100644 index 0000000000..8bce40ff14 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d8d6dab0b68be7a3feec477fd6026a0a6e4e8aeebd8c67adcec52270669420e +size 53355 diff --git a/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Night_0_en.png new file mode 100644 index 0000000000..d4f3bff641 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.messages.impl_MessagesViewSelectionMode_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39b9f7d1de15cb084e46996755d9f1b2be25278eb90070d0983d67146dc55edb +size 54377 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_0_en.png index bf4223a9cf..ed28276533 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b164aa5e1a4e8115aede74944e450dfd3abe33d13f6b91bc27745d87679a7f9 -size 13080 +oid sha256:bf1e3ce457d2e28ee929ade5bfc1abfc0a62ef2dc8443824300d15f78e843b75 +size 11233 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_1_en.png index 309ace3cff..eacac1fc43 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99beabe21f4f1dad2673e96631dd6756b78cb1c1495c7a7ba8d44257b593e007 -size 10999 +oid sha256:90452c32320a3eaad1c520885c487dc6242e1333f2084940c85e8f4060940b1e +size 9166 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png index c186f08d29..a74397edcd 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4bbd7ca482fe8e846b2748d25e429a5fffd2c0ecc88014942efb90ea312cb298 -size 34413 +oid sha256:c3959f54f33bb8302839691e8c1a6f24b5b192c318bc4767636a27bb2578061d +size 32623 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png index cb7871cabf..9e671f14cf 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:18eef832d5c341dbc7d9c6c5495464dd9bfbd5c9355c3fe74384e6b1fdb0fe40 -size 32481 +oid sha256:95cd751b1fcdd38cd38b5dd0335f0d157b9a0dfd45ca6a7eebf4ae55488f0c84 +size 30661 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png index 16399a868c..3ab43d84e3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Day_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ebb9b7f9c8e657d8e5a7fae6a133449fbbbb7529260dc4feb6c5da176eb1103 -size 36481 +oid sha256:91c711395cfbca04b7b41c23d01012870a97a4b4b0291d6dc81c0949e60d98e8 +size 34783 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_0_en.png index 4af9e7c6f5..149038c4f5 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_0_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_0_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ba018ba755b9925df764eec9a42a0f18853bce9944f153e853dda5427267e54 -size 12481 +oid sha256:71b4ecc3a18de7be9eb8ef453852f6f9a5c44ae8edb0a14cdf09a4a240c3faab +size 10703 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_1_en.png index ac3d4a319d..9287ee04f3 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6293fe8bfd160716a014932d7b9348fcad9cfad08a0c747bd49cf5922a5b685d -size 10732 +oid sha256:6e4a4c20eb92cf93d5e7a76973b46863ed79b32e36c9d970b311b9681a382b23 +size 8957 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png index 6ecca16bce..0fd706f24c 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0653e38a43b99d9b07e41cdccc1bdad0e1d22a1449fea87f3909c061dffeea1e -size 34221 +oid sha256:94998fe24668f8dde57a1cdfb6e8e8cc25f1d74d8b3eb69fccf4db9067667a86 +size 32446 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png index dd4fa7ec73..8fa06609d9 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_3_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:396c1b84d656baefa9161221bbc82343d55552dd7ea6aa44d87b4fc1a294a769 -size 32436 +oid sha256:b77e1b57b374991fdfd48c2ed42f1eefbbc3afeeda6c802831d9ab5949518581 +size 30666 diff --git a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png index 5178aaf31c..1b7bc1040d 100644 --- a/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png +++ b/tests/uitests/src/test/snapshots/images/libraries.roomselect.impl_RoomSelectView_Night_4_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5e5d889237d4e539e595c0dffd0f565b47b11257a8ef592567de0d302116f92 -size 36737 +oid sha256:a8d56ee6a21f4e9dfe20c64b6cc55eb94bb8b281aba375be31904660b4fa9e88 +size 35101 From 7ef93b51e481ee4cd55d46ae3a3c541562b747c9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Oct 2026 20:47:57 +0200 Subject: [PATCH 38/92] Use the Element Creations license header on the new selection files --- .../android/features/messages/impl/timeline/SelectionState.kt | 2 +- .../messages/impl/timeline/components/SelectionIndicator.kt | 2 +- .../features/messages/impl/topbars/SelectionModeTopBar.kt | 2 +- .../features/messages/impl/timeline/SelectionActionTest.kt | 2 +- .../features/messages/impl/timeline/SelectionStateTest.kt | 2 +- .../features/messages/impl/timeline/TimelineStateTest.kt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt index 7a9f073bc0..b252dfd10e 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt index 86261057c8..f405b79e69 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/SelectionIndicator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt index 1d12699277..50b14e5958 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt index 4939ec3a50..1d7155f742 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionActionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt index ea45813a10..bb8f66630d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt index 3ee818e438..8e3cb997c2 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 New Vector Ltd. + * Copyright (c) 2026 Element Creations Ltd. * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. * Please see LICENSE files in the repository root for full details. From d4310cb6e9aa286a6889594111073daadfa1ffe7 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Oct 2026 20:48:05 +0200 Subject: [PATCH 39/92] Use toImmutableSet instead of toPersistentSet --- .../io/element/android/features/messages/impl/MessagesView.kt | 4 ++-- .../android/features/messages/impl/timeline/SelectionState.kt | 4 ++-- .../features/messages/impl/timeline/TimelinePresenter.kt | 4 ++-- .../features/messages/impl/timeline/SelectionStateTest.kt | 4 ++-- 4 files changed, 8 insertions(+), 8 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 92c91dfee5..50b90be650 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 @@ -142,7 +142,7 @@ import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.wysiwyg.link.Link import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet import timber.log.Timber import kotlin.time.Duration.Companion.milliseconds @@ -774,7 +774,7 @@ internal fun MessagesViewSelectionModePreview() = ElementPreview { .filter { SelectionAction.Forward.canApplyTo(it) } .take(2) .mapNotNull { it.eventId } - .toPersistentSet() + .toImmutableSet() MessagesView( state = aMessagesState( timelineState = aTimelineState( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt index b252dfd10e..3c44839468 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/SelectionState.kt @@ -16,7 +16,7 @@ import io.element.android.features.messages.impl.timeline.model.event.canBeForwa import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.ImmutableSet -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet /** Maximum number of messages that can be selected at once. */ const val MAX_SELECTION_COUNT = 10 @@ -57,7 +57,7 @@ fun SelectionAction.canApplyTo(event: TimelineItem.Event): Boolean = when (this) */ fun SelectionState.deselect(eventIds: Set): SelectionState = when (this) { SelectionState.Disabled -> this - is SelectionState.Active -> copy(selectedEventIds = (selectedEventIds - eventIds).toPersistentSet()) + is SelectionState.Active -> copy(selectedEventIds = (selectedEventIds - eventIds).toImmutableSet()) } /** 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 0b70088336..a7006985a5 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 @@ -75,7 +75,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.combine @@ -285,7 +285,7 @@ class TimelinePresenter( snackbarDispatcher.post(SnackbarMessage(CommonStrings.screen_room_maximum_messages_selected)) current } - else -> current.copy(selectedEventIds = (selected + event.eventId).toPersistentSet()) + else -> current.copy(selectedEventIds = (selected + event.eventId).toImmutableSet()) } } is TimelineEvent.ExitSelectionMode -> { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt index bb8f66630d..920b3bdaae 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/SelectionStateTest.kt @@ -16,7 +16,7 @@ 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.AN_EVENT_ID_3 import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentSet +import kotlinx.collections.immutable.toImmutableSet import org.junit.Test class SelectionStateTest { @@ -84,6 +84,6 @@ class SelectionStateTest { private fun anActiveSelection(eventIds: List = emptyList()) = SelectionState.Active( action = SelectionAction.Forward, - selectedEventIds = eventIds.toPersistentSet(), + selectedEventIds = eventIds.toImmutableSet(), ) } From 9b2dcd4b260e43b10883ec373c2a1811817e8a6f Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Oct 2026 20:48:14 +0200 Subject: [PATCH 40/92] Fix the Konsist preview naming for the selection previews --- .../features/messages/impl/topbars/SelectionModeTopBar.kt | 4 ++-- .../io/element/android/tests/konsist/KonsistClassNameTest.kt | 1 + .../io/element/android/tests/konsist/KonsistPreviewTest.kt | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt index 50b14e5958..5cd2b15bda 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/topbars/SelectionModeTopBar.kt @@ -63,7 +63,7 @@ fun SelectionModeTopBar( @PreviewsDayNight @Composable internal fun SelectionModeTopBarPreview( - @PreviewParameter(SelectionCountProvider::class) count: Int, + @PreviewParameter(SelectionCountPreviewParam::class) count: Int, ) = ElementPreview { SelectionModeTopBar( selectedCount = count, @@ -73,6 +73,6 @@ internal fun SelectionModeTopBarPreview( ) } -internal class SelectionCountProvider : PreviewParameterProvider { +internal class SelectionCountPreviewParam : PreviewParameterProvider { override val values = sequenceOf(0, 1, 5) } 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 7c6eb2b630..5fa83ac198 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 @@ -66,6 +66,7 @@ class KonsistClassNameTest { "EditableAvatarViewUriPreviewParam", "LoginModeViewErrorPreviewParam", "OverlapRatioPreviewParam", + "SelectionCountPreviewParam", "TextFileContentPreviewParam", "HtmlMessageContentPreviewParam", ) diff --git a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt index 339ba1023a..f01cef9791 100644 --- a/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt +++ b/tests/konsist/src/test/kotlin/io/element/android/tests/konsist/KonsistPreviewTest.kt @@ -110,6 +110,7 @@ class KonsistPreviewTest { "MessageComposerViewVoicePreview", "MessagesReactionButtonAddPreview", "MessagesReactionButtonExtraPreview", + "MessagesViewSelectionModePreview", "MessagesViewWithIdentityChangePreview", "PendingMemberRowWithLongNamePreview", "PinUnlockViewInAppPreview", @@ -133,6 +134,7 @@ class KonsistPreviewTest { "SasEmojisPreview", "SecureBackupSetupViewChangePreview", "SelectedUserCannotRemovePreview", + "SelectionIndicatorSelectedPreview", "SpaceMembersViewNoHeroesPreview", "SyncStateViewServerUnreachablePreview", "TextComposerAddCaptionPreview", @@ -179,6 +181,7 @@ class KonsistPreviewTest { "TimelineItemVoiceViewScanningContentPreview", "TimelineItemVoiceViewUnifiedPreview", "TimelineViewMessageShieldPreview", + "TimelineViewSelectionModePreview", "TimelineViewWithReadMarkerBothIndicatorsPreview", "TimelineViewWithReadMarkerJumpToUnreadIndicatorOnlyPreview", "UserAvatarColorsPreview", From cb501f3b5f0186ac046b84f6e114a25b7bd60db9 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Oct 2026 20:48:24 +0200 Subject: [PATCH 41/92] Fix the Detekt issues on the timeline selection code --- .../messages/impl/timeline/components/TimelineItemRow.kt | 2 +- .../impl/timeline/model/TimelineItemSelectionData.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index 32cef08ea7..bc19f239cc 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -194,7 +194,7 @@ internal fun TimelineItemRow( layout(width = width, height = height) { contentPlaceable.place(0, 0) if (indicatorPlaceable != null) { - val bubbleCenter = contentPlaceable[BubbleVerticalCenter].takeIf { it != AlignmentLine.Unspecified } ?: (height / 2) + val bubbleCenter = contentPlaceable[BubbleVerticalCenter].takeIf { it != AlignmentLine.Unspecified } ?: height / 2 val x = if (layoutDirection == LayoutDirection.Ltr) 0 else width - indicatorPlaceable.width indicatorPlaceable.place(x = x, y = bubbleCenter - indicatorPlaceable.height / 2) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt index 60a30d5c73..eab6a2523f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemSelectionData.kt @@ -12,12 +12,12 @@ package io.element.android.features.messages.impl.timeline.model * * @param isSelectionModeActive whether the timeline is currently in selection mode. Drives * behaviour (e.g. the whole-row click target) and must reflect the real mode, not [progress]. - * @param progress animated 0f..1f value driving the visuals (content slide + indicator slide-in). - * Animated once in TimelineView and shared by every row. Differs from [isSelectionModeActive] - * during the enter/exit animation. * @param isSelected whether this specific row's event is part of the current selection. * @param canBeSelected whether this specific row's event can be selected for the action selection mode was entered for. * Rows which cannot be selected render no indicator, and their content does not slide. + * @param progress animated 0f..1f value driving the visuals (content slide + indicator slide-in). + * Animated once in TimelineView and shared by every row. Differs from [isSelectionModeActive] + * during the enter/exit animation. */ data class TimelineItemSelectionData( val isSelectionModeActive: Boolean = false, From 752b0c59b6e40f804d0499f723327089bd84cd84 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 5 Oct 2026 20:48:35 +0200 Subject: [PATCH 42/92] Move the composable lambda parameter last in TimelineItemRowContent --- .../messages/impl/timeline/components/TimelineItemRow.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/TimelineItemRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt index bc19f239cc..d729ee6532 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemRow.kt @@ -241,9 +241,9 @@ private fun TimelineItemRowContent( onJoinCallClick: (isAudioCall: Boolean) -> Unit, onSwipeToReply: (TimelineItem.Event) -> Unit, eventSink: (TimelineEvent.TimelineItemEvent) -> Unit, - eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit, modifier: Modifier = Modifier, selectionContentOffset: Dp = 0.dp, + eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit, ) { Box(modifier) { when (timelineItem) { From 90806a1c85b4ce4b9bc41269fdb8296bd2df5c4c Mon Sep 17 00:00:00 2001 From: Bardi Harborow Date: Tue, 6 Oct 2026 09:39:18 +1100 Subject: [PATCH 43/92] Hide full screen intent banner as soon as the permission is granted The permission is granted from the system settings, outside of the app, so nothing triggered a recomposition of FullScreenIntentPermissionsPresenter when the user came back. The "Enhance your call experience" banner kept being displayed until something else caused a recomposition. Check the permission again with LifecycleResumeEffect, as done in BatteryOptimizationPresenter, and add a test for this case. --- .../FullScreenIntentPermissionsPresenter.kt | 12 ++++- ...ullScreenIntentPermissionsPresenterTest.kt | 48 ++++++++++++------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt index 7d726ccdcc..201bc65e79 100644 --- a/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt +++ b/libraries/fullscreenintent/impl/src/main/kotlin/io/element/android/libraries/fullscreenintent/impl/FullScreenIntentPermissionsPresenter.kt @@ -15,11 +15,15 @@ import android.provider.Settings 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 androidx.core.app.NotificationManagerCompat import androidx.core.net.toUri import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit +import androidx.lifecycle.compose.LifecycleResumeEffect import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn @@ -61,9 +65,15 @@ class FullScreenIntentPermissionsPresenter( @Composable override fun present(): FullScreenIntentPermissionsState { val coroutineScope = rememberCoroutineScope() - val isGranted = notificationManagerCompat.canUseFullScreenIntent() + var isGranted by remember { mutableStateOf(notificationManagerCompat.canUseFullScreenIntent()) } val isBannerDismissed by isFullScreenIntentBannerDismissed.collectAsState(initial = true) + // The permission can be granted from the system settings, so check it again when the user comes back to the app. + LifecycleResumeEffect(Unit) { + isGranted = notificationManagerCompat.canUseFullScreenIntent() + onPauseOrDispose {} + } + fun handleEvent(event: FullScreenIntentPermissionsEvent) { when (event) { FullScreenIntentPermissionsEvent.Dismiss -> coroutineScope.launch { diff --git a/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt b/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt index 08bc2ed885..ec075bbc25 100644 --- a/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt +++ b/libraries/fullscreenintent/impl/src/test/kotlin/io/element/android/libraries/fullscreenintent/test/FullScreenIntentPermissionsPresenterTest.kt @@ -11,9 +11,7 @@ package io.element.android.libraries.fullscreenintent.test import android.content.Intent import android.os.Build import androidx.core.app.NotificationManagerCompat -import app.cash.molecule.RecompositionMode -import app.cash.molecule.moleculeFlow -import app.cash.turbine.test +import androidx.lifecycle.Lifecycle import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.fullscreenintent.api.FullScreenIntentPermissionsEvent @@ -23,8 +21,10 @@ import io.element.android.libraries.preferences.test.FakePreferenceDataStoreFact import io.element.android.services.toolbox.api.intent.ExternalIntentLauncher import io.element.android.services.toolbox.test.intent.FakeExternalIntentLauncher import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider +import io.element.android.tests.testutils.FakeLifecycleOwner import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.testWithLifecycleOwner import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -45,9 +45,7 @@ class FullScreenIntentPermissionsPresenterTest { every { canUseFullScreenIntent() } returns false } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val initialItem = awaitItem() assertThat(initialItem.shouldDisplayBanner).isTrue() @@ -61,21 +59,39 @@ class FullScreenIntentPermissionsPresenterTest { every { canUseFullScreenIntent() } returns true } ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val initialItem = awaitItem() assertThat(initialItem.shouldDisplayBanner).isFalse() } } + @Test + fun `shouldDisplay - becomes false when permission is granted while the app is in background`() = runTest { + var canUseFullScreenIntent = false + val presenter = createPresenter( + notificationManagerCompat = mockk { + every { canUseFullScreenIntent() } answers { canUseFullScreenIntent } + } + ) + val lifecycleOwner = FakeLifecycleOwner(Lifecycle.State.RESUMED) + presenter.testWithLifecycleOwner(lifecycleOwner) { + skipItems(1) + assertThat(awaitItem().shouldDisplayBanner).isTrue() + // User opens the system settings and grants the permission + lifecycleOwner.givenState(Lifecycle.State.STARTED) + canUseFullScreenIntent = true + lifecycleOwner.givenState(Lifecycle.State.RESUMED) + val finalItem = awaitItem() + assertThat(finalItem.permissionGranted).isTrue() + assertThat(finalItem.shouldDisplayBanner).isFalse() + } + } + @Test fun `dismissFullScreenIntentBanner - makes shouldDisplay false`() = runTest { val presenter = createPresenter() - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val loadedItem = awaitItem() loadedItem.eventSink(FullScreenIntentPermissionsEvent.Dismiss) @@ -89,9 +105,7 @@ class FullScreenIntentPermissionsPresenterTest { val launchLambda = lambdaRecorder { _ -> } val externalIntentLauncher = FakeExternalIntentLauncher(launchLambda) val presenter = createPresenter(externalIntentLauncher = externalIntentLauncher) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val loadedItem = awaitItem() loadedItem.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) @@ -108,9 +122,7 @@ class FullScreenIntentPermissionsPresenterTest { buildVersionSdkIntProvider = FakeBuildVersionSdkIntProvider(Build.VERSION_CODES.Q), externalIntentLauncher = externalIntentLauncher, ) - moleculeFlow(RecompositionMode.Immediate) { - presenter.present() - }.test { + presenter.testWithLifecycleOwner { skipItems(1) val loadedItem = awaitItem() loadedItem.eventSink(FullScreenIntentPermissionsEvent.OpenSettings) From f1723efa325cc59f792a96ed47fa1c4d540cde0e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 08:33:44 +0200 Subject: [PATCH 44/92] Update dependency com.google.testparameterinjector:test-parameter-injector to v1.24 (#7863) 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 80fcece2eb..c6a6bf57f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -171,7 +171,7 @@ test_mockk = "io.mockk:mockk:1.14.11" test_konsist = "com.github.jmartinesp:konsist:0.18.0" test_turbine = "app.cash.turbine:turbine:1.2.1" test_truth = "com.google.truth:truth:1.4.5" -test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.23" +test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.24" test_robolectric = "org.robolectric:robolectric:4.17" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } test_composable_preview_scanner = "io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.3" From 7be369a5da488c1803cd0e29a982fdeb92e2360b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 06:37:04 +0000 Subject: [PATCH 45/92] Update gradle/actions action to v6.4.0 --- .github/workflows/build.yml | 2 +- .github/workflows/build_enterprise.yml | 2 +- .github/workflows/generate_github_pages.yml | 2 +- .github/workflows/maestro-local.yml | 2 +- .github/workflows/nightlyReports.yml | 4 ++-- .github/workflows/quality.yml | 12 ++++++------ .github/workflows/recordScreenshots.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sonar.yml | 2 +- .github/workflows/sync-localazy.yml | 2 +- .github/workflows/tests.yml | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aa9be929ea..e997e1cbdd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,7 +56,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APKs diff --git a/.github/workflows/build_enterprise.yml b/.github/workflows/build_enterprise.yml index b1d7204168..294b94a872 100644 --- a/.github/workflows/build_enterprise.yml +++ b/.github/workflows/build_enterprise.yml @@ -61,7 +61,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug Gplay Enterprise APK diff --git a/.github/workflows/generate_github_pages.yml b/.github/workflows/generate_github_pages.yml index 763936e3d6..a180afb07e 100644 --- a/.github/workflows/generate_github_pages.yml +++ b/.github/workflows/generate_github_pages.yml @@ -24,7 +24,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/maestro-local.yml b/.github/workflows/maestro-local.yml index 1195c7b53d..68c76b2f6d 100644 --- a/.github/workflows/maestro-local.yml +++ b/.github/workflows/maestro-local.yml @@ -53,7 +53,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Assemble debug APK diff --git a/.github/workflows/nightlyReports.yml b/.github/workflows/nightlyReports.yml index fe38c0138f..88b267acc4 100644 --- a/.github/workflows/nightlyReports.yml +++ b/.github/workflows/nightlyReports.yml @@ -43,7 +43,7 @@ jobs: java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: false @@ -85,7 +85,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Dependency analysis diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 59a5d63b4d..006b0101f6 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -74,7 +74,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 @@ -113,7 +113,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Konsist tests @@ -154,7 +154,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run compose tests @@ -188,7 +188,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build Gplay Debug @@ -233,7 +233,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Detekt @@ -274,7 +274,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Run Ktlint check diff --git a/.github/workflows/recordScreenshots.yml b/.github/workflows/recordScreenshots.yml index efac08a99f..e6580d2f7b 100644 --- a/.github/workflows/recordScreenshots.yml +++ b/.github/workflows/recordScreenshots.yml @@ -60,7 +60,7 @@ jobs: java-version: '21' # Add gradle cache, this should speed up the process - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Record screenshots diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f264aa6de..6ec125b2d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 - name: Create app bundle env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} @@ -93,7 +93,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 - name: Create APKs env: ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 4febf272a9..d2b57a771e 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -50,7 +50,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Build debug code and test fixtures diff --git a/.github/workflows/sync-localazy.yml b/.github/workflows/sync-localazy.yml index 9065e6d4e9..8d93e958c6 100644 --- a/.github/workflows/sync-localazy.yml +++ b/.github/workflows/sync-localazy.yml @@ -25,7 +25,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} - name: Set up Python 3.12 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fe09a37ae2..b54741c566 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -68,7 +68,7 @@ jobs: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '21' - name: Configure gradle - uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 + uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0 with: cache-read-only: ${{ github.ref != 'refs/heads/develop' }} From 2de0d78ced8637b054467ddfb4a6875b1346b106 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 17:01:01 +0200 Subject: [PATCH 46/92] Import multi-account announcement strings from Localazy Add the `screen.multi_account_announcement.*` keys to the home module Localazy configuration and import the latest strings. Co-Authored-By: Claude Opus 5.5 (1M context) --- features/home/impl/src/main/res/values/localazy.xml | 3 +++ features/preferences/impl/src/main/res/values/localazy.xml | 1 + libraries/ui-strings/src/main/res/values/localazy.xml | 3 +++ tools/localazy/config.json | 1 + 4 files changed, 8 insertions(+) diff --git a/features/home/impl/src/main/res/values/localazy.xml b/features/home/impl/src/main/res/values/localazy.xml index b3c14354f5..4f3420fa23 100644 --- a/features/home/impl/src/main/res/values/localazy.xml +++ b/features/home/impl/src/main/res/values/localazy.xml @@ -24,6 +24,9 @@ "%1$s (%2$s) invited you" "This is a one time process, thanks for waiting." "Setting up your account." + "Add account" + "Go to %1$s to sign into another account. Then you can easily switch between them." + "You can now add multiple accounts" "Create a new conversation or room" "Clear filters" "Get started by messaging someone." diff --git a/features/preferences/impl/src/main/res/values/localazy.xml b/features/preferences/impl/src/main/res/values/localazy.xml index 6e70c9b685..c702ac62a3 100644 --- a/features/preferences/impl/src/main/res/values/localazy.xml +++ b/features/preferences/impl/src/main/res/values/localazy.xml @@ -110,6 +110,7 @@ If you proceed, some of your settings may change." "system settings" "System notifications turned off" "Notifications" + "Add account" "Away" "Be right back" "Custom…" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 102c627d90..0eeb366d72 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -247,6 +247,7 @@ "Call started" "You declined a call" "Chat backup" + "Chats" "Code block" "Copied to clipboard" "Copyright" @@ -287,6 +288,7 @@ Reason: %1$s." "File saved" "File saved to Downloads" "Forward message" + "Forward to" "Frequently used" "Gallery" @@ -299,6 +301,7 @@ Reason: %1$s." "GIF" "Group call in progress" + "Rooms" "Image" "In reply to %1$s" "Install APK" diff --git a/tools/localazy/config.json b/tools/localazy/config.json index 40b4ab299e..e7f5f1e036 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -199,6 +199,7 @@ "screen\\.home\\..*", "screen_roomlist_.*", "screen\\.roomlist\\..*", + "screen\\.multi_account_announcement\\..*", "session_verification_banner_.*", "confirm_recovery_key_banner_.*", "banner\\.set_up_recovery\\..*", From c654bf09478e65821925c9070183460ced1a54cc Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 17:01:02 +0200 Subject: [PATCH 47/92] Announce multi-account support Show a one-time bottom sheet on the home screen to announce that several accounts can now be added (element-hq/element-meta#3349). - Add `Announcement.MultiAccount`, which is never shown by default. - AppMigration11 queues the announcement on application upgrade only, and marks it as shown if the user already has several accounts. - HomePresenter shows the bottom sheet only when the announcement is pending, the MultiAccount feature flag is enabled and the user has a single account. - The "Add account" button hides the sheet, marks the announcement as dismissed and starts the add account flow through the new `HomeEntryPoint.Callback.navigateToAddAccount()`. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../android/appnav/LoggedInFlowNode.kt | 4 + .../features/announcement/api/Announcement.kt | 2 + .../impl/DefaultAnnouncementService.kt | 11 +- .../impl/store/DefaultAnnouncementStore.kt | 9 +- .../impl/DefaultAnnouncementServiceTest.kt | 20 +++ .../impl/store/InMemoryAnnouncementStore.kt | 4 + .../features/home/api/HomeEntryPoint.kt | 1 + .../android/features/home/impl/HomeEvent.kt | 1 + .../features/home/impl/HomeFlowNode.kt | 1 + .../features/home/impl/HomePresenter.kt | 20 +++ .../android/features/home/impl/HomeState.kt | 1 + .../home/impl/HomeStatePreviewParam.kt | 5 + .../android/features/home/impl/HomeView.kt | 11 ++ .../MultiAccountAnnouncementBottomSheet.kt | 128 ++++++++++++++++++ .../home/impl/DefaultHomeEntryPointTest.kt | 1 + .../features/home/impl/HomePresenterTest.kt | 101 ++++++++++++++ .../home/impl/roomlist/RoomListViewTest.kt | 28 +++- .../impl/migrations/AppMigration11.kt | 37 +++++ .../impl/migrations/AppMigration11Test.kt | 65 +++++++++ 19 files changed, 444 insertions(+), 6 deletions(-) create mode 100644 features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt create mode 100644 features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt create mode 100644 features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt 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 370c0db2f1..dce0911165 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -359,6 +359,10 @@ class LoggedInFlowNode( override fun navigateToBugReport() { callback.navigateToBugReport() } + + override fun navigateToAddAccount() { + callback.navigateToAddAccount() + } } homeEntryPoint.createNode( parentNode = this, diff --git a/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt index d743ae4cd6..69def559a1 100644 --- a/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt +++ b/features/announcement/api/src/main/kotlin/io/element/android/features/announcement/api/Announcement.kt @@ -17,4 +17,6 @@ sealed interface Announcement { } data object NewNotificationSound : Announcement + + data object MultiAccount : Announcement } diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt index adb81db61a..29fe04bc9f 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt @@ -29,10 +29,11 @@ class DefaultAnnouncementService( ) : AnnouncementService { override suspend fun showAnnouncement(announcement: Announcement) { when (announcement) { - is Announcement.Fullscreen -> showFullscreenAnnouncement(announcement) + is Announcement.Fullscreen -> showAnnouncementIfNeverShown(announcement) Announcement.NewNotificationSound -> { announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) } + Announcement.MultiAccount -> showAnnouncementIfNeverShown(announcement) } } @@ -44,16 +45,20 @@ class DefaultAnnouncementService( return combine( flowOf(Unit), announcementStore.announcementStatusFlow(Announcement.NewNotificationSound), - ) { _, newNotificationSoundStatus -> + announcementStore.announcementStatusFlow(Announcement.MultiAccount), + ) { _, newNotificationSoundStatus, multiAccountStatus -> buildList { if (newNotificationSoundStatus == AnnouncementStatus.Show) { add(Announcement.NewNotificationSound) } + if (multiAccountStatus == AnnouncementStatus.Show) { + add(Announcement.MultiAccount) + } } } } - private suspend fun showFullscreenAnnouncement(announcement: Announcement.Fullscreen) { + private suspend fun showAnnouncementIfNeverShown(announcement: Announcement) { val currentValue = announcementStore.announcementStatusFlow(announcement).first() if (currentValue == AnnouncementStatus.NeverShown) { announcementStore.setAnnouncementStatus(announcement, AnnouncementStatus.Show) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt index 2093baa8b3..a075c331cd 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.map private val spaceAnnouncementKey = intPreferencesKey("spaceAnnouncement") private val newNotificationSoundKey = intPreferencesKey("newNotificationSound") +private val multiAccountKey = intPreferencesKey("multiAccountAnnouncement") @ContributesBinding(AppScope::class) class DefaultAnnouncementStore( @@ -36,11 +37,14 @@ class DefaultAnnouncementStore( override fun announcementStatusFlow(announcement: Announcement): Flow { val key = announcement.toKey() // Default-status convention: Shown for one-shot intro/feature announcements that should - // NOT replay on first launch (Fullscreen.Space is disabled; NewNotificationSound is opted - // in by AppMigration08). + // NOT replay on first launch. + // - Fullscreen.Space is disabled + // - NewNotificationSound is opted in by AppMigration08 + // - MultiAccount is opted in by AppMigration11 val defaultStatus = when (announcement) { Announcement.Fullscreen.Space -> AnnouncementStatus.Shown Announcement.NewNotificationSound -> AnnouncementStatus.Shown + Announcement.MultiAccount -> AnnouncementStatus.Shown } return store.data.map { prefs -> val ordinal = prefs[key] ?: defaultStatus.ordinal @@ -56,4 +60,5 @@ class DefaultAnnouncementStore( private fun Announcement.toKey() = when (this) { Announcement.Fullscreen.Space -> spaceAnnouncementKey Announcement.NewNotificationSound -> newNotificationSoundKey + Announcement.MultiAccount -> multiAccountKey } diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt index c72d147c1d..e140a0030d 100644 --- a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt @@ -51,6 +51,22 @@ class DefaultAnnouncementServiceTest { assertThat(announcementStore.announcementStatusFlow(Announcement.NewNotificationSound).first()).isEqualTo(AnnouncementStatus.Show) } + @Test + fun `when showing MultiAccount announcement, announcement is set to show only if it was never shown`() = runTest { + val announcementStore = InMemoryAnnouncementStore() + val sut = createDefaultAnnouncementService( + announcementStore = announcementStore, + ) + assertThat(announcementStore.announcementStatusFlow(Announcement.MultiAccount).first()).isEqualTo(AnnouncementStatus.NeverShown) + sut.showAnnouncement(Announcement.MultiAccount) + assertThat(announcementStore.announcementStatusFlow(Announcement.MultiAccount).first()).isEqualTo(AnnouncementStatus.Show) + // Simulate user close the announcement + sut.onAnnouncementDismissed(Announcement.MultiAccount) + // Calling again showAnnouncement should not change the value + sut.showAnnouncement(Announcement.MultiAccount) + assertThat(announcementStore.announcementStatusFlow(Announcement.MultiAccount).first()).isEqualTo(AnnouncementStatus.Shown) + } + @Test fun `test announcementsToShowFlow`() = runTest { val announcementStore = InMemoryAnnouncementStore() @@ -61,7 +77,11 @@ class DefaultAnnouncementServiceTest { assertThat(awaitItem()).isEmpty() announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound) + announcementStore.setAnnouncementStatus(Announcement.MultiAccount, AnnouncementStatus.Show) + assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound, Announcement.MultiAccount) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Shown) + assertThat(awaitItem()).containsExactly(Announcement.MultiAccount) + announcementStore.setAnnouncementStatus(Announcement.MultiAccount, AnnouncementStatus.Shown) assertThat(awaitItem()).isEmpty() } } diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt index ed6dfec850..9b5e762eb6 100644 --- a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/store/InMemoryAnnouncementStore.kt @@ -16,9 +16,11 @@ import kotlinx.coroutines.flow.asStateFlow class InMemoryAnnouncementStore( initialFullscreenAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, initialNewNotificationSoundAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, + initialMultiAccountAnnouncementStatus: AnnouncementStatus = AnnouncementStatus.NeverShown, ) : AnnouncementStore { private val fullScreenAnnouncement = MutableStateFlow(initialFullscreenAnnouncementStatus) private val newNotificationSoundAnnouncement = MutableStateFlow(initialNewNotificationSoundAnnouncementStatus) + private val multiAccountAnnouncement = MutableStateFlow(initialMultiAccountAnnouncementStatus) override suspend fun setAnnouncementStatus(announcement: Announcement, status: AnnouncementStatus) { announcement.toMutableStateFlow().value = status @@ -31,10 +33,12 @@ class InMemoryAnnouncementStore( override suspend fun reset() { fullScreenAnnouncement.value = AnnouncementStatus.NeverShown newNotificationSoundAnnouncement.value = AnnouncementStatus.NeverShown + multiAccountAnnouncement.value = AnnouncementStatus.NeverShown } private fun Announcement.toMutableStateFlow() = when (this) { is Announcement.Fullscreen -> fullScreenAnnouncement Announcement.NewNotificationSound -> newNotificationSoundAnnouncement + Announcement.MultiAccount -> multiAccountAnnouncement } } diff --git a/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt b/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt index 1c6a0a906e..486a8bafce 100644 --- a/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt +++ b/features/home/api/src/main/kotlin/io/element/android/features/home/api/HomeEntryPoint.kt @@ -32,5 +32,6 @@ interface HomeEntryPoint : FeatureEntryPoint { fun navigateToEnterRecoveryKey() fun navigateToRoomSettings(roomId: RoomId) fun navigateToBugReport() + fun navigateToAddAccount() } } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvent.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvent.kt index 7d2bb09d30..6d1eec3dff 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvent.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeEvent.kt @@ -13,4 +13,5 @@ import io.element.android.libraries.matrix.api.core.SessionId sealed interface HomeEvent { data class SelectHomeNavigationBarItem(val item: HomeNavigationBarItem) : HomeEvent data class SwitchToAccount(val sessionId: SessionId) : HomeEvent + data object DismissMultiAccountAnnouncement : HomeEvent } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt index 74dd6e441d..a481171e97 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeFlowNode.kt @@ -221,6 +221,7 @@ class HomeFlowNode( homeState = state, onRoomClick = ::navigateToRoom, onSettingsClick = callback::navigateToSettings, + onAddAccountClick = callback::navigateToAddAccount, onStartChatClick = callback::navigateToCreateRoom, onCreateSpaceClick = callback::navigateToCreateSpace, onSetUpRecoveryClick = callback::navigateToSetUpRecovery, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt index f0bcedbb43..7c43d6a90c 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt @@ -19,12 +19,16 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.roomlist.RoomListState import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.sync.SyncService @@ -43,6 +47,8 @@ class HomePresenter( private val homeSpacesPresenter: Presenter, private val rageshakeFeatureAvailability: RageshakeFeatureAvailability, private val sessionStore: SessionStore, + private val announcementService: AnnouncementService, + private val featureFlagService: FeatureFlagService, ) : Presenter { private val currentUserWithNeighborsBuilder = CurrentUserWithNeighborsBuilder() @@ -59,6 +65,16 @@ class HomePresenter( }.collectAsState(initial = persistentListOf(matrixUser)) val isOnline by syncService.isOnline.collectAsState() val canReportBug by remember { rageshakeFeatureAvailability.isAvailable() }.collectAsState(false) + val showMultiAccountAnnouncement by remember { + combine( + announcementService.announcementsToShowFlow(), + featureFlagService.isFeatureEnabledFlow(FeatureFlags.MultiAccount), + sessionStore.sessionsFlow(), + ) { announcements, isMultiAccountEnabled, sessions -> + // Do not announce the feature to users who already have several accounts + isMultiAccountEnabled && sessions.size == 1 && announcements.contains(Announcement.MultiAccount) + } + }.collectAsState(false) val roomListState = roomListPresenter.present() val homeSpacesState = homeSpacesPresenter.present() var currentHomeNavigationBarItemOrdinal by rememberSaveable { mutableIntStateOf(HomeNavigationBarItem.Chats.ordinal) } @@ -82,6 +98,9 @@ class HomePresenter( is HomeEvent.SwitchToAccount -> coroutineState.launch { sessionStore.setLatestSession(event.sessionId.value) } + HomeEvent.DismissMultiAccountAnnouncement -> coroutineState.launch { + announcementService.onAnnouncementDismissed(Announcement.MultiAccount) + } } } @@ -95,6 +114,7 @@ class HomePresenter( homeSpacesState = homeSpacesState, snackbarMessage = snackbarMessage, canReportBug = canReportBug, + showMultiAccountAnnouncement = showMultiAccountAnnouncement, eventSink = ::handleEvent, ) } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt index 462084aefb..a49d3876e9 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt @@ -28,6 +28,7 @@ data class HomeState( val homeSpacesState: HomeSpacesState, val snackbarMessage: SnackbarMessage?, val canReportBug: Boolean, + val showMultiAccountAnnouncement: Boolean, val eventSink: (HomeEvent) -> Unit, ) { val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt index f39942275a..a17e678d25 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt @@ -41,6 +41,9 @@ open class HomeStatePreviewParam : PreviewParameterProvider { aHomeState( currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces, ), + aHomeState( + showMultiAccountAnnouncement = true, + ), ) + RoomListStatePreviewParam().values.map { aHomeState(roomListState = it) } + aHomeState( @@ -61,6 +64,7 @@ internal fun aHomeState( roomListState: RoomListState = aRoomListState(), homeSpacesState: HomeSpacesState = aHomeSpacesState(), canReportBug: Boolean = true, + showMultiAccountAnnouncement: Boolean = false, eventSink: (HomeEvent) -> Unit = {} ) = HomeState( currentUserAndNeighbors = currentUserAndNeighbors.toImmutableList(), @@ -68,6 +72,7 @@ internal fun aHomeState( hasNetworkConnection = hasNetworkConnection, snackbarMessage = snackbarMessage, canReportBug = canReportBug, + showMultiAccountAnnouncement = showMultiAccountAnnouncement, currentHomeNavigationBarItem = currentHomeNavigationBarItem, roomListState = roomListState, homeSpacesState = homeSpacesState, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index bcc3229598..6ff31a7c12 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.zIndex import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.home.impl.components.HomeTopBar +import io.element.android.features.home.impl.components.MultiAccountAnnouncementBottomSheet import io.element.android.features.home.impl.components.RoomListContentView import io.element.android.features.home.impl.components.RoomListMenuAction import io.element.android.features.home.impl.model.RoomListRoomSummary @@ -76,6 +77,7 @@ fun HomeView( homeState: HomeState, onRoomClick: (RoomId, EventId?) -> Unit, onSettingsClick: () -> Unit, + onAddAccountClick: () -> Unit, onSetUpRecoveryClick: () -> Unit, onConfirmRecoveryKeyClick: () -> Unit, onStartChatClick: () -> Unit, @@ -110,6 +112,13 @@ fun HomeView( ) } + if (homeState.showMultiAccountAnnouncement) { + MultiAccountAnnouncementBottomSheet( + onDismiss = { homeState.eventSink(HomeEvent.DismissMultiAccountAnnouncement) }, + onAddAccountClick = { if (firstThrottler.canHandle()) onAddAccountClick() }, + ) + } + leaveRoomView() HomeScaffold( @@ -366,6 +375,7 @@ internal fun HomeViewPreview(@PreviewParameter(HomeStatePreviewParam::class) sta homeState = state, onRoomClick = { _, _ -> }, onSettingsClick = {}, + onAddAccountClick = {}, onSetUpRecoveryClick = {}, onConfirmRecoveryKeyClick = {}, onStartChatClick = {}, @@ -386,6 +396,7 @@ internal fun HomeViewA11yPreview() = ElementPreview { homeState = aHomeState(), onRoomClick = { _, _ -> }, onSettingsClick = {}, + onAddAccountClick = {}, onSetUpRecoveryClick = {}, onConfirmRecoveryKeyClick = {}, onStartChatClick = {}, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt new file mode 100644 index 0000000000..f5ffaeb41f --- /dev/null +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.home.impl.components + +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.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.SheetValue +import androidx.compose.material3.rememberBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.heading +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.features.home.impl.R +import io.element.android.libraries.designsystem.components.BigIcon +import io.element.android.libraries.designsystem.preview.ElementPreview +import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.IconSource +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.annotatedTextWithBold +import io.element.android.libraries.ui.strings.CommonStrings +import kotlinx.coroutines.launch + +/** + * Bottom sheet announcing that several accounts can now be added. + * Ref: https://www.figma.com/design/kEAcfun9iSpszeUDvdKZ6b/ER-351--Multi-account-in-EX?node-id=195-43073 + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun MultiAccountAnnouncementBottomSheet( + onDismiss: () -> Unit, + onAddAccountClick: () -> Unit, +) { + val sheetState = rememberBottomSheetState( + initialValue = SheetValue.Hidden, + ) + val coroutineScope = rememberCoroutineScope() + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = onDismiss, + scrollable = false, + ) { + MultiAccountAnnouncementContent( + onAddAccountClick = { + coroutineScope.launch { + sheetState.hide() + onDismiss() + onAddAccountClick() + } + }, + ) + } +} + +@Composable +private fun MultiAccountAnnouncementContent( + onAddAccountClick: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(all = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + BigIcon( + style = BigIcon.Style.Default(CompoundIcons.UserProfileSolid()), + ) + Spacer(Modifier.height(16.dp)) + Text( + modifier = Modifier + .padding(horizontal = 8.dp) + .semantics { heading() }, + text = stringResource(R.string.screen_multi_account_announcement_title), + style = ElementTheme.typography.fontHeadingMdBold, + color = ElementTheme.colors.textPrimary, + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(8.dp)) + val path = stringResource(CommonStrings.common_settings) + " > " + stringResource(CommonStrings.common_add_another_account) + Text( + modifier = Modifier.padding(horizontal = 8.dp), + text = annotatedTextWithBold( + text = stringResource(R.string.screen_multi_account_announcement_description, path), + boldText = path, + ), + style = ElementTheme.typography.fontBodyMdRegular, + color = ElementTheme.colors.textSecondary, + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(40.dp)) + Button( + text = stringResource(R.string.screen_multi_account_announcement_action), + leadingIcon = IconSource.Vector(CompoundIcons.Plus()), + modifier = Modifier.fillMaxWidth(), + onClick = onAddAccountClick, + ) + Spacer(Modifier.height(16.dp)) + } +} + +@PreviewsDayNight +@Composable +internal fun MultiAccountAnnouncementContentPreview() = ElementPreview(fillMaxSize = true) { + MultiAccountAnnouncementBottomSheet( + onDismiss = {}, + onAddAccountClick = {}, + ) +} diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt index cf9336b6ea..643de32ecb 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/DefaultHomeEntryPointTest.kt @@ -52,6 +52,7 @@ class DefaultHomeEntryPointTest : RobolectricTest() { override fun navigateToEnterRecoveryKey() = lambdaError() override fun navigateToRoomSettings(roomId: RoomId) = lambdaError() override fun navigateToBugReport() = lambdaError() + override fun navigateToAddAccount() = lambdaError() } val result = entryPoint.createNode( parentNode = parentNode, diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt index a5553352db..2f6971da8b 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt @@ -9,12 +9,18 @@ package io.element.android.features.home.impl import com.google.common.truth.Truth.assertThat +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.home.impl.roomlist.aRoomListState import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.home.impl.spaces.aHomeSpacesState import io.element.android.features.rageshake.api.RageshakeFeatureAvailability +import io.element.android.features.rageshake.test.logs.FakeAnnouncementService import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.indicator.test.FakeIndicatorService import io.element.android.libraries.matrix.api.MatrixClient @@ -30,6 +36,8 @@ import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData import io.element.android.tests.testutils.WarmUpRule +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value import io.element.android.tests.testutils.test import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest @@ -144,6 +152,95 @@ class HomePresenterTest { assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) } } + + @Test + fun `present - multi account announcement is shown when announced, feature enabled and a single account`() = runTest { + val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + initialList = listOf(aSessionData()), + updateUserProfileResult = { _, _, _, _ -> }, + ), + announcementService = FakeAnnouncementService( + initialAnnouncementsToShowFlowValue = listOf(Announcement.MultiAccount), + ), + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.MultiAccount.key to true), + ), + ) + presenter.test { + assertThat(awaitItem().showMultiAccountAnnouncement).isFalse() + assertThat(expectMostRecentItem().showMultiAccountAnnouncement).isTrue() + } + } + + @Test + fun `present - multi account announcement is not shown when the feature is disabled`() = runTest { + val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + initialList = listOf(aSessionData()), + updateUserProfileResult = { _, _, _, _ -> }, + ), + announcementService = FakeAnnouncementService( + initialAnnouncementsToShowFlowValue = listOf(Announcement.MultiAccount), + ), + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.MultiAccount.key to false), + ), + ) + presenter.test { + assertThat(expectMostRecentItem().showMultiAccountAnnouncement).isFalse() + } + } + + @Test + fun `present - multi account announcement is not shown when there are several accounts`() = runTest { + val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData(sessionId = "@alice:server.org"), + aSessionData(sessionId = "@bob:server.org"), + ), + updateUserProfileResult = { _, _, _, _ -> }, + ), + announcementService = FakeAnnouncementService( + initialAnnouncementsToShowFlowValue = listOf(Announcement.MultiAccount), + ), + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.MultiAccount.key to true), + ), + ) + presenter.test { + assertThat(expectMostRecentItem().showMultiAccountAnnouncement).isFalse() + } + } + + @Test + fun `present - dismiss multi account announcement`() = runTest { + val onAnnouncementDismissedResult = lambdaRecorder { } + val announcementService = FakeAnnouncementService( + initialAnnouncementsToShowFlowValue = listOf(Announcement.MultiAccount), + onAnnouncementDismissedResult = onAnnouncementDismissedResult, + ) + val presenter = createHomePresenter( + sessionStore = InMemorySessionStore( + initialList = listOf(aSessionData()), + updateUserProfileResult = { _, _, _, _ -> }, + ), + announcementService = announcementService, + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.MultiAccount.key to true), + ), + ) + presenter.test { + val state = expectMostRecentItem() + assertThat(state.showMultiAccountAnnouncement).isTrue() + state.eventSink(HomeEvent.DismissMultiAccountAnnouncement) + announcementService.emitAnnouncementsToShow(emptyList()) + assertThat(awaitItem().showMultiAccountAnnouncement).isFalse() + onAnnouncementDismissedResult.assertions().isCalledOnce() + .with(value(Announcement.MultiAccount)) + } + } } internal fun createHomePresenter( @@ -154,6 +251,8 @@ internal fun createHomePresenter( indicatorService: IndicatorService = FakeIndicatorService(), homeSpacesPresenter: Presenter = Presenter { aHomeSpacesState() }, sessionStore: SessionStore = InMemorySessionStore(), + announcementService: AnnouncementService = FakeAnnouncementService(), + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), ) = HomePresenter( client = client, syncService = syncService, @@ -163,4 +262,6 @@ internal fun createHomePresenter( homeSpacesPresenter = homeSpacesPresenter, rageshakeFeatureAvailability = rageshakeFeatureAvailability, sessionStore = sessionStore, + announcementService = announcementService, + featureFlagService = featureFlagService, ) diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt index bd18cd903e..3cff7ca28b 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/roomlist/RoomListViewTest.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.v2.runAndroidComposeUiTest +import io.element.android.features.home.impl.HomeEvent import io.element.android.features.home.impl.HomeView import io.element.android.features.home.impl.R import io.element.android.features.home.impl.aHomeState @@ -259,6 +260,23 @@ class RoomListViewTest : RobolectricTest() { ) ) } + + @Test + fun `clicking on add account in the multi-account announcement emits the expected Event and invokes the callback`() = runAndroidComposeUiTest { + val eventsRecorder = EventsRecorder() + ensureCalledOnce { callback -> + setRoomListView( + state = aRoomListState(), + showMultiAccountAnnouncement = true, + homeEventSink = eventsRecorder, + onAddAccountClick = callback, + ) + clickOn(R.string.screen_multi_account_announcement_action) + // Wait for the bottom sheet to be hidden + waitForIdle() + } + eventsRecorder.assertSingle(HomeEvent.DismissMultiAccountAnnouncement) + } } private fun AndroidComposeUiTest.setRoomListView( @@ -273,12 +291,20 @@ private fun AndroidComposeUiTest.setRoomListView( onMenuActionClick: (RoomListMenuAction) -> Unit = EnsureNeverCalledWithParam(), onReportRoomClick: (RoomId) -> Unit = EnsureNeverCalledWithParam(), onDeclineInviteAndBlockUser: (RoomListRoomSummary) -> Unit = EnsureNeverCalledWithParam(), + onAddAccountClick: () -> Unit = EnsureNeverCalled(), + showMultiAccountAnnouncement: Boolean = false, + homeEventSink: (HomeEvent) -> Unit = {}, ) { setSafeContent { HomeView( - homeState = aHomeState(roomListState = state), + homeState = aHomeState( + roomListState = state, + showMultiAccountAnnouncement = showMultiAccountAnnouncement, + eventSink = homeEventSink, + ), onRoomClick = { roomId, _ -> onRoomClick(roomId) }, onSettingsClick = onSettingsClick, + onAddAccountClick = onAddAccountClick, onSetUpRecoveryClick = onSetUpRecoveryClick, onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick, onStartChatClick = onCreateRoomClick, diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt new file mode 100644 index 0000000000..97bc5d24a4 --- /dev/null +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.migration.impl.migrations + +import dev.zacsweers.metro.AppScope +import dev.zacsweers.metro.ContributesIntoSet +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService +import io.element.android.libraries.sessionstorage.api.SessionStore + +/** + * Ensure the multi-account announcement is displayed, but only on application upgrade, + * and only if the user does not already have several accounts. + */ +@ContributesIntoSet(AppScope::class) +class AppMigration11( + private val sessionStore: SessionStore, + private val announcementService: AnnouncementService, +) : AppMigration { + override val order: Int = 11 + + override suspend fun migrate(isFreshInstall: Boolean) { + if (isFreshInstall) return + + if (sessionStore.getAllSessions().size > 1) { + // The user already uses several accounts, no need to announce the feature + announcementService.onAnnouncementDismissed(Announcement.MultiAccount) + } else { + announcementService.showAnnouncement(Announcement.MultiAccount) + } + } +} diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt new file mode 100644 index 0000000000..08bfd5b2ec --- /dev/null +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.features.migration.impl.migrations + +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.rageshake.test.logs.FakeAnnouncementService +import io.element.android.libraries.sessionstorage.test.InMemorySessionStore +import io.element.android.libraries.sessionstorage.test.aSessionData +import io.element.android.tests.testutils.lambda.lambdaRecorder +import io.element.android.tests.testutils.lambda.value +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class AppMigration11Test { + @Test + fun `migration on fresh install should not invoke the AnnouncementService`() = runTest { + val service = FakeAnnouncementService() + val migration = AppMigration11( + sessionStore = InMemorySessionStore(initialList = listOf(aSessionData())), + announcementService = service, + ) + // FakeAnnouncementService lambdas throw by default + migration.migrate(isFreshInstall = true) + } + + @Test + fun `migration on upgrade with a single account should show the announcement`() = runTest { + val showAnnouncementResult = lambdaRecorder { } + val service = FakeAnnouncementService( + showAnnouncementResult = showAnnouncementResult, + ) + val migration = AppMigration11( + sessionStore = InMemorySessionStore(initialList = listOf(aSessionData())), + announcementService = service, + ) + migration.migrate(isFreshInstall = false) + showAnnouncementResult.assertions().isCalledOnce() + .with(value(Announcement.MultiAccount)) + } + + @Test + fun `migration on upgrade with several accounts should mark the announcement as dismissed`() = runTest { + val onAnnouncementDismissedResult = lambdaRecorder { } + val service = FakeAnnouncementService( + onAnnouncementDismissedResult = onAnnouncementDismissedResult, + ) + val migration = AppMigration11( + sessionStore = InMemorySessionStore( + initialList = listOf( + aSessionData(sessionId = "@alice:server.org"), + aSessionData(sessionId = "@bob:server.org"), + ) + ), + announcementService = service, + ) + migration.migrate(isFreshInstall = false) + onAnnouncementDismissedResult.assertions().isCalledOnce() + .with(value(Announcement.MultiAccount)) + } +} From 4e25ecd34cf9f0e49671cfaee56585e3f82d91c7 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 18:01:54 +0200 Subject: [PATCH 48/92] Dismiss the multi-account announcement when the user has several accounts If the announcement was queued at upgrade while the user had a single account, and the user then added another account without seeing it, the announcement stayed pending and was shown later if the user removed an account. Mark it as dismissed as soon as several accounts are detected. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../android/features/home/impl/HomePresenter.kt | 15 +++++++++++++++ .../features/home/impl/HomePresenterTest.kt | 6 +++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt index 7c43d6a90c..95c98a4aec 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt @@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @Inject @@ -75,6 +76,20 @@ class HomePresenter( isMultiAccountEnabled && sessions.size == 1 && announcements.contains(Announcement.MultiAccount) } }.collectAsState(false) + LaunchedEffect(Unit) { + // The user already uses several accounts, so the feature does not need to be announced anymore, + // even if they later remove accounts. + combine( + announcementService.announcementsToShowFlow(), + sessionStore.sessionsFlow(), + ) { announcements, sessions -> + announcements.contains(Announcement.MultiAccount) && sessions.size > 1 + } + .filter { it } + .collect { + announcementService.onAnnouncementDismissed(Announcement.MultiAccount) + } + } val roomListState = roomListPresenter.present() val homeSpacesState = homeSpacesPresenter.present() var currentHomeNavigationBarItemOrdinal by rememberSaveable { mutableIntStateOf(HomeNavigationBarItem.Chats.ordinal) } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt index 2f6971da8b..d4b03b68c7 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt @@ -193,7 +193,8 @@ class HomePresenterTest { } @Test - fun `present - multi account announcement is not shown when there are several accounts`() = runTest { + fun `present - multi account announcement is not shown and is dismissed when there are several accounts`() = runTest { + val onAnnouncementDismissedResult = lambdaRecorder { } val presenter = createHomePresenter( sessionStore = InMemorySessionStore( initialList = listOf( @@ -204,6 +205,7 @@ class HomePresenterTest { ), announcementService = FakeAnnouncementService( initialAnnouncementsToShowFlowValue = listOf(Announcement.MultiAccount), + onAnnouncementDismissedResult = onAnnouncementDismissedResult, ), featureFlagService = FakeFeatureFlagService( initialState = mapOf(FeatureFlags.MultiAccount.key to true), @@ -211,6 +213,8 @@ class HomePresenterTest { ) presenter.test { assertThat(expectMostRecentItem().showMultiAccountAnnouncement).isFalse() + onAnnouncementDismissedResult.assertions().isCalledOnce() + .with(value(Announcement.MultiAccount)) } } From 4d183ffeaf33ebf1304550f4cf07cebbe69909aa Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Oct 2026 09:04:30 +0200 Subject: [PATCH 49/92] Add consign when AI adds new states for screenshot. --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index a4a73ca965..dc34ebd571 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ PRs must meet these rules. - Create previews for **all main states** of a Composable. - Use `@PreviewsDayNight` for consistency. -- Use `PreviewParameterProvider` (e.g., `FooStatePreviewParam`) to provide states. +- Use `PreviewParameterProvider` (e.g., `FooStatePreviewParam`) to provide states. Always add new states to existing providers at the end of the sequence. - Wrap previews in `ElementPreview { ... }`. - When writing tests, never try to record the screenshots, the CI will do it. From d91c22d1b29e67881c398b6be2021adf6a59bdee Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Oct 2026 09:10:47 +0200 Subject: [PATCH 50/92] Move new states at the end of the sequence --- .../android/features/home/impl/HomeStatePreviewParam.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt index a17e678d25..f86c32c532 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStatePreviewParam.kt @@ -41,9 +41,6 @@ open class HomeStatePreviewParam : PreviewParameterProvider { aHomeState( currentHomeNavigationBarItem = HomeNavigationBarItem.Spaces, ), - aHomeState( - showMultiAccountAnnouncement = true, - ), ) + RoomListStatePreviewParam().values.map { aHomeState(roomListState = it) } + aHomeState( @@ -51,6 +48,8 @@ open class HomeStatePreviewParam : PreviewParameterProvider { homeSpacesState = aHomeSpacesState( spaceRooms = emptyList(), ), + ) + aHomeState( + showMultiAccountAnnouncement = true, ) } From 86e72ac11e19d946ca6ccd7a655f369471af5d1d Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 6 Oct 2026 07:38:09 +0000 Subject: [PATCH 51/92] Update screenshots --- ...mpl.components_MultiAccountAnnouncementContent_Day_0_en.png | 3 +++ ...l.components_MultiAccountAnnouncementContent_Night_0_en.png | 3 +++ .../snapshots/images/features.home.impl_HomeView_Day_17_en.png | 3 +++ .../images/features.home.impl_HomeView_Night_17_en.png | 3 +++ 4 files changed, 12 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_17_en.png create mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_17_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png new file mode 100644 index 0000000000..747c2f7197 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69f16135c4bfcddac589989018f053f3a09b6111a55370915ccaf2e8837c89e7 +size 33204 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png new file mode 100644 index 0000000000..1d11c81145 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cf546530844f35beb6efa6f1b6874dcf95d3079889a1ea21702d14266ca81e0 +size 31449 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_17_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_17_en.png new file mode 100644 index 0000000000..342d1ab6ff --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Day_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5c44832e3d4cbb637d48abc19174a66bdff343de3e60aa94c3d6bfaac44e78d +size 62363 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_17_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_17_en.png new file mode 100644 index 0000000000..a2a191b30f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeView_Night_17_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:513e23929e81026a24cb2d964fd8a65ba4998ac715021d480d7e7211d6a48d0a +size 59148 From 72f4c4f8972d8106b3ec68eee159e01ce347d910 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Oct 2026 09:33:33 +0200 Subject: [PATCH 52/92] Fix multi-account announcement never being displayed --- .../impl/store/DefaultAnnouncementStore.kt | 4 ++-- .../migration/impl/migrations/AppMigration11.kt | 7 +++---- .../migration/impl/migrations/AppMigration11Test.kt | 10 +++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt index a075c331cd..c51fc11f26 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt @@ -40,11 +40,11 @@ class DefaultAnnouncementStore( // NOT replay on first launch. // - Fullscreen.Space is disabled // - NewNotificationSound is opted in by AppMigration08 - // - MultiAccount is opted in by AppMigration11 + // - MultiAccount is NeverShown by default, AppMigration11 sets it to Show or Shown. val defaultStatus = when (announcement) { Announcement.Fullscreen.Space -> AnnouncementStatus.Shown Announcement.NewNotificationSound -> AnnouncementStatus.Shown - Announcement.MultiAccount -> AnnouncementStatus.Shown + Announcement.MultiAccount -> AnnouncementStatus.NeverShown } return store.data.map { prefs -> val ordinal = prefs[key] ?: defaultStatus.ordinal diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt index 97bc5d24a4..71fc6a1d52 100644 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt +++ b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.sessionstorage.api.SessionStore /** * Ensure the multi-account announcement is displayed, but only on application upgrade, * and only if the user does not already have several accounts. + * In the other cases, mark the announcement as already shown. */ @ContributesIntoSet(AppScope::class) class AppMigration11( @@ -25,10 +26,8 @@ class AppMigration11( override val order: Int = 11 override suspend fun migrate(isFreshInstall: Boolean) { - if (isFreshInstall) return - - if (sessionStore.getAllSessions().size > 1) { - // The user already uses several accounts, no need to announce the feature + if (isFreshInstall || sessionStore.getAllSessions().size > 1) { + // New users, or users who already have several accounts, do not need to be informed about the feature announcementService.onAnnouncementDismissed(Announcement.MultiAccount) } else { announcementService.showAnnouncement(Announcement.MultiAccount) diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt index 08bfd5b2ec..b8d45ace46 100644 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt +++ b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt @@ -18,14 +18,18 @@ import org.junit.Test class AppMigration11Test { @Test - fun `migration on fresh install should not invoke the AnnouncementService`() = runTest { - val service = FakeAnnouncementService() + fun `migration on fresh install should mark the announcement as dismissed`() = runTest { + val onAnnouncementDismissedResult = lambdaRecorder { } + val service = FakeAnnouncementService( + onAnnouncementDismissedResult = onAnnouncementDismissedResult, + ) val migration = AppMigration11( sessionStore = InMemorySessionStore(initialList = listOf(aSessionData())), announcementService = service, ) - // FakeAnnouncementService lambdas throw by default migration.migrate(isFreshInstall = true) + onAnnouncementDismissedResult.assertions().isCalledOnce() + .with(value(Announcement.MultiAccount)) } @Test From ddcd01a2bfea592d750ecde99ce6c89721e50030 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Oct 2026 09:46:28 +0200 Subject: [PATCH 53/92] Follow naming convention --- .../home/impl/components/MultiAccountAnnouncementBottomSheet.kt | 2 +- ...components_MultiAccountAnnouncementBottomSheet_Day_0_en.png} | 0 ...mponents_MultiAccountAnnouncementBottomSheet_Night_0_en.png} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/uitests/src/test/snapshots/images/{features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png => features.home.impl.components_MultiAccountAnnouncementBottomSheet_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png => features.home.impl.components_MultiAccountAnnouncementBottomSheet_Night_0_en.png} (100%) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt index f5ffaeb41f..cec1dbf3a0 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/MultiAccountAnnouncementBottomSheet.kt @@ -120,7 +120,7 @@ private fun MultiAccountAnnouncementContent( @PreviewsDayNight @Composable -internal fun MultiAccountAnnouncementContentPreview() = ElementPreview(fillMaxSize = true) { +internal fun MultiAccountAnnouncementBottomSheetPreview() = ElementPreview(fillMaxSize = true) { MultiAccountAnnouncementBottomSheet( onDismiss = {}, onAddAccountClick = {}, diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementBottomSheet_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementBottomSheet_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementBottomSheet_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementContent_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/features.home.impl.components_MultiAccountAnnouncementBottomSheet_Night_0_en.png From b1f4178100ee3c908a2eff732b88aa66d240e727 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 6 Oct 2026 10:30:21 +0200 Subject: [PATCH 54/92] Replace AppMigration11 by a dismissal when entering the login flow AppMigration11 was run once, in the first release containing it, whatever the state of the MultiAccount feature flag. Users installing the app while the feature flag was disabled were considered as new users and would never see the announcement once the feature flag got enabled. The multi-account announcement is now pending by default (NeverShown), and is marked as shown when the user enters the login flow while the feature is enabled, either for the first time or to add another account. Users who were already logged in before the feature became available will see the announcement. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../android/appnav/NotLoggedInFlowNode.kt | 15 ++++ .../android/appnav/RootFlowNodeTest.kt | 2 + .../impl/DefaultAnnouncementService.kt | 4 +- .../impl/store/DefaultAnnouncementStore.kt | 3 +- .../impl/DefaultAnnouncementServiceTest.kt | 6 +- .../impl/migrations/AppMigration11.kt | 36 ---------- .../impl/migrations/AppMigration11Test.kt | 69 ------------------- 7 files changed, 26 insertions(+), 109 deletions(-) delete mode 100644 features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt delete mode 100644 features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index 080f3c1d77..8b8b323aab 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -13,6 +13,7 @@ package io.element.android.appnav import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.lifecycle.lifecycleScope import coil3.SingletonImageLoader import coil3.annotation.DelicateCoilApi import com.bumble.appyx.core.lifecycle.subscribe @@ -24,6 +25,8 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode +import io.element.android.features.announcement.api.Announcement +import io.element.android.features.announcement.api.AnnouncementService import io.element.android.features.login.api.LoginEntryPoint import io.element.android.features.login.api.LoginParams import io.element.android.libraries.architecture.BackstackView @@ -33,8 +36,11 @@ import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.inputs import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices import io.element.android.libraries.designsystem.utils.ScreenOrientation +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher +import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) @@ -45,6 +51,8 @@ class NotLoggedInFlowNode( private val loginEntryPoint: LoginEntryPoint, private val imageLoaderHolder: ImageLoaderHolder, private val analyticsColdStartWatcher: AnalyticsColdStartWatcher, + private val featureFlagService: FeatureFlagService, + private val announcementService: AnnouncementService, ) : BaseFlowNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -68,6 +76,13 @@ class NotLoggedInFlowNode( override fun onBuilt() { super.onBuilt() analyticsColdStartWatcher.whenLoggingIn() + lifecycleScope.launch { + // Users who enter the login flow while the feature is available, either for the first time or to add + // another account, do not need to be informed about the multi-account feature. + if (featureFlagService.isFeatureEnabled(FeatureFlags.MultiAccount)) { + announcementService.onAnnouncementDismissed(Announcement.MultiAccount) + } + } lifecycle.subscribe( onResume = { SingletonImageLoader.setUnsafe(imageLoaderHolder.get()) diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt index cc23aea626..dd0a10c2ea 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt @@ -141,6 +141,8 @@ class RootFlowNodeTest : RobolectricTest() { loginEntryPoint = loginEntryPoint, imageLoaderHolder = FakeImageLoaderHolder(), analyticsColdStartWatcher = FakeAnalyticsColdStartWatcher(), + featureFlagService = FakeFeatureFlagService(), + announcementService = FakeAnnouncementService(), ) } ) diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt index 29fe04bc9f..3c36989571 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementService.kt @@ -51,7 +51,9 @@ class DefaultAnnouncementService( if (newNotificationSoundStatus == AnnouncementStatus.Show) { add(Announcement.NewNotificationSound) } - if (multiAccountStatus == AnnouncementStatus.Show) { + // The multi-account announcement is pending until it is dismissed. Users who were already + // logged in when the feature became available still have the default NeverShown status. + if (multiAccountStatus != AnnouncementStatus.Shown) { add(Announcement.MultiAccount) } } diff --git a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt index c51fc11f26..2235285bcf 100644 --- a/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt +++ b/features/announcement/impl/src/main/kotlin/io/element/android/features/announcement/impl/store/DefaultAnnouncementStore.kt @@ -40,7 +40,8 @@ class DefaultAnnouncementStore( // NOT replay on first launch. // - Fullscreen.Space is disabled // - NewNotificationSound is opted in by AppMigration08 - // - MultiAccount is NeverShown by default, AppMigration11 sets it to Show or Shown. + // - MultiAccount is NeverShown by default, which means pending. It is set to Shown when the user enters + // the login flow while the feature is enabled (see NotLoggedInFlowNode), or dismisses the announcement. val defaultStatus = when (announcement) { Announcement.Fullscreen.Space -> AnnouncementStatus.Shown Announcement.NewNotificationSound -> AnnouncementStatus.Shown diff --git a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt index e140a0030d..18442b362d 100644 --- a/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt +++ b/features/announcement/impl/src/test/kotlin/io/element/android/features/announcement/impl/DefaultAnnouncementServiceTest.kt @@ -74,10 +74,12 @@ class DefaultAnnouncementServiceTest { announcementStore = announcementStore, ) sut.announcementsToShowFlow().test { - assertThat(awaitItem()).isEmpty() + // MultiAccount is pending by default + assertThat(awaitItem()).containsExactly(Announcement.MultiAccount) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Show) - assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound) + assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound, Announcement.MultiAccount) announcementStore.setAnnouncementStatus(Announcement.MultiAccount, AnnouncementStatus.Show) + // Still pending assertThat(awaitItem()).containsExactly(Announcement.NewNotificationSound, Announcement.MultiAccount) announcementStore.setAnnouncementStatus(Announcement.NewNotificationSound, AnnouncementStatus.Shown) assertThat(awaitItem()).containsExactly(Announcement.MultiAccount) diff --git a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt b/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt deleted file mode 100644 index 71fc6a1d52..0000000000 --- a/features/migration/impl/src/main/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.migration.impl.migrations - -import dev.zacsweers.metro.AppScope -import dev.zacsweers.metro.ContributesIntoSet -import io.element.android.features.announcement.api.Announcement -import io.element.android.features.announcement.api.AnnouncementService -import io.element.android.libraries.sessionstorage.api.SessionStore - -/** - * Ensure the multi-account announcement is displayed, but only on application upgrade, - * and only if the user does not already have several accounts. - * In the other cases, mark the announcement as already shown. - */ -@ContributesIntoSet(AppScope::class) -class AppMigration11( - private val sessionStore: SessionStore, - private val announcementService: AnnouncementService, -) : AppMigration { - override val order: Int = 11 - - override suspend fun migrate(isFreshInstall: Boolean) { - if (isFreshInstall || sessionStore.getAllSessions().size > 1) { - // New users, or users who already have several accounts, do not need to be informed about the feature - announcementService.onAnnouncementDismissed(Announcement.MultiAccount) - } else { - announcementService.showAnnouncement(Announcement.MultiAccount) - } - } -} diff --git a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt b/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt deleted file mode 100644 index b8d45ace46..0000000000 --- a/features/migration/impl/src/test/kotlin/io/element/android/features/migration/impl/migrations/AppMigration11Test.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.features.migration.impl.migrations - -import io.element.android.features.announcement.api.Announcement -import io.element.android.features.rageshake.test.logs.FakeAnnouncementService -import io.element.android.libraries.sessionstorage.test.InMemorySessionStore -import io.element.android.libraries.sessionstorage.test.aSessionData -import io.element.android.tests.testutils.lambda.lambdaRecorder -import io.element.android.tests.testutils.lambda.value -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class AppMigration11Test { - @Test - fun `migration on fresh install should mark the announcement as dismissed`() = runTest { - val onAnnouncementDismissedResult = lambdaRecorder { } - val service = FakeAnnouncementService( - onAnnouncementDismissedResult = onAnnouncementDismissedResult, - ) - val migration = AppMigration11( - sessionStore = InMemorySessionStore(initialList = listOf(aSessionData())), - announcementService = service, - ) - migration.migrate(isFreshInstall = true) - onAnnouncementDismissedResult.assertions().isCalledOnce() - .with(value(Announcement.MultiAccount)) - } - - @Test - fun `migration on upgrade with a single account should show the announcement`() = runTest { - val showAnnouncementResult = lambdaRecorder { } - val service = FakeAnnouncementService( - showAnnouncementResult = showAnnouncementResult, - ) - val migration = AppMigration11( - sessionStore = InMemorySessionStore(initialList = listOf(aSessionData())), - announcementService = service, - ) - migration.migrate(isFreshInstall = false) - showAnnouncementResult.assertions().isCalledOnce() - .with(value(Announcement.MultiAccount)) - } - - @Test - fun `migration on upgrade with several accounts should mark the announcement as dismissed`() = runTest { - val onAnnouncementDismissedResult = lambdaRecorder { } - val service = FakeAnnouncementService( - onAnnouncementDismissedResult = onAnnouncementDismissedResult, - ) - val migration = AppMigration11( - sessionStore = InMemorySessionStore( - initialList = listOf( - aSessionData(sessionId = "@alice:server.org"), - aSessionData(sessionId = "@bob:server.org"), - ) - ), - announcementService = service, - ) - migration.migrate(isFreshInstall = false) - onAnnouncementDismissedResult.assertions().isCalledOnce() - .with(value(Announcement.MultiAccount)) - } -} From fe8b3764e5a4462dee7dd1098e6542a477f3d27c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:52:02 +0200 Subject: [PATCH 55/92] Move matrixmedia sources to the `libraries.matrixmedia.*` packages The matrixmedia modules were using the `libraries.matrix.ui.media` package (shared with the matrixui module) and `libraries.matrix.ui.media.*` namespaces. Use `libraries.matrixmedia.api`, `libraries.matrixmedia.impl` and `libraries.matrixmedia.test` instead, matching the module paths. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../io/element/android/appnav/LoggedInAppScopeFlowNode.kt | 2 +- .../io/element/android/appnav/NotLoggedInFlowNode.kt | 2 +- .../android/appnav/root/RootNavStateFlowFactory.kt | 2 +- .../kotlin/io/element/android/appnav/RootFlowNodeTest.kt | 2 +- .../impl/notifications/RingingCallNotificationCreator.kt | 2 +- .../android/features/call/impl/utils/ActiveCallManager.kt | 2 +- .../notifications/RingingCallNotificationCreatorTest.kt | 2 +- .../features/call/utils/DefaultActiveCallManagerTest.kt | 2 +- .../impl/timeline/components/MessagesReactionButton.kt | 2 +- .../components/event/TimelineItemAttachmentsListView.kt | 2 +- .../timeline/components/event/TimelineItemStickerView.kt | 2 +- .../timeline/components/event/TimelineItemVideoView.kt | 6 +++--- .../components/reactionsummary/ReactionSummaryView.kt | 2 +- .../timeline/model/event/TimelineItemGalleryContent.kt | 6 +++--- .../impl/timeline/model/event/TimelineItemImageContent.kt | 6 +++--- libraries/matrixmedia/api/build.gradle.kts | 2 +- .../{matrix/ui/media => matrixmedia/api}/Avatar.kt | 2 +- .../ui/media => matrixmedia/api}/ImageLoaderHolder.kt | 2 +- .../api}/InitialsAvatarBitmapGenerator.kt | 2 +- .../ui/media => matrixmedia/api}/MediaRequestData.kt | 2 +- libraries/matrixmedia/impl/build.gradle.kts | 2 +- .../ui/media => matrixmedia/impl}/AvatarDataExt.kt | 4 +++- .../impl}/AvatarDataFetcherFactory.kt | 2 +- .../ui/media => matrixmedia/impl}/CoilMediaFetcher.kt | 3 ++- .../impl}/DefaultImageLoaderHolder.kt | 3 ++- .../impl}/DefaultInitialsAvatarBitmapGenerator.kt | 3 ++- .../ui/media => matrixmedia/impl}/ImageLoaderFactories.kt | 2 +- .../impl}/MediaRequestDataFetcherFactory.kt | 3 ++- .../media => matrixmedia/impl}/MediaRequestDataKeyer.kt | 3 ++- .../impl}/AvatarDataFetcherFactoryTest.kt | 2 +- .../impl}/DefaultImageLoaderHolderTest.kt | 2 +- .../media => matrixmedia/impl}/FakeImageLoaderFactory.kt | 2 +- libraries/matrixmedia/test/build.gradle.kts | 2 +- .../ui/media => matrixmedia}/test/FakeImageLoader.kt | 2 +- .../media => matrixmedia}/test/FakeImageLoaderHolder.kt | 4 ++-- .../test/FakeInitialsAvatarBitmapGenerator.kt | 4 ++-- .../libraries/matrix/ui/components/AttachmentThumbnail.kt | 2 +- .../impl/details/MediaDeleteConfirmationBottomSheet.kt | 2 +- .../android/libraries/mediaviewer/impl/model/MediaItem.kt | 2 +- .../libraries/mediaviewer/impl/viewer/MediaViewerView.kt | 2 +- .../push/api/notifications/NotificationBitmapLoader.kt | 2 +- .../impl/notifications/DefaultNotificationBitmapLoader.kt | 6 +++--- .../notifications/DefaultNotificationDrawerManager.kt | 2 +- .../DefaultNotificationConversationService.kt | 2 +- .../DefaultBaseRoomGroupMessageCreatorTest.kt | 8 ++++---- .../notifications/DefaultNotificationDrawerManagerTest.kt | 2 +- .../impl/notifications/NotificationDataFactoryTest.kt | 2 +- .../push/impl/notifications/NotificationRendererTest.kt | 2 +- .../DefaultNotificationConversationServiceTest.kt | 2 +- .../factories/DefaultNotificationCreatorTest.kt | 4 ++-- ...media.impl_InitialsAvatarBitmapGenerator_Day_0_en.png} | 0 ...dia.impl_InitialsAvatarBitmapGenerator_Night_0_en.png} | 0 52 files changed, 71 insertions(+), 64 deletions(-) rename libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/api}/Avatar.kt (92%) rename libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/api}/ImageLoaderHolder.kt (95%) rename libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/api}/InitialsAvatarBitmapGenerator.kt (95%) rename libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/api}/MediaRequestData.kt (96%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/AvatarDataExt.kt (73%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/AvatarDataFetcherFactory.kt (96%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/CoilMediaFetcher.kt (97%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/DefaultImageLoaderHolder.kt (93%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/DefaultInitialsAvatarBitmapGenerator.kt (97%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/ImageLoaderFactories.kt (97%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/MediaRequestDataFetcherFactory.kt (86%) rename libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/MediaRequestDataKeyer.kt (87%) rename libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/AvatarDataFetcherFactoryTest.kt (98%) rename libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/DefaultImageLoaderHolderTest.kt (98%) rename libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia/impl}/FakeImageLoaderFactory.kt (93%) rename libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia}/test/FakeImageLoader.kt (96%) rename libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia}/test/FakeImageLoaderHolder.kt (85%) rename libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/{matrix/ui/media => matrixmedia}/test/FakeInitialsAvatarBitmapGenerator.kt (87%) rename tests/uitests/src/test/snapshots/images/{libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en.png => libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Day_0_en.png} (100%) rename tests/uitests/src/test/snapshots/images/{libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en.png => libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Night_0_en.png} (100%) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt index 0dbcb0f3f1..ee8398186d 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInAppScopeFlowNode.kt @@ -33,7 +33,7 @@ import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.matrix.api.MatrixClient -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import kotlinx.parcelize.Parcelize /** diff --git a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt index 8b8b323aab..c66ac97d83 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/NotLoggedInFlowNode.kt @@ -38,7 +38,7 @@ import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileD import io.element.android.libraries.designsystem.utils.ScreenOrientation import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize diff --git a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt index 8b02752a64..17d6826a6c 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/root/RootNavStateFlowFactory.kt @@ -13,7 +13,7 @@ import com.bumble.appyx.core.state.SavedStateMap import dev.zacsweers.metro.Inject import io.element.android.appnav.session.MatrixSessionCache import io.element.android.features.preferences.api.CacheService -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory import io.element.android.libraries.sessionstorage.api.SessionStore import kotlinx.coroutines.flow.Flow diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt index dd0a10c2ea..5967a6189f 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RootFlowNodeTest.kt @@ -42,7 +42,7 @@ import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.test.FakeSdkMetadata import io.element.android.libraries.matrix.test.auth.FakeMatrixAuthenticationService import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder +import io.element.android.libraries.matrixmedia.test.FakeImageLoaderHolder import io.element.android.libraries.oauth.test.FakeOAuthActionFlow import io.element.android.libraries.oauth.test.FakeOAuthIntentResolver import io.element.android.libraries.preferences.test.FakeSessionPreferencesStoreFactory diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt index 78b08e4468..82a4ec0bb5 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreator.kt @@ -29,7 +29,7 @@ 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.core.SessionId import io.element.android.libraries.matrix.api.core.UserId -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import kotlin.time.Duration.Companion.seconds diff --git a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt index c0f01fe0ac..74a6b4873b 100644 --- a/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt +++ b/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/ActiveCallManager.kt @@ -31,7 +31,7 @@ import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.push.api.notifications.ForegroundServiceType import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.api.notifications.OnMissedCallNotificationHandler diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt index 3c072c5958..ffdd51e69a 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder +import io.element.android.libraries.matrixmedia.test.FakeImageLoaderHolder import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.robolectric.RobolectricTest diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt index c95f8c5088..0e99b59c69 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt @@ -34,7 +34,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.room.aRoomInfo -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder +import io.element.android.libraries.matrixmedia.test.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.ForegroundServiceType import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.test.notifications.FakeOnMissedCallNotificationHandler 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 931e0de801..7b667c0d6a 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 @@ -52,7 +52,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.ui.strings.CommonStrings @Composable diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt index 232c107698..2e71af8447 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemAttachmentsListView.kt @@ -48,13 +48,13 @@ 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.Icon import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationValue import io.element.android.libraries.matrix.ui.media.contentvalidation.InvalidContentView import io.element.android.libraries.matrix.ui.media.contentvalidation.NotFoundContentView import io.element.android.libraries.matrix.ui.media.contentvalidation.collectMediaState import io.element.android.libraries.matrix.ui.media.contentvalidation.rememberEventContentValidationState +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.ui.strings.CommonStrings import kotlin.math.max diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt index c7878c00f5..aef5952689 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemStickerView.kt @@ -37,10 +37,10 @@ import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenu import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.NoopContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.collectOverallState +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.ui.strings.CommonStrings private const val STICKER_SIZE_IN_DP = 128 diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt index 5b3803713e..a4dede5d37 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemVideoView.kt @@ -47,12 +47,12 @@ import io.element.android.libraries.designsystem.modifiers.onKeyboardContextMenu import io.element.android.libraries.designsystem.modifiers.roundedBackground import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight -import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_HEIGHT -import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.NoopContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.collectOverallState +import io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_HEIGHT +import io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_WIDTH +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.isTalkbackActive diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt index 5b6c7fda37..17e650a9e9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/reactionsummary/ReactionSummaryView.kt @@ -71,8 +71,8 @@ import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.matrixmedia.api.MediaRequestData import kotlinx.coroutines.launch internal val REACTION_SUMMARY_LINE_HEIGHT = 25.sp diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt index 840f19488d..cfb702678c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemGalleryContent.kt @@ -8,7 +8,7 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.MediaRequestData import kotlinx.collections.immutable.ImmutableList import kotlin.time.Duration @@ -51,8 +51,8 @@ data class GalleryItem( MediaRequestData( source = thumbnailSource ?: mediaSource, kind = MediaRequestData.Kind.Thumbnail( - width = thumbnailWidth?.toLong() ?: io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH, - height = thumbnailHeight?.toLong() ?: io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_HEIGHT, + width = thumbnailWidth?.toLong() ?: io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_WIDTH, + height = thumbnailHeight?.toLong() ?: io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_HEIGHT, ), ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt index 5e78adc945..e67738b0fd 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemImageContent.kt @@ -10,9 +10,9 @@ package io.element.android.features.messages.impl.timeline.model.event import io.element.android.libraries.core.mimetype.MimeTypes.isMimeTypeAnimatedImage import io.element.android.libraries.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_HEIGHT -import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_HEIGHT +import io.element.android.libraries.matrixmedia.api.MAX_THUMBNAIL_WIDTH +import io.element.android.libraries.matrixmedia.api.MediaRequestData data class TimelineItemImageContent( override val filename: String, diff --git a/libraries/matrixmedia/api/build.gradle.kts b/libraries/matrixmedia/api/build.gradle.kts index 90c7ee635e..500b714c6a 100644 --- a/libraries/matrixmedia/api/build.gradle.kts +++ b/libraries/matrixmedia/api/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.libraries.matrix.ui.media.api" + namespace = "io.element.android.libraries.matrixmedia.api" } dependencies { diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/Avatar.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/Avatar.kt similarity index 92% rename from libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/Avatar.kt rename to libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/Avatar.kt index 4d4dd402d6..09c0d19f2c 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/Avatar.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/Avatar.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.api /** * The size in pixel of the thumbnail to generate for the avatar. diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/ImageLoaderHolder.kt similarity index 95% rename from libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt rename to libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/ImageLoaderHolder.kt index 228fbd2cff..e78bdfc3b5 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderHolder.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/ImageLoaderHolder.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.api import coil3.ImageLoader import io.element.android.libraries.matrix.api.MatrixClient diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/InitialsAvatarBitmapGenerator.kt similarity index 95% rename from libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt rename to libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/InitialsAvatarBitmapGenerator.kt index f145383e87..f77fd5f032 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/InitialsAvatarBitmapGenerator.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/InitialsAvatarBitmapGenerator.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.api import android.graphics.Bitmap import io.element.android.libraries.designsystem.components.avatar.AvatarData diff --git a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/MediaRequestData.kt similarity index 96% rename from libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt rename to libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/MediaRequestData.kt index 47841a573b..a1f4d2af04 100644 --- a/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestData.kt +++ b/libraries/matrixmedia/api/src/main/kotlin/io/element/android/libraries/matrixmedia/api/MediaRequestData.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.api import io.element.android.libraries.matrix.api.media.MediaSource diff --git a/libraries/matrixmedia/impl/build.gradle.kts b/libraries/matrixmedia/impl/build.gradle.kts index 62799b5eaa..fc550c189e 100644 --- a/libraries/matrixmedia/impl/build.gradle.kts +++ b/libraries/matrixmedia/impl/build.gradle.kts @@ -13,7 +13,7 @@ plugins { } android { - namespace = "io.element.android.libraries.matrix.ui.media.impl" + namespace = "io.element.android.libraries.matrixmedia.impl" } setupDependencyInjection() diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataExt.kt similarity index 73% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataExt.kt index 0b1a09bdf9..1310f0f750 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataExt.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataExt.kt @@ -5,10 +5,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrixmedia.api.AVATAR_THUMBNAIL_SIZE_IN_PIXEL +import io.element.android.libraries.matrixmedia.api.MediaRequestData internal fun AvatarData.toMediaRequestData(): MediaRequestData { return MediaRequestData( diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactory.kt similarity index 96% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactory.kt index 9d7767e42d..264be31e6e 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactory.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactory.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.ImageLoader import coil3.fetch.Fetcher diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/CoilMediaFetcher.kt similarity index 97% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/CoilMediaFetcher.kt index d31b5671eb..cd9073bb51 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/CoilMediaFetcher.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/CoilMediaFetcher.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.decode.DataSource import coil3.decode.ImageSource @@ -17,6 +17,7 @@ import io.element.android.libraries.matrix.api.exception.isNetworkError import io.element.android.libraries.matrix.api.media.MatrixMediaLoader import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.toFile +import io.element.android.libraries.matrixmedia.api.MediaRequestData import okio.Buffer import okio.FileSystem import okio.Path.Companion.toOkioPath diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolder.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolder.kt similarity index 93% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolder.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolder.kt index a0957a3b13..0aec82dcbd 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolder.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolder.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.ImageLoader import dev.zacsweers.metro.AppScope @@ -14,6 +14,7 @@ import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.SingleIn import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionObserver diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultInitialsAvatarBitmapGenerator.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultInitialsAvatarBitmapGenerator.kt similarity index 97% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultInitialsAvatarBitmapGenerator.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultInitialsAvatarBitmapGenerator.kt index 71c894f305..0baa317c5c 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/DefaultInitialsAvatarBitmapGenerator.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultInitialsAvatarBitmapGenerator.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import android.graphics.Canvas import android.graphics.Paint @@ -36,6 +36,7 @@ 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.Text +import io.element.android.libraries.matrixmedia.api.InitialsAvatarBitmapGenerator @ContributesBinding(AppScope::class) class DefaultInitialsAvatarBitmapGenerator : InitialsAvatarBitmapGenerator { diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/ImageLoaderFactories.kt similarity index 97% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/ImageLoaderFactories.kt index b414560fc9..a37a5bcf53 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/ImageLoaderFactories.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import android.content.Context import android.os.Build diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataFetcherFactory.kt similarity index 86% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataFetcherFactory.kt index f0cfd2047b..32c196afe7 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataFetcherFactory.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataFetcherFactory.kt @@ -6,12 +6,13 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.ImageLoader import coil3.fetch.Fetcher import coil3.request.Options import io.element.android.libraries.matrix.api.media.MatrixMediaLoader +import io.element.android.libraries.matrixmedia.api.MediaRequestData internal class MediaRequestDataFetcherFactory( private val matrixMediaLoader: MatrixMediaLoader, diff --git a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataKeyer.kt similarity index 87% rename from libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt rename to libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataKeyer.kt index 660b80500a..c8a654d634 100644 --- a/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrix/ui/media/MediaRequestDataKeyer.kt +++ b/libraries/matrixmedia/impl/src/main/kotlin/io/element/android/libraries/matrixmedia/impl/MediaRequestDataKeyer.kt @@ -6,11 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.key.Keyer import coil3.request.Options import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.matrixmedia.api.MediaRequestData internal class AvatarDataKeyer : Keyer { override fun key(data: AvatarData, options: Options): String? { diff --git a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactoryTest.kt b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactoryTest.kt similarity index 98% rename from libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactoryTest.kt rename to libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactoryTest.kt index eefacb3065..d6989aa72a 100644 --- a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/AvatarDataFetcherFactoryTest.kt +++ b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/AvatarDataFetcherFactoryTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import android.graphics.Bitmap import coil3.ComponentRegistry diff --git a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolderTest.kt similarity index 98% rename from libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt rename to libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolderTest.kt index 2bb1ddcae4..b84fa9153c 100644 --- a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/DefaultImageLoaderHolderTest.kt +++ b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/DefaultImageLoaderHolderTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import androidx.test.platform.app.InstrumentationRegistry import coil3.ImageLoader diff --git a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeImageLoaderFactory.kt b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/FakeImageLoaderFactory.kt similarity index 93% rename from libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeImageLoaderFactory.kt rename to libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/FakeImageLoaderFactory.kt index bcafad0737..506db985d0 100644 --- a/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrix/ui/media/FakeImageLoaderFactory.kt +++ b/libraries/matrixmedia/impl/src/test/kotlin/io/element/android/libraries/matrixmedia/impl/FakeImageLoaderFactory.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media +package io.element.android.libraries.matrixmedia.impl import coil3.ImageLoader import io.element.android.libraries.matrix.api.media.MatrixMediaLoader diff --git a/libraries/matrixmedia/test/build.gradle.kts b/libraries/matrixmedia/test/build.gradle.kts index 5b8e966d08..86aac509c9 100644 --- a/libraries/matrixmedia/test/build.gradle.kts +++ b/libraries/matrixmedia/test/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } android { - namespace = "io.element.android.libraries.matrix.ui.media.test" + namespace = "io.element.android.libraries.matrixmedia.test" } dependencies { diff --git a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoader.kt b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoader.kt similarity index 96% rename from libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoader.kt rename to libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoader.kt index d3cd7388e5..d35d29c4f8 100644 --- a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoader.kt +++ b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoader.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media.test +package io.element.android.libraries.matrixmedia.test import coil3.ComponentRegistry import coil3.ImageLoader diff --git a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoaderHolder.kt b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoaderHolder.kt similarity index 85% rename from libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoaderHolder.kt rename to libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoaderHolder.kt index 59b9efda7d..8adb434438 100644 --- a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeImageLoaderHolder.kt +++ b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeImageLoaderHolder.kt @@ -6,12 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media.test +package io.element.android.libraries.matrixmedia.test import coil3.ImageLoader import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder class FakeImageLoaderHolder( val fakeImageLoader: ImageLoader = FakeImageLoader(), diff --git a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeInitialsAvatarBitmapGenerator.kt b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeInitialsAvatarBitmapGenerator.kt similarity index 87% rename from libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeInitialsAvatarBitmapGenerator.kt rename to libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeInitialsAvatarBitmapGenerator.kt index 2d0df756ac..83759c4690 100644 --- a/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrix/ui/media/test/FakeInitialsAvatarBitmapGenerator.kt +++ b/libraries/matrixmedia/test/src/main/kotlin/io/element/android/libraries/matrixmedia/test/FakeInitialsAvatarBitmapGenerator.kt @@ -5,11 +5,11 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.matrix.ui.media.test +package io.element.android.libraries.matrixmedia.test import coil3.Bitmap import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.ui.media.InitialsAvatarBitmapGenerator +import io.element.android.libraries.matrixmedia.api.InitialsAvatarBitmapGenerator import io.element.android.tests.testutils.lambda.lambdaError class FakeInitialsAvatarBitmapGenerator( 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 38c5732875..1b8da50426 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 @@ -28,7 +28,7 @@ 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.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.MediaRequestData import kotlinx.parcelize.Parcelize @Composable diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt index 824e032dde..6b3b6a30ea 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/details/MediaDeleteConfirmationBottomSheet.kt @@ -44,7 +44,7 @@ import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.core.EventId -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.mediaviewer.impl.R import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.Strings diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/model/MediaItem.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/model/MediaItem.kt index 5367751a19..95ebb6c818 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/model/MediaItem.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/model/MediaItem.kt @@ -12,9 +12,9 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.timeline.Timeline -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationState import io.element.android.libraries.matrix.ui.media.contentvalidation.ContentValidationValue +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.mediaviewer.api.MediaInfo sealed interface MediaItem { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index e921f1d80c..aa4c0c92a6 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -94,8 +94,8 @@ import io.element.android.libraries.designsystem.utils.hasCompactHeightWindowSiz 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.media.MediaSource -import io.element.android.libraries.matrix.ui.media.MediaRequestData import io.element.android.libraries.matrix.ui.media.contentvalidation.collectMediaState +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.mediaviewer.api.MediaInfo import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.impl.details.MediaBottomSheetState diff --git a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt index 9dd53ade09..16226813d2 100644 --- a/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationBitmapLoader.kt @@ -12,7 +12,7 @@ import android.graphics.Bitmap import androidx.core.graphics.drawable.IconCompat import coil3.ImageLoader import io.element.android.libraries.designsystem.components.avatar.AvatarData -import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL +import io.element.android.libraries.matrixmedia.api.AVATAR_THUMBNAIL_SIZE_IN_PIXEL /** * Loads the avatars used to decorate notifications, since the notification APIs need bitmaps rather than Compose images. diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationBitmapLoader.kt index 086914ecf5..e891fa3ea6 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationBitmapLoader.kt @@ -23,9 +23,9 @@ import dev.zacsweers.metro.ContributesBinding import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.media.MediaSource -import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL -import io.element.android.libraries.matrix.ui.media.InitialsAvatarBitmapGenerator -import io.element.android.libraries.matrix.ui.media.MediaRequestData +import io.element.android.libraries.matrixmedia.api.AVATAR_THUMBNAIL_SIZE_IN_PIXEL +import io.element.android.libraries.matrixmedia.api.InitialsAvatarBitmapGenerator +import io.element.android.libraries.matrixmedia.api.MediaRequestData import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider import timber.log.Timber diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt index 82ae7ba233..12109381df 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManager.kt @@ -20,7 +20,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.user.MatrixUser -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt index 7b08958cd1..583a10f6fb 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationService.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.di.annotations.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder +import io.element.android.libraries.matrixmedia.api.ImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService import io.element.android.libraries.push.impl.intent.IntentProvider diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt index 408063712a..17ab5b3967 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultBaseRoomGroupMessageCreatorTest.kt @@ -19,10 +19,10 @@ import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_TIMESTAMP import io.element.android.libraries.matrix.ui.components.aMatrixUser -import io.element.android.libraries.matrix.ui.media.AVATAR_THUMBNAIL_SIZE_IN_PIXEL -import io.element.android.libraries.matrix.ui.media.MediaRequestData -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoader -import io.element.android.libraries.matrix.ui.media.test.FakeInitialsAvatarBitmapGenerator +import io.element.android.libraries.matrixmedia.api.AVATAR_THUMBNAIL_SIZE_IN_PIXEL +import io.element.android.libraries.matrixmedia.api.MediaRequestData +import io.element.android.libraries.matrixmedia.test.FakeImageLoader +import io.element.android.libraries.matrixmedia.test.FakeInitialsAvatarBitmapGenerator import io.element.android.libraries.push.impl.notifications.factories.MARK_AS_READ_ACTION_TITLE import io.element.android.libraries.push.impl.notifications.factories.QUICK_REPLY_ACTION_TITLE import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt index 3a655d2cce..ce7690ed0a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotificationDrawerManagerTest.kt @@ -32,7 +32,7 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.ui.components.aMatrixUser -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder +import io.element.android.libraries.matrixmedia.test.FakeImageLoaderHolder import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.factories.NotificationAccountParams import io.element.android.libraries.push.impl.notifications.factories.NotificationCreator diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt index 1a46546db8..46845fd55b 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactoryTest.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoader +import io.element.android.libraries.matrixmedia.test.FakeImageLoader import io.element.android.libraries.push.impl.notifications.factories.aNotificationAccountParams import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 43991444fe..4a1970b4f2 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoader +import io.element.android.libraries.matrixmedia.test.FakeImageLoader import io.element.android.libraries.push.api.notifications.NotificationIdProvider import io.element.android.libraries.push.impl.notifications.fake.FakeActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationCreator diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt index cb5fad7509..0296836ccb 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/conversations/DefaultNotificationConversationServiceTest.kt @@ -22,7 +22,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID_2 import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_SESSION_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClientProvider -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoaderHolder +import io.element.android.libraries.matrixmedia.test.FakeImageLoaderHolder import io.element.android.libraries.push.impl.notifications.factories.FakeIntentProvider import io.element.android.libraries.push.impl.notifications.shortcut.createShortcutId import io.element.android.libraries.push.test.notifications.push.FakeNotificationBitmapLoader diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt index ead16c02d7..513db1ef0c 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt @@ -27,8 +27,8 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THREAD_ID import io.element.android.libraries.matrix.test.core.aBuildMeta import io.element.android.libraries.matrix.ui.components.aMatrixUser -import io.element.android.libraries.matrix.ui.media.test.FakeImageLoader -import io.element.android.libraries.matrix.ui.media.test.FakeInitialsAvatarBitmapGenerator +import io.element.android.libraries.matrixmedia.test.FakeImageLoader +import io.element.android.libraries.matrixmedia.test.FakeInitialsAvatarBitmapGenerator import io.element.android.libraries.push.api.notifications.NotificationBitmapLoader import io.element.android.libraries.push.impl.notifications.DefaultNotificationBitmapLoader import io.element.android.libraries.push.impl.notifications.NotificationActionIds diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Day_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Day_0_en.png rename to tests/uitests/src/test/snapshots/images/libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Day_0_en.png diff --git a/tests/uitests/src/test/snapshots/images/libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en.png b/tests/uitests/src/test/snapshots/images/libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Night_0_en.png similarity index 100% rename from tests/uitests/src/test/snapshots/images/libraries.matrix.ui.media_InitialsAvatarBitmapGenerator_Night_0_en.png rename to tests/uitests/src/test/snapshots/images/libraries.matrixmedia.impl_InitialsAvatarBitmapGenerator_Night_0_en.png From a88a5cf698718e0083111282f349905046e7a88d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:52:44 +0200 Subject: [PATCH 56/92] Fix `pushstore/impl` namespace The namespace was `libraries.push.pushstore.impl`, while the sources are in `libraries.pushstore.impl`. Also update the `adb uninstall` hint of the instrumentation test accordingly. Co-Authored-By: Claude Opus 5.5 (1M context) --- libraries/pushstore/impl/build.gradle.kts | 2 +- .../libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/pushstore/impl/build.gradle.kts b/libraries/pushstore/impl/build.gradle.kts index 4e643fd5f9..8691bd2196 100644 --- a/libraries/pushstore/impl/build.gradle.kts +++ b/libraries/pushstore/impl/build.gradle.kts @@ -13,7 +13,7 @@ plugins { } android { - namespace = "io.element.android.libraries.push.pushstore.impl" + namespace = "io.element.android.libraries.pushstore.impl" defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt index a3b31acf6c..d06bd43582 100644 --- a/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt +++ b/libraries/pushstore/impl/src/androidTest/kotlin/io/element/android/libraries/pushstore/impl/DefaultUserPushStoreFactoryTest.kt @@ -18,7 +18,7 @@ import kotlin.concurrent.thread /** * Note: to clear the emulator, invoke: - * adb uninstall io.element.android.libraries.push.pushstore.impl.test + * adb uninstall io.element.android.libraries.pushstore.impl.test */ class DefaultUserPushStoreFactoryTest { /** From de6f8204010b1ef564c8f634e9d998e6ce4fabc0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:52:52 +0200 Subject: [PATCH 57/92] Fix `usersearch/test` namespace The namespace was `libraries.usersearch`, without the `.test` suffix, while the sources are in `libraries.usersearch.test`. Co-Authored-By: Claude Opus 5.5 (1M context) --- libraries/usersearch/test/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/usersearch/test/build.gradle.kts b/libraries/usersearch/test/build.gradle.kts index 21cc140a7d..7c986fdf96 100644 --- a/libraries/usersearch/test/build.gradle.kts +++ b/libraries/usersearch/test/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } android { - namespace = "io.element.android.libraries.usersearch" + namespace = "io.element.android.libraries.usersearch.test" } dependencies { From abad21218d28bb3130504a99b34c0e0ed1ae5e93 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:53:39 +0200 Subject: [PATCH 58/92] Move `call/impl` tests to the `features.call.impl` package Some test sources were in `features.call`, `features.call.ui`, `features.call.utils` and `features.call.notifications`, while the code they test is in the corresponding `features.call.impl.*` packages. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../{ => impl}/DefaultElementCallEntryPointTest.kt | 7 +++---- .../call/{ => impl}/MapWebkitPermissionsTest.kt | 2 +- .../RingingCallNotificationCreatorTest.kt | 3 +-- .../features/call/{ => impl}/ui/CallDataTest.kt | 2 +- .../{ => impl}/ui/CallScreenBackPressPolicyTest.kt | 4 +--- .../call/{ => impl}/ui/CallScreenPresenterTest.kt | 11 ++++------- .../features/call/{ => impl}/ui/CallScreenViewTest.kt | 7 +------ .../call/{ => impl}/ui/FakeCallScreenNavigator.kt | 4 +--- .../call/{ => impl}/ui/FakeLanguageTagProvider.kt | 3 +-- .../{ => impl}/utils/DefaultActiveCallManagerTest.kt | 6 +----- .../{ => impl}/utils/DefaultCallWidgetProviderTest.kt | 3 +-- .../call/{ => impl}/utils/FakeActiveCallManager.kt | 4 +--- .../call/{ => impl}/utils/FakeCallWidgetProvider.kt | 3 +-- .../call/{ => impl}/utils/FakeNativeCallEntryPoint.kt | 2 +- .../{ => impl}/utils/FakeWidgetMessageInterceptor.kt | 3 +-- 15 files changed, 20 insertions(+), 44 deletions(-) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/DefaultElementCallEntryPointTest.kt (95%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/MapWebkitPermissionsTest.kt (96%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/notifications/RingingCallNotificationCreatorTest.kt (97%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/CallDataTest.kt (93%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/CallScreenBackPressPolicyTest.kt (93%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/CallScreenPresenterTest.kt (96%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/CallScreenViewTest.kt (93%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/FakeCallScreenNavigator.kt (78%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/ui/FakeLanguageTagProvider.kt (79%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/DefaultActiveCallManagerTest.kt (98%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/DefaultCallWidgetProviderTest.kt (98%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/FakeActiveCallManager.kt (88%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/FakeCallWidgetProvider.kt (90%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/FakeNativeCallEntryPoint.kt (90%) rename features/call/impl/src/test/kotlin/io/element/android/features/call/{ => impl}/utils/FakeWidgetMessageInterceptor.kt (84%) diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/DefaultElementCallEntryPointTest.kt similarity index 95% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/DefaultElementCallEntryPointTest.kt index 82c28209f7..76527b1165 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/DefaultElementCallEntryPointTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/DefaultElementCallEntryPointTest.kt @@ -6,17 +6,16 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call +package io.element.android.features.call.impl import android.content.Intent import androidx.test.platform.app.InstrumentationRegistry import com.google.common.truth.Truth.assertThat import io.element.android.features.call.api.CallData -import io.element.android.features.call.impl.DefaultElementCallEntryPoint import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.ui.ElementCallActivity -import io.element.android.features.call.utils.FakeActiveCallManager -import io.element.android.features.call.utils.FakeNativeCallEntryPoint +import io.element.android.features.call.impl.utils.FakeActiveCallManager +import io.element.android.features.call.impl.utils.FakeNativeCallEntryPoint import io.element.android.features.callnative.api.NativeCallEntryPoint import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/MapWebkitPermissionsTest.kt similarity index 96% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/MapWebkitPermissionsTest.kt index e356358437..da76f088ef 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/MapWebkitPermissionsTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/MapWebkitPermissionsTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call +package io.element.android.features.call.impl import android.Manifest import android.webkit.PermissionRequest diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreatorTest.kt similarity index 97% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreatorTest.kt index ffdd51e69a..7b37c5dcd6 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/notifications/RingingCallNotificationCreatorTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/notifications/RingingCallNotificationCreatorTest.kt @@ -6,13 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.notifications +package io.element.android.features.call.impl.notifications import androidx.core.graphics.drawable.IconCompat import androidx.test.platform.app.InstrumentationRegistry import coil3.ImageLoader import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.features.call.impl.ui.IncomingCallActivity import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.matrix.test.AN_EVENT_ID diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallDataTest.kt similarity index 93% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallDataTest.kt index f0cdd44082..3193a73343 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallDataTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallDataTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui +package io.element.android.features.call.impl.ui import com.google.common.truth.Truth.assertThat import io.element.android.features.call.api.CallData diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicyTest.kt similarity index 93% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicyTest.kt index f07f7039d3..c75d14a599 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenBackPressPolicyTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenBackPressPolicyTest.kt @@ -5,11 +5,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui +package io.element.android.features.call.impl.ui import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.impl.ui.CallScreenBackPressAction -import io.element.android.features.call.impl.ui.CallScreenBackPressPolicy import org.junit.Test class CallScreenBackPressPolicyTest { diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenterTest.kt similarity index 96% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenterTest.kt index 276e6670f1..2889869565 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenPresenterTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenPresenterTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui +package io.element.android.features.call.impl.ui import app.cash.molecule.RecompositionMode import app.cash.molecule.moleculeFlow @@ -14,13 +14,10 @@ import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.features.call.api.CallData -import io.element.android.features.call.impl.ui.CallScreenEvent -import io.element.android.features.call.impl.ui.CallScreenNavigator -import io.element.android.features.call.impl.ui.CallScreenPresenter +import io.element.android.features.call.impl.utils.FakeActiveCallManager +import io.element.android.features.call.impl.utils.FakeCallWidgetProvider +import io.element.android.features.call.impl.utils.FakeWidgetMessageInterceptor import io.element.android.features.call.impl.utils.WidgetMessageSerializer -import io.element.android.features.call.utils.FakeActiveCallManager -import io.element.android.features.call.utils.FakeCallWidgetProvider -import io.element.android.features.call.utils.FakeWidgetMessageInterceptor import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.core.coroutine.CoroutineDispatchers diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenViewTest.kt similarity index 93% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenViewTest.kt index 76ce8e8ede..4107a0826a 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/CallScreenViewTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/CallScreenViewTest.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui +package io.element.android.features.call.impl.ui import android.view.KeyEvent import android.webkit.WebView @@ -18,11 +18,6 @@ import androidx.compose.ui.test.v2.runAndroidComposeUiTest import io.element.android.features.call.impl.pip.PictureInPictureEvent import io.element.android.features.call.impl.pip.PictureInPictureState import io.element.android.features.call.impl.pip.aPictureInPictureState -import io.element.android.features.call.impl.ui.CallScreenEvent -import io.element.android.features.call.impl.ui.CallScreenState -import io.element.android.features.call.impl.ui.CallScreenView -import io.element.android.features.call.impl.ui.JavascriptBackHandlerBridge -import io.element.android.features.call.impl.ui.aCallScreenState import io.element.android.tests.testutils.EventsRecorder import io.element.android.tests.testutils.pressBackKey import io.element.android.tests.testutils.robolectric.RobolectricTest diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeCallScreenNavigator.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeCallScreenNavigator.kt similarity index 78% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeCallScreenNavigator.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeCallScreenNavigator.kt index 431b746362..f4a0b7e16a 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeCallScreenNavigator.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeCallScreenNavigator.kt @@ -6,9 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui - -import io.element.android.features.call.impl.ui.CallScreenNavigator +package io.element.android.features.call.impl.ui class FakeCallScreenNavigator : CallScreenNavigator { var closeCalled = false diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeLanguageTagProvider.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeLanguageTagProvider.kt similarity index 79% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeLanguageTagProvider.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeLanguageTagProvider.kt index f68842813b..4329174636 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/ui/FakeLanguageTagProvider.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/ui/FakeLanguageTagProvider.kt @@ -6,10 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.ui +package io.element.android.features.call.impl.ui import androidx.compose.runtime.Composable -import io.element.android.features.call.impl.ui.LanguageTagProvider class FakeLanguageTagProvider(private val languageTag: String?) : LanguageTagProvider { @Composable diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultActiveCallManagerTest.kt similarity index 98% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultActiveCallManagerTest.kt index 0e99b59c69..6820985825 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultActiveCallManagerTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultActiveCallManagerTest.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils import android.os.PowerManager import androidx.core.app.NotificationManagerCompat @@ -16,10 +16,6 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator import io.element.android.features.call.impl.notifications.aCallNotificationData -import io.element.android.features.call.impl.utils.ActiveCall -import io.element.android.features.call.impl.utils.CallState -import io.element.android.features.call.impl.utils.DefaultActiveCallManager -import io.element.android.features.call.impl.utils.DefaultCurrentCallService 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.core.SessionId diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProviderTest.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProviderTest.kt similarity index 98% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProviderTest.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProviderTest.kt index 093abfcd32..69d95ac12c 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/DefaultCallWidgetProviderTest.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProviderTest.kt @@ -6,10 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils import com.google.common.truth.Truth.assertThat -import io.element.android.features.call.impl.utils.DefaultCallWidgetProvider import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.widget.CallWidgetSettingsProvider import io.element.android.libraries.matrix.test.A_ROOM_ID diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeActiveCallManager.kt similarity index 88% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeActiveCallManager.kt index c2c38284a9..96fb0cb5cb 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeActiveCallManager.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeActiveCallManager.kt @@ -6,12 +6,10 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils import io.element.android.features.call.api.CallData import io.element.android.features.call.impl.notifications.CallNotificationData -import io.element.android.features.call.impl.utils.ActiveCall -import io.element.android.features.call.impl.utils.ActiveCallManager import io.element.android.tests.testutils.simulateLongTask import kotlinx.coroutines.flow.MutableStateFlow diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeCallWidgetProvider.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeCallWidgetProvider.kt similarity index 90% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeCallWidgetProvider.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeCallWidgetProvider.kt index ebc00b0e8f..98789c1d71 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeCallWidgetProvider.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeCallWidgetProvider.kt @@ -6,9 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils -import io.element.android.features.call.impl.utils.CallWidgetProvider import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeNativeCallEntryPoint.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeNativeCallEntryPoint.kt similarity index 90% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeNativeCallEntryPoint.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeNativeCallEntryPoint.kt index 742ba55bdc..dcabb72507 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeNativeCallEntryPoint.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeNativeCallEntryPoint.kt @@ -5,7 +5,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils import io.element.android.features.call.api.CallData import io.element.android.features.callnative.api.NativeCallEntryPoint diff --git a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeWidgetMessageInterceptor.kt b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeWidgetMessageInterceptor.kt similarity index 84% rename from features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeWidgetMessageInterceptor.kt rename to features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeWidgetMessageInterceptor.kt index 6b68b1519c..8fff92faf3 100644 --- a/features/call/impl/src/test/kotlin/io/element/android/features/call/utils/FakeWidgetMessageInterceptor.kt +++ b/features/call/impl/src/test/kotlin/io/element/android/features/call/impl/utils/FakeWidgetMessageInterceptor.kt @@ -6,9 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.call.utils +package io.element.android.features.call.impl.utils -import io.element.android.features.call.impl.utils.WidgetMessageInterceptor import kotlinx.coroutines.flow.MutableSharedFlow class FakeWidgetMessageInterceptor : WidgetMessageInterceptor { From 0cf1d82281e91c0671dd8d0c069644fe6154f99e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:54:16 +0200 Subject: [PATCH 59/92] Move `StartChatNavigator` to the `features.startchat.impl` package It was the only file of the `startchat/impl` module in the `features.startchat` package. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../android/features/startchat/impl/StartChatFlowNode.kt | 1 - .../android/features/startchat/{ => impl}/StartChatNavigator.kt | 2 +- .../startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt | 2 +- .../startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt | 2 +- .../android/features/startchat/impl/root/StartChatNode.kt | 2 +- .../android/features/startchat/impl/FakeStartChatNavigator.kt | 1 - .../impl/joinbyaddress/JoinBaseRoomByAddressPresenterTest.kt | 2 +- 7 files changed, 5 insertions(+), 7 deletions(-) rename features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/{ => impl}/StartChatNavigator.kt (97%) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt index 9cb8d48778..4f19f0e573 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatFlowNode.kt @@ -22,7 +22,6 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode import io.element.android.features.createroom.api.CreateRoomEntryPoint -import io.element.android.features.startchat.DefaultStartChatNavigator import io.element.android.features.startchat.api.StartChatEntryPoint import io.element.android.features.startchat.impl.joinbyaddress.JoinRoomByAddressNode import io.element.android.features.startchat.impl.root.StartChatNode diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatNavigator.kt similarity index 97% rename from features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt rename to features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatNavigator.kt index 125dc2c2b3..2bd2ac2f1b 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/StartChatNavigator.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/StartChatNavigator.kt @@ -6,7 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.startchat +package io.element.android.features.startchat.impl import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.navmodel.backstack.BackStack diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt index c81cb1af66..acfe51ff92 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressNode.kt @@ -17,7 +17,7 @@ import com.bumble.appyx.core.plugin.plugins import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import io.element.android.annotations.ContributesNode -import io.element.android.features.startchat.StartChatNavigator +import io.element.android.features.startchat.impl.StartChatNavigator import io.element.android.libraries.di.SessionScope @ContributesNode(SessionScope::class) diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt index 533f680674..be8b96c21a 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinRoomByAddressPresenter.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.setValue import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedFactory import dev.zacsweers.metro.AssistedInject -import io.element.android.features.startchat.StartChatNavigator +import io.element.android.features.startchat.impl.StartChatNavigator import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.matrix.api.MatrixClient diff --git a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt index 60ef9ee00f..69d82ad37c 100644 --- a/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt +++ b/features/startchat/impl/src/main/kotlin/io/element/android/features/startchat/impl/root/StartChatNode.kt @@ -21,7 +21,7 @@ import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.AssistedInject import im.vector.app.features.analytics.plan.MobileScreen import io.element.android.annotations.ContributesNode -import io.element.android.features.startchat.StartChatNavigator +import io.element.android.features.startchat.impl.StartChatNavigator import io.element.android.libraries.deeplink.api.usecase.InviteFriendsUseCase import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt index ba1b6a4e69..4b7b289490 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/FakeStartChatNavigator.kt @@ -8,7 +8,6 @@ package io.element.android.features.startchat.impl -import io.element.android.features.startchat.StartChatNavigator import io.element.android.libraries.matrix.api.core.RoomIdOrAlias class FakeStartChatNavigator( diff --git a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressPresenterTest.kt b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressPresenterTest.kt index 393f51dd2d..924ca691b7 100644 --- a/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressPresenterTest.kt +++ b/features/startchat/impl/src/test/kotlin/io/element/android/features/startchat/impl/joinbyaddress/JoinBaseRoomByAddressPresenterTest.kt @@ -9,8 +9,8 @@ package io.element.android.features.startchat.impl.joinbyaddress import com.google.common.truth.Truth.assertThat -import io.element.android.features.startchat.StartChatNavigator import io.element.android.features.startchat.impl.FakeStartChatNavigator +import io.element.android.features.startchat.impl.StartChatNavigator import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.alias.RoomAliasHelper From bbaa83c8b8b0dc2979e6c943053530fedec77e8f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:54:33 +0200 Subject: [PATCH 60/92] Move `PickerTypeTest` to the `libraries.mediapickers.api` package The test was in `libraries.mediapickers`, while `PickerType` is in `libraries.mediapickers.api`. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../android/libraries/mediapickers/{ => api}/PickerTypeTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/{ => api}/PickerTypeTest.kt (94%) diff --git a/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt b/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/api/PickerTypeTest.kt similarity index 94% rename from libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt rename to libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/api/PickerTypeTest.kt index 752398f7c4..bdbf517886 100644 --- a/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/PickerTypeTest.kt +++ b/libraries/mediapickers/api/src/test/kotlin/io/element/android/libraries/mediapickers/api/PickerTypeTest.kt @@ -6,13 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.mediapickers +package io.element.android.libraries.mediapickers.api import android.net.Uri import androidx.activity.result.contract.ActivityResultContracts import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.mimetype.MimeTypes -import io.element.android.libraries.mediapickers.api.PickerType import io.element.android.tests.testutils.robolectric.RobolectricTest import org.junit.Test From 845a1935008f0552dcc61a9b40a5e7708923a8ee Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:56:41 +0200 Subject: [PATCH 61/92] Move `viewfolder/impl` tests to the `features.viewfolder.impl` package The tests and fakes were in `features.viewfolder.test.*`, which looks like a package of a test module, while they are in the `impl` module and test code from `features.viewfolder.impl.*`. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../{test => impl}/file/FakeFileContentReader.kt | 4 +--- .../viewfolder/{test => impl}/file/FakeFileSave.kt | 4 +--- .../viewfolder/{test => impl}/file/FakeFileShare.kt | 4 +--- .../{test => impl}/file/ViewFilePresenterTest.kt | 8 +------- .../{test => impl}/folder/FakeFolderExplorer.kt | 3 +-- .../{test => impl}/folder/ViewFolderPresenterTest.kt | 4 +--- 6 files changed, 6 insertions(+), 21 deletions(-) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/file/FakeFileContentReader.kt (80%) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/file/FakeFileSave.kt (76%) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/file/FakeFileShare.kt (76%) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/file/ViewFilePresenterTest.kt (93%) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/folder/FakeFolderExplorer.kt (80%) rename features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/{test => impl}/folder/ViewFolderPresenterTest.kt (94%) diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileContentReader.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileContentReader.kt similarity index 80% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileContentReader.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileContentReader.kt index 6dd621ac6a..9e5b158b5d 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileContentReader.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileContentReader.kt @@ -6,9 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.file - -import io.element.android.features.viewfolder.impl.file.FileContentReader +package io.element.android.features.viewfolder.impl.file class FakeFileContentReader : FileContentReader { private var result: Result> = Result.success(emptyList()) diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileSave.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileSave.kt similarity index 76% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileSave.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileSave.kt index 3649d30759..87af5947bb 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileSave.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileSave.kt @@ -6,9 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.file - -import io.element.android.features.viewfolder.impl.file.FileSave +package io.element.android.features.viewfolder.impl.file class FakeFileSave : FileSave { var hasBeenCalled = false diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileShare.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileShare.kt similarity index 76% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileShare.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileShare.kt index 7348e4bed0..3b3a4a7245 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/FakeFileShare.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/FakeFileShare.kt @@ -6,9 +6,7 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.file - -import io.element.android.features.viewfolder.impl.file.FileShare +package io.element.android.features.viewfolder.impl.file class FakeFileShare : FileShare { var hasBeenCalled = false diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenterTest.kt similarity index 93% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenterTest.kt index 7e47d0064d..e61dfebad2 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/file/ViewFilePresenterTest.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/file/ViewFilePresenterTest.kt @@ -6,18 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.file +package io.element.android.features.viewfolder.impl.file 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.viewfolder.impl.file.ColorationMode -import io.element.android.features.viewfolder.impl.file.FileContentReader -import io.element.android.features.viewfolder.impl.file.FileSave -import io.element.android.features.viewfolder.impl.file.FileShare -import io.element.android.features.viewfolder.impl.file.ViewFileEvent -import io.element.android.features.viewfolder.impl.file.ViewFilePresenter import io.element.android.libraries.androidutils.clipboard.ClipboardHelper import io.element.android.libraries.androidutils.clipboard.FakeClipboardHelper import io.element.android.libraries.androidutils.toast.FakeToastHelper diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/FakeFolderExplorer.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/FakeFolderExplorer.kt similarity index 80% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/FakeFolderExplorer.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/FakeFolderExplorer.kt index 7761bfb976..fd28fc8040 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/FakeFolderExplorer.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/FakeFolderExplorer.kt @@ -6,9 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.folder +package io.element.android.features.viewfolder.impl.folder -import io.element.android.features.viewfolder.impl.folder.FolderExplorer import io.element.android.features.viewfolder.impl.model.Item class FakeFolderExplorer : FolderExplorer { diff --git a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/ViewFolderPresenterTest.kt b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenterTest.kt similarity index 94% rename from features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/ViewFolderPresenterTest.kt rename to features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenterTest.kt index 31fd91eabe..1e8f90976f 100644 --- a/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/test/folder/ViewFolderPresenterTest.kt +++ b/features/viewfolder/impl/src/test/kotlin/io/element/android/features/viewfolder/impl/folder/ViewFolderPresenterTest.kt @@ -6,14 +6,12 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.features.viewfolder.test.folder +package io.element.android.features.viewfolder.impl.folder 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.viewfolder.impl.folder.FolderExplorer -import io.element.android.features.viewfolder.impl.folder.ViewFolderPresenter import io.element.android.features.viewfolder.impl.model.Item import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.test.core.aBuildMeta From c64aa67e56e00cf1dc6ac2358e6168c9916320c5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 5 Oct 2026 13:56:55 +0200 Subject: [PATCH 62/92] Move `voicerecorder/impl` test fakes next to the interfaces they fake The fakes were in `libraries.voicerecorder.test`, the package of the `voicerecorder/test` module. Move them to `libraries.voicerecorder.impl.audio` and `libraries.voicerecorder.impl.file`. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../voicerecorder/impl/DefaultVoiceRecorderTest.kt | 10 +++++----- .../{test => impl/audio}/FakeAudioLevelCalculator.kt | 3 +-- .../{test => impl/audio}/FakeAudioReader.kt | 4 +--- .../{test => impl/audio}/FakeAudioReaderFactory.kt | 5 +---- .../voicerecorder/{test => impl/audio}/FakeEncoder.kt | 4 ++-- .../{test => impl/file}/FakeFileSystem.kt | 2 +- .../{test => impl/file}/FakeVoiceFileManager.kt | 4 +--- 7 files changed, 12 insertions(+), 20 deletions(-) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/audio}/FakeAudioLevelCalculator.kt (77%) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/audio}/FakeAudioReader.kt (85%) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/audio}/FakeAudioReaderFactory.kt (70%) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/audio}/FakeEncoder.kt (83%) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/file}/FakeFileSystem.kt (93%) rename libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/{test => impl/file}/FakeVoiceFileManager.kt (77%) diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt index 070b341d76..62f91d3ccf 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/DefaultVoiceRecorderTest.kt @@ -17,13 +17,13 @@ import io.element.android.libraries.core.mimetype.MimeTypes 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 +import io.element.android.libraries.voicerecorder.impl.audio.FakeAudioLevelCalculator +import io.element.android.libraries.voicerecorder.impl.audio.FakeAudioReaderFactory +import io.element.android.libraries.voicerecorder.impl.audio.FakeEncoder import io.element.android.libraries.voicerecorder.impl.audio.SampleRate import io.element.android.libraries.voicerecorder.impl.di.VoiceRecorderBindingContainer -import io.element.android.libraries.voicerecorder.test.FakeAudioLevelCalculator -import io.element.android.libraries.voicerecorder.test.FakeAudioReaderFactory -import io.element.android.libraries.voicerecorder.test.FakeEncoder -import io.element.android.libraries.voicerecorder.test.FakeFileSystem -import io.element.android.libraries.voicerecorder.test.FakeVoiceFileManager +import io.element.android.libraries.voicerecorder.impl.file.FakeFileSystem +import io.element.android.libraries.voicerecorder.impl.file.FakeVoiceFileManager import io.element.android.tests.testutils.testCoroutineDispatchers import io.mockk.mockk import kotlinx.coroutines.test.TestScope diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioLevelCalculator.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioLevelCalculator.kt similarity index 77% rename from libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioLevelCalculator.kt rename to libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioLevelCalculator.kt index 48e12c3192..583d36fdfd 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioLevelCalculator.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioLevelCalculator.kt @@ -6,9 +6,8 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.voicerecorder.test +package io.element.android.libraries.voicerecorder.impl.audio -import io.element.android.libraries.voicerecorder.impl.audio.AudioLevelCalculator import kotlin.math.abs class FakeAudioLevelCalculator : AudioLevelCalculator { diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReader.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReader.kt similarity index 85% rename from libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReader.kt rename to libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReader.kt index b14bb400e5..e946abecd8 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReader.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReader.kt @@ -6,11 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.voicerecorder.test +package io.element.android.libraries.voicerecorder.impl.audio import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.voicerecorder.impl.audio.Audio -import io.element.android.libraries.voicerecorder.impl.audio.AudioReader import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import kotlinx.coroutines.yield diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReaderFactory.kt similarity index 70% rename from libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt rename to libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReaderFactory.kt index d3e434538e..2b5a998a90 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/test/FakeAudioReaderFactory.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/audio/FakeAudioReaderFactory.kt @@ -6,12 +6,9 @@ * Please see LICENSE files in the repository root for full details. */ -package io.element.android.libraries.voicerecorder.test +package io.element.android.libraries.voicerecorder.impl.audio import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.voicerecorder.impl.audio.Audio -import io.element.android.libraries.voicerecorder.impl.audio.AudioConfig -import io.element.android.libraries.voicerecorder.impl.audio.AudioReader class FakeAudioReaderFactory( private val audio: List