diff --git a/changelog.d/360.feature b/changelog.d/360.feature new file mode 100644 index 0000000000..7532c338ba --- /dev/null +++ b/changelog.d/360.feature @@ -0,0 +1 @@ +Add media pickers to the room screen. diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index 5d78791ee4..578c8334da 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -10,9 +10,9 @@ "Public room (anyone)" "Room name" "e.g. Product Sprint" - "Create a room" "Topic (optional)" "What is this room about?" "An error occurred when trying to start a chat" "We can’t validate this user’s Matrix ID. The invite might not be received." + "Create a room" \ No newline at end of file diff --git a/features/logout/api/src/main/res/values/localazy.xml b/features/logout/api/src/main/res/values/localazy.xml index 594475f35c..514c002567 100644 --- a/features/logout/api/src/main/res/values/localazy.xml +++ b/features/logout/api/src/main/res/values/localazy.xml @@ -1,8 +1,8 @@ "Are you sure you want to sign out?" - "Sign out" "Sign out" "Signing out…" + "Sign out" "Sign out" \ No newline at end of file diff --git a/features/messages/impl/build.gradle.kts b/features/messages/impl/build.gradle.kts index 3bd73d5ed2..f53f130bf7 100644 --- a/features/messages/impl/build.gradle.kts +++ b/features/messages/impl/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(projects.anvilannotations) anvil(projects.anvilcodegen) api(projects.features.messages.api) + implementation(projects.libraries.androidutils) implementation(projects.libraries.core) implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) 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 9665907eb2..ebc7f9e829 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 @@ -27,7 +27,6 @@ import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.libraries.di.RoomScope -import io.element.android.libraries.matrix.api.core.RoomId @ContributesNode(RoomScope::class) class MessagesNode @AssistedInject constructor( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt index 619298b4f5..8426f79721 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.features.messages.impl.actionlist.anActionListState +import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker import io.element.android.features.messages.impl.textcomposer.aMessageComposerState import io.element.android.features.messages.impl.timeline.aTimelineItemContent import io.element.android.features.messages.impl.timeline.aTimelineItemList @@ -32,6 +33,8 @@ open class MessagesStateProvider : PreviewParameterProvider { get() = sequenceOf( aMessagesState(), aMessagesState().copy(hasNetworkConnection = false), + aMessagesState().copy(composerState = aMessageComposerState().copy(attachmentSourcePicker = AttachmentSourcePicker.AllMedia)), + aMessagesState().copy(composerState = aMessageComposerState().copy(attachmentSourcePicker = AttachmentSourcePicker.Camera)), ) } 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 132d139174..5bf26d88bc 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 @@ -21,6 +21,7 @@ package io.element.android.features.messages.impl +import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -35,6 +36,7 @@ import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ListItem import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -43,11 +45,14 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -57,20 +62,24 @@ import androidx.compose.ui.unit.sp import io.element.android.features.messages.impl.actionlist.ActionListEvents import io.element.android.features.messages.impl.actionlist.ActionListView import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction +import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker +import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents import io.element.android.features.messages.impl.textcomposer.MessageComposerView import io.element.android.features.messages.impl.timeline.TimelineView import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView +import io.element.android.libraries.androidutils.ui.hideKeyboard import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight 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.ModalBottomSheetLayout import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.LogCompositions -import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorView import kotlinx.coroutines.launch import timber.log.Timber @@ -86,9 +95,24 @@ fun MessagesView( initialValue = ModalBottomSheetValue.Hidden, ) val snackbarHostState = remember { SnackbarHostState() } - val focusManager = LocalFocusManager.current + val composerState = state.composerState + val initialBottomSheetState = if (LocalInspectionMode.current && composerState.attachmentSourcePicker != null) { + ModalBottomSheetValue.Expanded + } else { + ModalBottomSheetValue.Hidden + } + val bottomSheetState = rememberModalBottomSheetState(initialValue = initialBottomSheetState) val coroutineScope = rememberCoroutineScope() + BackHandler(enabled = bottomSheetState.isVisible) { + coroutineScope.launch { + bottomSheetState.hide() + } + } + + // This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose + val localView = LocalView.current + LogCompositions(tag = "MessagesScreen", msg = "Content") fun onMessageClicked(event: TimelineItem.Event) { @@ -97,7 +121,7 @@ fun MessagesView( fun onMessageLongClicked(event: TimelineItem.Event) { Timber.v("OnMessageLongClicked= ${event.id}") - focusManager.clearFocus(force = true) + localView.hideKeyboard() state.actionListState.eventSink(ActionListEvents.ComputeForMessage(event)) coroutineScope.launch { itemActionsBottomSheetState.show() @@ -108,41 +132,67 @@ fun MessagesView( state.eventSink(MessagesEvents.HandleAction(action, event)) } - Scaffold( - modifier = modifier, - contentWindowInsets = WindowInsets.statusBars, - topBar = { - Column { - ConnectivityIndicatorView(isOnline = state.hasNetworkConnection) - MessagesViewTopBar( - roomTitle = state.roomName, - roomAvatar = state.roomAvatar, - onBackPressed = onBackPressed, - onRoomDetailsClicked = onRoomDetailsClicked, + LaunchedEffect(composerState.attachmentSourcePicker) { + if (composerState.attachmentSourcePicker != null) { + // We need to use this instead of `LocalFocusManager.clearFocus()` to hide the keyboard when focus is on an Android View + localView.hideKeyboard() + bottomSheetState.show() + } else { + bottomSheetState.hide() + } + } + // Send 'DismissAttachmentMenu' event when the bottomsheet was just hidden + LaunchedEffect(bottomSheetState.isVisible) { + if (!bottomSheetState.isVisible) { + composerState.eventSink(MessageComposerEvents.DismissAttachmentMenu) + } + } + ModalBottomSheetLayout( + sheetState = bottomSheetState, + displayHandle = true, + sheetContent = { + MediaPickerMenu( + addAttachmentSourcePicker = composerState.attachmentSourcePicker, + eventSink = composerState.eventSink + ) + } + ) { + Scaffold( + modifier = modifier, + contentWindowInsets = WindowInsets.statusBars, + topBar = { + Column { + ConnectivityIndicatorView(isOnline = state.hasNetworkConnection) + MessagesViewTopBar( + roomTitle = state.roomName, + roomAvatar = state.roomAvatar, + onBackPressed = onBackPressed, + onRoomDetailsClicked = onRoomDetailsClicked, + ) + } + }, + content = { padding -> + MessagesViewContent( + state = state, + modifier = Modifier.padding(padding), + onMessageClicked = ::onMessageClicked, + onMessageLongClicked = ::onMessageLongClicked ) - } - }, - content = { padding -> - MessagesViewContent( - state = state, - modifier = Modifier.padding(padding), - onMessageClicked = ::onMessageClicked, - onMessageLongClicked = ::onMessageLongClicked - ) - }, - snackbarHost = { - SnackbarHost( - snackbarHostState, - modifier = Modifier.navigationBarsPadding() - ) - }, - ) + }, + snackbarHost = { + SnackbarHost( + snackbarHostState, + modifier = Modifier.navigationBarsPadding() + ) + }, + ) - ActionListView( - state = state.actionListState, - modalBottomSheetState = itemActionsBottomSheetState, - onActionSelected = ::onActionSelected - ) + ActionListView( + state = state.actionListState, + modalBottomSheetState = itemActionsBottomSheetState, + onActionSelected = ::onActionSelected + ) + } } @Composable @@ -216,6 +266,53 @@ fun MessagesViewTopBar( ) } +@Composable +internal fun MediaPickerMenu( + addAttachmentSourcePicker: AttachmentSourcePicker?, + eventSink: (MessageComposerEvents) -> Unit, +) { + when (addAttachmentSourcePicker) { + null -> return + AttachmentSourcePicker.AllMedia -> AllMediaSourcePickerMenu(eventSink = eventSink) + AttachmentSourcePicker.Camera -> CameraSourcePickerMenu(eventSink = eventSink) + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun AllMediaSourcePickerMenu( + eventSink: (MessageComposerEvents) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) }) { + Text(stringResource(R.string.screen_room_attachment_source_gallery)) + } + ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) }) { + Text(stringResource(R.string.screen_room_attachment_source_files)) + } + ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickAttachmentSource.FromCamera) }) { + Text(stringResource(R.string.screen_room_attachment_source_camera)) + } + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun CameraSourcePickerMenu( + eventSink: (MessageComposerEvents) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo) }) { + Text(stringResource(R.string.screen_room_attachment_source_camera_photo)) + } + ListItem(Modifier.clickable { eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video) }) { + Text(stringResource(R.string.screen_room_attachment_source_camera_video)) + } + } +} + @Preview @Composable internal fun MessagesViewLightPreview(@PreviewParameter(MessagesStateProvider::class) state: MessagesState) = diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerEvents.kt index 610f6e1ec0..b3728d1367 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerEvents.kt @@ -24,6 +24,15 @@ sealed interface MessageComposerEvents { object CloseSpecialMode : MessageComposerEvents data class SetMode(val composerMode: MessageComposerMode) : MessageComposerEvents data class UpdateText(val text: CharSequence) : MessageComposerEvents - - object TakePhoto : MessageComposerEvents + object AddAttachment : MessageComposerEvents + object DismissAttachmentMenu : MessageComposerEvents + sealed interface PickAttachmentSource : MessageComposerEvents { + object FromGallery : PickAttachmentSource + object FromCamera : PickAttachmentSource + object FromFiles : PickAttachmentSource + } + sealed interface PickCameraAttachmentSource : MessageComposerEvents { + object Photo : PickCameraAttachmentSource + object Video : PickCameraAttachmentSource + } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt index 603776e518..8d495aa269 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenter.kt @@ -19,13 +19,17 @@ package io.element.android.features.messages.impl.textcomposer import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.data.StableCharSequence import io.element.android.libraries.core.data.toStableCharSequence +import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.di.SingleIn import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -36,6 +40,7 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +@SingleIn(RoomScope::class) class MessageComposerPresenter @Inject constructor( private val appCoroutineScope: CoroutineScope, private val room: MatrixRoom, @@ -47,11 +52,22 @@ class MessageComposerPresenter @Inject constructor( override fun present(): MessageComposerState { val localCoroutineScope = rememberCoroutineScope() - // Example usage of custom pickers + val galleryMediaPicker = mediaPickerProvider.registerGalleryPicker(onResult = { uri -> + Timber.d("Media picked from $uri") + }) + + val filesPicker = mediaPickerProvider.registerFilePicker(onResult = { uri -> + Timber.d("File picked from $uri") + }) + val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker(onResult = { uri -> Timber.d("Photo saved at $uri") }) + val cameraVideoPicker = mediaPickerProvider.registerCameraVideoPicker(onResult = { uri -> + Timber.d("Video saved at $uri") + }) + val isFullScreen = rememberSaveable { mutableStateOf(false) } @@ -62,6 +78,8 @@ class MessageComposerPresenter @Inject constructor( mutableStateOf(MessageComposerMode.Normal("")) } + var attachmentSourcePicker: AttachmentSourcePicker? by remember { mutableStateOf(null) } + LaunchedEffect(composerMode.value) { when (val modeValue = composerMode.value) { is MessageComposerMode.Edit -> text.value = modeValue.defaultContent.toStableCharSequence() @@ -80,21 +98,47 @@ class MessageComposerPresenter @Inject constructor( is MessageComposerEvents.SendMessage -> appCoroutineScope.sendMessage(event.message, composerMode, text) is MessageComposerEvents.SetMode -> composerMode.value = event.composerMode - MessageComposerEvents.TakePhoto -> localCoroutineScope.launch { - if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) { - cameraPhotoPicker.launch() - } - }} + MessageComposerEvents.AddAttachment -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = AttachmentSourcePicker.AllMedia + } + MessageComposerEvents.DismissAttachmentMenu -> attachmentSourcePicker = null + MessageComposerEvents.PickAttachmentSource.FromGallery -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = null + galleryMediaPicker.launch() + } + MessageComposerEvents.PickAttachmentSource.FromFiles -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = null + filesPicker.launch() + } + MessageComposerEvents.PickAttachmentSource.FromCamera -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = AttachmentSourcePicker.Camera + } + MessageComposerEvents.PickCameraAttachmentSource.Photo -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = null + cameraPhotoPicker.launch() + } + MessageComposerEvents.PickCameraAttachmentSource.Video -> localCoroutineScope.ifMediaPickersEnabled { + attachmentSourcePicker = null + cameraVideoPicker.launch() + } + } } return MessageComposerState( text = text.value, isFullScreen = isFullScreen.value, mode = composerMode.value, + attachmentSourcePicker = attachmentSourcePicker, eventSink = ::handleEvents ) } + private fun CoroutineScope.ifMediaPickersEnabled(action: suspend () -> Unit) = launch { + if (featureFlagService.isFeatureEnabled(FeatureFlags.ShowMediaUploadingFlow)) { + action() + } + } + private fun MutableState.setToNormal() { value = MessageComposerMode.Normal("") } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerState.kt index ce6d68f8f7..7824f1f242 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerState.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerState.kt @@ -25,7 +25,13 @@ data class MessageComposerState( val text: StableCharSequence?, val isFullScreen: Boolean, val mode: MessageComposerMode, + val attachmentSourcePicker: AttachmentSourcePicker?, val eventSink: (MessageComposerEvents) -> Unit ) { val isSendButtonVisible: Boolean = text?.charSequence.isNullOrEmpty().not() } + +sealed interface AttachmentSourcePicker { + object AllMedia : AttachmentSourcePicker + object Camera : AttachmentSourcePicker +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerStateProvider.kt index 6a8ec37e30..38a88ee91f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerStateProvider.kt @@ -31,5 +31,6 @@ fun aMessageComposerState() = MessageComposerState( text = StableCharSequence(""), isFullScreen = false, mode = MessageComposerMode.Normal(content = ""), + attachmentSourcePicker = null, eventSink = {} ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerView.kt index ebdfca6f9e..9b167013da 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerView.kt @@ -54,7 +54,7 @@ fun MessageComposerView( onCloseSpecialMode = ::onCloseSpecialMode, onComposerTextChange = ::onComposerTextChange, onAddAttachment = { - state.eventSink(MessageComposerEvents.TakePhoto) + state.eventSink(MessageComposerEvents.AddAttachment) }, composerCanSendMessage = state.isSendButtonVisible, composerText = state.text?.charSequence?.toString(), diff --git a/features/messages/impl/src/main/res/values/localazy.xml b/features/messages/impl/src/main/res/values/localazy.xml new file mode 100644 index 0000000000..8c53bca6c4 --- /dev/null +++ b/features/messages/impl/src/main/res/values/localazy.xml @@ -0,0 +1,9 @@ + + + "Camera" + "Take photo" + "Record a video" + "Attachment" + "Photo & Video Library" + "Failed processing media to upload, please try again." + \ No newline at end of file diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index b18e9631ef..e550371abd 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -22,7 +22,9 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine import app.cash.turbine.test +import com.google.common.truth.Truth import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.impl.textcomposer.AttachmentSourcePicker import io.element.android.features.messages.impl.textcomposer.MessageComposerEvents import io.element.android.features.messages.impl.textcomposer.MessageComposerPresenter import io.element.android.features.messages.impl.textcomposer.MessageComposerState @@ -279,6 +281,103 @@ class MessageComposerPresenterTest { } } + @Test + fun `present - Open attachments menu`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.AddAttachment) + + assertThat(awaitItem().attachmentSourcePicker).isEqualTo(AttachmentSourcePicker.AllMedia) + } + } + + @Test + fun `present - Open camera attachments menu`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromCamera) + + assertThat(awaitItem().attachmentSourcePicker).isEqualTo(AttachmentSourcePicker.Camera) + } + } + + @Test + fun `present - Dismiss attachments menu`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.AddAttachment) + skipItems(1) + + initialState.eventSink(MessageComposerEvents.DismissAttachmentMenu) + assertThat(awaitItem().attachmentSourcePicker).isNull() + } + } + + @Test + fun `present - Pick media from gallery`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromGallery) + + // TODO verify some post processing of the selected media is done + } + } + + @Test + fun `present - Pick file from storage`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickAttachmentSource.FromFiles) + + // TODO verify some post processing of the selected media is done + } + } + @Test fun `present - Take photo`() = runTest { val fakeMatrixRoom = FakeMatrixRoom() @@ -292,11 +391,30 @@ class MessageComposerPresenterTest { presenter.present() }.test { val initialState = awaitItem() - initialState.eventSink(MessageComposerEvents.TakePhoto) + initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Photo) // TODO verify some post processing of the captured image is done } } + + @Test + fun `present - Record video`() = runTest { + val fakeMatrixRoom = FakeMatrixRoom() + val presenter = MessageComposerPresenter( + this, + fakeMatrixRoom, + pickerProvider, + featureFlagService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink(MessageComposerEvents.PickCameraAttachmentSource.Video) + + // TODO verify some post processing of the captured video is done + } + } } fun anEditMode() = MessageComposerMode.Edit(AN_EVENT_ID, A_MESSAGE) diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 584f4322d3..d1356896d0 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -6,7 +6,6 @@ "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them." "Message encryption enabled" - "Invite people" "Share room" "Block" "Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime." @@ -14,6 +13,7 @@ "Unblock" "On unblocking the user, you will be able to see all messages by them again." "Unblock user" + "Invite people" "Leave room" "People" "Security" diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt index fe64256164..95d603b9dd 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/ModalBottomSheetLayout.kt @@ -19,9 +19,17 @@ package io.element.android.libraries.designsystem.theme.components import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetDefaults import androidx.compose.material.ModalBottomSheetState @@ -30,12 +38,14 @@ import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.modifiers.applyIf import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.PreviewGroup @@ -46,15 +56,36 @@ fun ModalBottomSheetLayout( sheetContent: @Composable ColumnScope.() -> Unit, modifier: Modifier = Modifier, sheetState: ModalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), - sheetShape: Shape = MaterialTheme.shapes.large, + sheetShape: Shape = MaterialTheme.shapes.large.copy(bottomStart = CornerSize(0.dp), bottomEnd = CornerSize(0.dp)), sheetElevation: Dp = ModalBottomSheetDefaults.Elevation, sheetBackgroundColor: Color = MaterialTheme.colorScheme.surface, sheetContentColor: Color = contentColorFor(sheetBackgroundColor), scrimColor: Color = ModalBottomSheetDefaults.scrimColor, + displayHandle: Boolean = false, + useSystemPadding: Boolean = true, content: @Composable () -> Unit = {} ) { androidx.compose.material.ModalBottomSheetLayout( - sheetContent = sheetContent, + sheetContent = { + Column( + Modifier.fillMaxWidth() + .applyIf(useSystemPadding, ifTrue = { + navigationBarsPadding() + }) + ) { + if (displayHandle) { + Spacer(modifier = Modifier.height(16.dp)) + Box( + modifier = Modifier + .background(MaterialTheme.colorScheme.onSurfaceVariant, RoundedCornerShape(2.dp)) + .size(width = 32.dp, height = 4.dp) + .align(Alignment.CenterHorizontally), + ) + Spacer(modifier = Modifier.height(24.dp)) + } + sheetContent() + } + }, modifier = modifier, sheetState = sheetState, sheetShape = sheetShape, @@ -79,10 +110,13 @@ internal fun ModalBottomSheetLayoutDarkPreview() = @Composable private fun ContentToPreview() { ModalBottomSheetLayout( - modifier = Modifier.height(100.dp), + modifier = Modifier.height(140.dp), + displayHandle = true, sheetState = ModalBottomSheetState(ModalBottomSheetValue.Expanded), sheetContent = { - Text(text = "Sheet Content", modifier = Modifier.padding(16.dp).background(color = Color.Green)) + Text(text = "Sheet Content", modifier = Modifier + .padding(start = 16.dp, end = 16.dp, bottom = 20.dp) + .background(color = Color.Green)) } ) { Text(text = "Content", modifier = Modifier.background(color = Color.Red)) diff --git a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index bab573de58..1ede7be8fa 100644 --- a/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -25,6 +25,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -55,8 +57,9 @@ fun TextComposer( if (LocalInspectionMode.current) { FakeComposer(modifier) } else { + val focusRequester = FocusRequester() AndroidView( - modifier = modifier, + modifier = modifier.focusRequester(focusRequester), factory = { context -> RichTextComposerLayout(context).apply { // Sets up listeners for View -> Compose communication diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 0ee60c0b88..dfedf9cf23 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -95,14 +95,10 @@ "Neu" "Teile Analyse-Daten" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." - "Blockieren" - "Nutzer blockieren" - "Blockierung aufheben" - "Nutzer entblockieren" "Erkennungsschwelle" "Version: %1$s (%2$s)" "de" "Fehler" "Erfolg" "Nutzer blockieren" - \ No newline at end of file + diff --git a/libraries/ui-strings/src/main/res/values-es/translations.xml b/libraries/ui-strings/src/main/res/values-es/translations.xml index b430048f79..4ec790f330 100644 --- a/libraries/ui-strings/src/main/res/values-es/translations.xml +++ b/libraries/ui-strings/src/main/res/values-es/translations.xml @@ -128,12 +128,6 @@ "Este es el principio de esta conversación." "Nuevos" "Marque si quieres ocultar todos los mensajes actuales y futuros de este usuario" - "Bloquear" - "Los usuarios bloqueados no podrán enviarte mensajes y se ocultarán todos sus mensajes. Puede revertir esta acción en cualquier momento." - "Bloquear usuario" - "Desbloquear" - "Al desbloquear al usuario, podrás volver a ver todos sus mensajes." - "Desbloquear usuario" "Agitar con fuerza" "Umbral de detección" "General" @@ -142,4 +136,4 @@ "Error" "Terminado" "Bloquear usuario" - \ No newline at end of file + diff --git a/libraries/ui-strings/src/main/res/values-it/translations.xml b/libraries/ui-strings/src/main/res/values-it/translations.xml index 8fbe54dae2..a0e90ece3e 100644 --- a/libraries/ui-strings/src/main/res/values-it/translations.xml +++ b/libraries/ui-strings/src/main/res/values-it/translations.xml @@ -128,12 +128,6 @@ "Questo è l\'inizio della conversazione." "Nuovo" "Seleziona se vuoi nascondere tutti i messaggi attuali e futuri di questo utente" - "Blocca" - "Gli utenti bloccati non saranno in grado di inviarti nuovi messaggi e tutti quelli già esistenti saranno nascosti. Potrai annullare questa azione in qualsiasi momento." - "Blocca utente" - "Sblocca" - "Dopo aver sbloccato l\'utente, potrai vedere nuovamente tutti i suoi messaggi." - "Sblocca utente" "Rageshake" "Soglia di rilevamento" "Generali" @@ -142,4 +136,4 @@ "Errore" "Operazione riuscita" "Blocca utente" - \ No newline at end of file + diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index d9602214d0..32a7e62d45 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -143,12 +143,6 @@ "Nu"" împărtășim informații cu terți" "Ajutați la îmbunătățirea %1$s" "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" - "Blocați" - "Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând." - "Blocați utilizatorul" - "Deblocați" - "La deblocarea utilizatorului, veți putea vedea din nou toate mesajele de la acesta." - "Deblocați utilizatorul" "Rageshake" "Prag de detecție" "General" @@ -160,4 +154,4 @@ "Puteți citi toate condițiile noastre %1$s." "aici" "Blocați utilizatorul" - \ No newline at end of file + diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index de7750a3ee..fe7cc27a84 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -147,12 +147,6 @@ "Failed processing media to upload, please try again." "Failed uploading media, please try again." "Check if you want to hide all current and future messages from this user" - "Block" - "Blocked users will not be able to send you messages and all message by them will be hidden. You can reverse this action anytime." - "Block user" - "Unblock" - "On unblocking the user, you will be able to see all messages by them again." - "Unblock user" "Rageshake" "Detection threshold" "General" diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 5555d6e6b3..860002e3dd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21698ff1c1f2b30ee9c3cc0c2539b35fe7cf54aac07cb0dc376d7c1a03c8814b -size 4483 +oid sha256:e3cb476c16c2cae9f3230cc4030b66662b9f63cef22208fc5bf577d0b16bf946 +size 4484 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 5555d6e6b3..860002e3dd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21698ff1c1f2b30ee9c3cc0c2539b35fe7cf54aac07cb0dc376d7c1a03c8814b -size 4483 +oid sha256:e3cb476c16c2cae9f3230cc4030b66662b9f63cef22208fc5bf577d0b16bf946 +size 4484 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_2,NEXUS_5,1.0,en].png index c18cc0dd08..29104a87c0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db69f27f60dd9d93bb4d313741b84aa4a3ed008d229590338514c7683c0e3a11 -size 14786 +oid sha256:dea394d708a714603ea77543a7ab31550baaea72c75255c56ac9162589096128 +size 14453 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_0,NEXUS_5,1.0,en].png index eb47d1cd71..4c04d9893f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc3bf884b0425c72cafecdd4afa4e2c28064799f695962360ae4c979a3fe542e -size 4490 +oid sha256:54b434198b8b6b534e0e82310e58eec162f18aba876f0dca9a1790c137230595 +size 4496 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_1,NEXUS_5,1.0,en].png index eb47d1cd71..4c04d9893f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc3bf884b0425c72cafecdd4afa4e2c28064799f695962360ae4c979a3fe542e -size 4490 +oid sha256:54b434198b8b6b534e0e82310e58eec162f18aba876f0dca9a1790c137230595 +size 4496 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_2,NEXUS_5,1.0,en].png index 94f6bbbd99..9734dff6c3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.actionlist_null_DefaultGroup_SheetContentLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c630475e03d86195a0ebcc57bd12934b799fee956c635b30df60913cd9a3f50 -size 16032 +oid sha256:f3080445c87d85fd5c51228e33ef7f91eb3a718f2f8288bdfa2a48d6769a25a1 +size 15480 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 7391b6f1d2..580da9ceb6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6de6a4dee8a62839c15a84de8cb9817b3de8ae9fd2317e723c47bb679a72b7d -size 39603 +oid sha256:01f54909964a4ed07d8850ab2bffad8b99ed641d731c1808b04f164ff3e0cbba +size 39632 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index a56c033ac1..ff59a95d62 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d8f233685a21a72081b24ee91bc90e94b340c39f7e2c498f76a69e5b7e129ff -size 41480 +oid sha256:4ea39fdf1cb61657bc1eafb7d353ee15517c26c0e6bb2804b7c8350fa651299e +size 41508 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1d82aaf60b --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7d7470933e27c64182c5f8acec7b4ce3896db1cc3836ff6aa6cf33fe8688f10 +size 37524 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a943b1245f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ac05963f89ac0d3045700447a40a4a6f38cc155812de7eb9a87fb9a01643033 +size 36300 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 7186b2e07d..a24d98a08c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:caaf172ddb39cbd1a77bfb2295202e5c0aa95c846353a17da6abe6c50316cf63 -size 38510 +oid sha256:c6c558d2a8e6adb4831b91aff249d16dfc48389adef5ada6e1215df1d597639a +size 38654 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index b9628a18da..549011130b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91a5fe69790f195bba47a878efb1d93a4e35bff9ec11f262cdf9c237b4cc3639 -size 40563 +oid sha256:946802d97d29f9fe41a71f1ee343e298e1b0d9dbbc6561cc82fed712e623f89c +size 40708 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3586f9c218 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97f10ca5290a0b4b1e034e157abb0f069fac2f2c2e88234edb1efc25c6c111f0 +size 36228 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8866feaf6f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cfc76bb6a8d8b269f53c2cc1c369a5c5ffb0f0fb858ebfa50c613a793bc1b421 +size 35195 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutDarkPreview_0_null,NEXUS_5,1.0,en].png index 452e9b46b0..7d690c9e94 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b36389c997a7796eb7c070d4927c9a5197bca08b24cf58797de9d622c8a6176 -size 13945 +oid sha256:5eb57bf5069755caaa2188db848517a915b298ec3ba72334e27c90b354d0b4ef +size 14302 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutLightPreview_0_null,NEXUS_5,1.0,en].png index e3c83080c7..a5ab66d757 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.theme.components_null_BottomSheets_ModalBottomSheetLayoutLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1579ab462c62f872774fbdb820da1f6abc09e4b8e6fb2e3baaf3d46a0722ed94 -size 15471 +oid sha256:4c37402ebdad96583f879614d07ea45f5193f6976ce5f2382333106dda77f095 +size 15520 diff --git a/tools/localazy/config.json b/tools/localazy/config.json index ec6d5e65c8..5219187f75 100644 --- a/tools/localazy/config.json +++ b/tools/localazy/config.json @@ -85,6 +85,18 @@ "screen_room_member_list_.*", "screen_dm_details_.*" ] + }, + { + "name": ":features:messages:impl", + "includeRegex": [ + "screen_room_.*", + "screen_dm_details_.*" + ], + "excludeRegex": [ + "screen_room_details_.*", + "screen_room_member.*", + "screen_dm_.*" + ] } ] } diff --git a/tools/localazy/generateLocalazyConfig.py b/tools/localazy/generateLocalazyConfig.py index e2bc310e1e..6dfffa9c69 100755 --- a/tools/localazy/generateLocalazyConfig.py +++ b/tools/localazy/generateLocalazyConfig.py @@ -36,10 +36,13 @@ allActions = [] # Iterating on the config for entry in config["modules"]: # Create action for the default language + excludeRegex = regexToAlwaysExclude + if "excludeRegex" in entry: + excludeRegex += entry["excludeRegex"] action = baseAction | { "output": convertModuleToPath(entry["name"]) + "/src/main/res/values/localazy.xml", "includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])), - "excludeKeys": list(map(lambda i: "REGEX:" + i, regexToAlwaysExclude)), + "excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)), "conditions": [ "equals: ${languageCode}, en" ] @@ -51,7 +54,7 @@ for entry in config["modules"]: actionTranslation = baseAction | { "output": convertModuleToPath(entry["name"]) + "/src/main/res/values-${langAndroidResNoScript}/translations.xml", "includeKeys": list(map(lambda i: "REGEX:" + i, entry["includeRegex"])), - "excludeKeys": list(map(lambda i: "REGEX:" + i, regexToAlwaysExclude)), + "excludeKeys": list(map(lambda i: "REGEX:" + i, excludeRegex)), "conditions": [ "!equals: ${languageCode}, en" ]