Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Wire
* Copyright (C) 2024 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.home.messagecomposer

import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.Text
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.test.espresso.Espresso.pressBack
import com.wire.android.ui.home.messagecomposer.state.AdditionalOptionSubMenuState
import com.wire.android.ui.home.messagecomposer.state.MessageCompositionInputStateHolder
import org.junit.Rule
import org.junit.Test

class MessageComposerBackHandlerTest {

@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

@Test
fun givenExpandedComposer_whenPressingSystemBack_thenComposerCollapses() {
composeTestRule.setContent {
val focusManager = LocalFocusManager.current
val focusRequester = remember { FocusRequester() }
val stateHolder = remember(focusManager) {
MessageCompositionInputStateHolder(
messageTextState = TextFieldState(),
keyboardController = null,
focusManager = focusManager,
focusRequester = focusRequester
)
}

LaunchedEffect(stateHolder) {
stateHolder.inputFocused = true
stateHolder.toggleInputSize()
}

BackHandler(stateHolder.inputFocused) {
stateHolder.collapseComposer(AdditionalOptionSubMenuState.Default)
}

Text(
text = if (stateHolder.isTextExpanded) "expanded" else "collapsed",
modifier = Modifier.testTag(COMPOSER_BACK_STATE_TAG)
)
}

composeTestRule.onNodeWithTag(COMPOSER_BACK_STATE_TAG).assertTextEquals("expanded")
pressBack()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag(COMPOSER_BACK_STATE_TAG).assertTextEquals("collapsed")
}
}

private const val COMPOSER_BACK_STATE_TAG = "composer_back_state"
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,6 @@ fun EnabledMessageComposer(
}

ActiveMessageComposerInput(
onClearFocus = {
messageCompositionInputStateHolder.clearFocus()
},
conversationId = conversationId,
messageComposition = messageComposition.value,
keyboardOptions = keyboardOptions,
Expand All @@ -273,7 +270,6 @@ fun EnabledMessageComposer(
focusRequester = messageCompositionInputStateHolder.focusRequester,
onFocused = ::onInputFocused,
onToggleInputSize = messageCompositionInputStateHolder::toggleInputSize,
onTextCollapse = messageCompositionInputStateHolder::collapseText,
onCancelReply = messageCompositionHolder.value::clearReply,
onCancelEdit = ::cancelEdit,
onChangeSelfDeletionClicked = onChangeSelfDeletionClicked,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

package com.wire.android.ui.home.messagecomposer

import android.view.KeyEvent
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
Expand Down Expand Up @@ -49,9 +48,6 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.nativeKeyCode
import androidx.compose.ui.input.key.onPreInterceptKeyBeforeSoftKeyboard
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -97,7 +93,6 @@ fun ActiveMessageComposerInput(
onEditButtonClicked: () -> Unit,
onChangeSelfDeletionClicked: (currentlySelected: SelfDeletionTimer) -> Unit,
onToggleInputSize: () -> Unit,
onTextCollapse: () -> Unit,
onCancelReply: () -> Unit,
onCancelEdit: () -> Unit,
onFocused: () -> Unit,
Expand All @@ -107,7 +102,6 @@ fun ActiveMessageComposerInput(
optionsSelected: Boolean,
onPlusClick: () -> Unit,
modifier: Modifier = Modifier,
onClearFocus: () -> Unit = { }
) {
Column(
modifier = modifier
Expand Down Expand Up @@ -149,8 +143,6 @@ fun ActiveMessageComposerInput(
showOptions = showOptions,
optionsSelected = optionsSelected,
onPlusClick = onPlusClick,
onTextCollapse = onTextCollapse,
onClearFocus = onClearFocus,
modifier = Modifier
.fillMaxWidth()
.let {
Expand Down Expand Up @@ -190,13 +182,11 @@ private fun InputContent(
showOptions: Boolean,
optionsSelected: Boolean,
onPlusClick: () -> Unit,
onTextCollapse: () -> Unit,
modifier: Modifier = Modifier,
viewModel: SelfDeletingMessageActionViewModel =
hiltViewModelScoped<SelfDeletingMessageActionViewModelImpl, SelfDeletingMessageActionViewModel, SelfDeletingMessageActionArgs>(
SelfDeletingMessageActionArgs(conversationId = conversationId)
),
onClearFocus: () -> Unit = { }
) {
ConstraintLayout(modifier = modifier) {
val (additionalOptionButton, input, actions) = createRefs()
Expand All @@ -219,7 +209,6 @@ private fun InputContent(

val collapsedMaxHeight = dimensions().messageComposerActiveInputMaxHeight
MessageComposerTextInput(
isTextExpanded = isTextExpanded,
focusRequester = focusRequester,
colors = inputType.inputTextColor(isSelfDeleting = viewModel.state().duration != null),
messageTextState = messageTextState,
Expand All @@ -228,10 +217,8 @@ private fun InputContent(
onFocused = onFocused,
onSelectedLineIndexChanged = onSelectedLineIndexChanged,
onLineBottomYCoordinateChanged = onLineBottomYCoordinateChanged,
onTextCollapse = onTextCollapse,
keyboardOptions = keyboardOptions,
onKeyBoardAction = onKeyboardAction,
onClearFocus = onClearFocus,
modifier = Modifier
.fillMaxWidth()
.constrainAs(input) {
Expand Down Expand Up @@ -282,18 +269,15 @@ private fun InputContent(
@Composable
private fun MessageComposerTextInput(
messageTextState: TextFieldState,
isTextExpanded: Boolean,
focusRequester: FocusRequester,
colors: WireTextFieldColors,
placeHolderText: String,
onTextCollapse: () -> Unit,
onFocused: () -> Unit,
keyboardOptions: KeyboardOptions,
onKeyBoardAction: KeyboardActionHandler?,
modifier: Modifier = Modifier,
onSelectedLineIndexChanged: (Int) -> Unit = { },
onLineBottomYCoordinateChanged: (Float) -> Unit = { },
onClearFocus: () -> Unit = { },
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
Expand All @@ -320,19 +304,6 @@ private fun MessageComposerTextInput(
if (focusState.isFocused) {
onFocused()
}
}
.onPreInterceptKeyBeforeSoftKeyboard { event ->
if (event.key.nativeKeyCode == KeyEvent.KEYCODE_BACK) {
onClearFocus()
if (isTextExpanded) {
onTextCollapse()
true
} else {
false
}
} else {
false
}
},
interactionSource = interactionSource,
onSelectedLineIndexChanged = onSelectedLineIndexChanged,
Expand Down Expand Up @@ -387,7 +358,6 @@ private fun PreviewActiveMessageComposerInput(inputType: InputType, isTextExpand
onEditButtonClicked = {},
onChangeSelfDeletionClicked = {},
onToggleInputSize = {},
onTextCollapse = {},
onCancelReply = {},
onCancelEdit = {},
onFocused = {},
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@
<string name="label_conversation_pictures_empty">Bislang hat niemand Bilder in dieser Unterhaltung geteilt 🥲</string>
<string name="label_conversation_files_empty">Bislang hat niemand Dateien in dieser Unterhaltung geteilt 🙀</string>
<!-- Search Contact-->
<string name="label_selected">Ausgewählt</string>
<string name="label_selected">Ausgewählt (%1$d)</string>
<string name="label_contacts">Kontakte</string>
<string name="label_new_group">Neue Gruppe</string>
<string name="label_new_channel">Neuer Channel</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-hu/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@
<string name="label_conversation_pictures_empty">Ebben a beszélgetésben még senki nem osztott meg képet 🥲</string>
<string name="label_conversation_files_empty">Ebben a beszégetésben még senki nem osztott meg fájlt 🙀</string>
<!-- Search Contact-->
<string name="label_selected">Kiválasztva</string>
<string name="label_selected">Kiválasztva (%1$d)</string>
<string name="label_contacts">Névjegyek</string>
<string name="label_new_group">Új csoport</string>
<string name="label_new_channel">Új csatorna</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-pt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ Uma mensagem excluída não pode ser restaurada.</string>
<string name="label_conversation_pictures_empty">Ninguém compartilhou fotos nesta conversa ainda 🥲</string>
<string name="label_conversation_files_empty">Ninguém compartilhou arquivos nesta conversa ainda 🙀</string>
<!-- Search Contact-->
<string name="label_selected">Selecionado</string>
<string name="label_selected">Selecionado (%1$d)</string>
<string name="label_contacts">Contatos</string>
<string name="label_new_group">Novo Grupo</string>
<string name="label_new_channel">Novo Canal</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@
<string name="label_conversation_pictures_empty">Никто не делился фотографиями в этой беседе 🥲</string>
<string name="label_conversation_files_empty">Никто не делился файлами в этой беседе 🙀</string>
<!-- Search Contact-->
<string name="label_selected">Выбрано</string>
<string name="label_selected">Выбрано (%1$d)</string>
<string name="label_contacts">Контакты</string>
<string name="label_new_group">Новая группа</string>
<string name="label_new_channel">Новый канал</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-si/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@
<string name="label_conversation_pictures_empty">කිසිවෙක් මෙම සංවාදයෙහි ඡායාරූප බෙදාගෙන නැත 🥲</string>
<string name="label_conversation_files_empty">කිසිවෙක් මෙම සංවාදයෙහි ගොනු බෙදාගෙන නැත 🙀</string>
<!-- Search Contact-->
<string name="label_selected">තෝරා ගන්නා ලදී</string>
<string name="label_selected">තෝරා ගන්නා ලදී (%1$d)</string>
<string name="label_contacts">සම්බන්ධතා</string>
<string name="label_new_group">නව සමූහයක්</string>
<string name="label_new_channel">නව නාලිකාව</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -980,7 +980,7 @@
<string name="label_conversation_files_empty">Nobody shared files in this conversation yet 🙀</string>
<string name="conversation_search_files_button">Search files</string>
<!-- Search Contact-->
<string name="label_selected">Selected</string>
<string name="label_selected">Selected (%1$d)</string>
<string name="label_contacts">Contacts</string>
<string name="label_new_group">New Group</string>
<string name="label_new_channel">New Channel</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ import org.junit.jupiter.api.extension.ExtensionContext
*/
class NavigationTestExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
mockkStatic("com.wire.android.ui.NavArgsGettersKt")
mockkStatic("com.ramcosta.composedestinations.generated.app.NavArgsGettersKt")
}

override fun afterEach(context: ExtensionContext?) {
unmockkStatic("com.wire.android.ui.NavArgsGettersKt")
unmockkStatic("com.ramcosta.composedestinations.generated.app.NavArgsGettersKt")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,13 @@ internal class NavigationUtilsTest {
}

@Test
fun `given route with segments but without parameters, when getting primary route, then return only host part`() {
fun `given route with segments but without parameters, when getting primary route, then return host with first segment`() {
// Given
val host = "route"
val route = "$host/segment1/segment2"
val route = "route/segment1/segment2"
// When
val result = route.getBaseRoute()
// Then
assertEquals(host, result)
assertEquals("route/segment1", result)
}

@Test
Expand All @@ -136,13 +135,52 @@ internal class NavigationUtilsTest {
}

@Test
fun `given route with segments and parameters, when getting primary route, then return only host part`() {
fun `given route with segments and parameters, when getting primary route, then return host with first segment`() {
// Given
val host = "route"
val route = "$host/segment1/segment2?param1=value1&param2=value2"
val route = "route/segment1/segment2?param1=value1&param2=value2"
// When
val result = route.getBaseRoute()
// Then
assertEquals(host, result)
assertEquals("route/segment1", result)
}

@Test
fun `given empty string, when getting base route, then return empty string`() {
// Given
val route = ""
// When
val result = route.getBaseRoute()
// Then
assertEquals("", result)
}

@Test
fun `given route with single segment, when getting base route, then return full route`() {
// Given
val route = "route/segment"
// When
val result = route.getBaseRoute()
// Then
assertEquals("route/segment", result)
}

@Test
fun `given route with single segment and parameters, when getting base route, then return route with segment`() {
// Given
val route = "route/segment?param=value"
// When
val result = route.getBaseRoute()
// Then
assertEquals("route/segment", result)
}

@Test
fun `given route where query param appears before second slash, when getting base route, then stop at query param`() {
// Given
val route = "route/segment?param=value/other"
// When
val result = route.getBaseRoute()
// Then
assertEquals("route/segment", result)
}
}
Loading