diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt index b780e2b76..b500ea54b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/resetpassword/ResetPasswordScreen.kt @@ -29,7 +29,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.x8bit.bitwarden.R import com.x8bit.bitwarden.data.auth.datasource.disk.model.ForcePasswordResetReason -import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar +import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard import com.x8bit.bitwarden.ui.platform.components.dialog.BasicDialogState @@ -101,8 +101,9 @@ fun ResetPasswordScreen( .fillMaxSize() .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { - BitwardenMediumTopAppBar( + BitwardenTopAppBar( title = stringResource(id = R.string.update_master_password), + navigationIcon = null, scrollBehavior = scrollBehavior, actions = { BitwardenTextButton( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt index d23676a6e..bb48e1120 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/setpassword/SetPasswordScreen.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.x8bit.bitwarden.R -import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenMediumTopAppBar +import com.x8bit.bitwarden.ui.platform.components.appbar.BitwardenTopAppBar import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTextButton import com.x8bit.bitwarden.ui.platform.components.card.BitwardenInfoCalloutCard import com.x8bit.bitwarden.ui.platform.components.dialog.BasicDialogState @@ -62,8 +62,9 @@ fun SetPasswordScreen( .fillMaxSize() .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { - BitwardenMediumTopAppBar( + BitwardenTopAppBar( title = stringResource(id = R.string.set_master_password), + navigationIcon = null, scrollBehavior = scrollBehavior, actions = { BitwardenTextButton( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenMediumTopAppBar.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenMediumTopAppBar.kt index 5141198ba..6adbbad15 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenMediumTopAppBar.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenMediumTopAppBar.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MediumTopAppBar import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.rememberTopAppBarState @@ -45,13 +46,14 @@ fun BitwardenMediumTopAppBar( dividerStyle: TopAppBarDividerStyle = TopAppBarDividerStyle.ON_SCROLL, actions: @Composable RowScope.() -> Unit = {}, ) { - MediumTopAppBar( + TopAppBar( colors = bitwardenTopAppBarColors(), scrollBehavior = scrollBehavior, + expandedHeight = 56.dp, title = { Text( text = title, - style = BitwardenTheme.typography.titleLarge, + style = BitwardenTheme.typography.headlineMedium, modifier = Modifier.testTag(tag = "PageTitleLabel"), ) }, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenTopAppBar.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenTopAppBar.kt index a42e5bb1a..a1f426bbf 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenTopAppBar.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/components/appbar/BitwardenTopAppBar.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import com.x8bit.bitwarden.R import com.x8bit.bitwarden.ui.platform.base.util.mirrorIfRtl import com.x8bit.bitwarden.ui.platform.base.util.scrolledContainerBottomDivider @@ -107,6 +108,8 @@ fun BitwardenTopAppBar( colors = bitwardenTopAppBarColors(), scrollBehavior = scrollBehavior, navigationIcon = navigationIconContent, + collapsedHeight = 48.dp, + expandedHeight = 96.dp, title = { // The height of the component is controlled and will only allow for 1 extra row, // making adding any arguments for softWrap and minLines superfluous. @@ -127,6 +130,7 @@ fun BitwardenTopAppBar( colors = bitwardenTopAppBarColors(), scrollBehavior = scrollBehavior, navigationIcon = navigationIconContent, + expandedHeight = 48.dp, title = { Text( text = title, diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreen.kt index 1f3a49ff5..3991c3d94 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/SettingsScreen.kt @@ -70,9 +70,7 @@ fun SettingsScreen( } } - val scrollBehavior = - TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) - + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) BitwardenScaffold( topBar = { BitwardenMediumTopAppBar( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt index 08bb27fb8..dc81660ca 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/generator/GeneratorScreen.kt @@ -179,14 +179,7 @@ fun GeneratorScreen( RandomWordHandlers.create(viewModel = viewModel) } - val scrollBehavior = when (state.generatorMode) { - GeneratorMode.Default -> { - TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()) - } - - else -> TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) - } - + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) BitwardenScaffold( topBar = { when (state.generatorMode) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreen.kt index 868a6e344..7eea0745d 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreen.kt @@ -103,7 +103,7 @@ fun SendScreen( ) val sendHandlers = remember(viewModel) { SendHandlers.create(viewModel) } - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( state = rememberTopAppBarState(), ) BitwardenScaffold( diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt index 2ce2cbc3f..af5fa042b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultScreen.kt @@ -179,7 +179,7 @@ private fun VaultScreenScaffold( onDimBottomNavBarRequest(shouldShowMenu) } var shouldShowExitConfirmationDialog by rememberSaveable { mutableStateOf(false) } - val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( state = rememberTopAppBarState(), canScroll = { !accountMenuVisible }, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt index 5994323fd..42c01a151 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendScreenTest.kt @@ -12,7 +12,10 @@ import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isDialog import androidx.compose.ui.test.isDisplayed import androidx.compose.ui.test.isPopup +import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick @@ -37,6 +40,7 @@ import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +@Suppress("LargeClass") class SendScreenTest : BaseComposeTest() { private var onNavigateToNewSendCalled = false @@ -438,14 +442,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -462,14 +472,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -492,14 +508,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -522,14 +544,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -552,14 +580,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -582,14 +616,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -611,14 +651,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick() @@ -646,14 +692,20 @@ class SendScreenTest : BaseComposeTest() { viewState = SendState.ViewState.Content( textTypeCount = 0, fileTypeCount = 1, - sendItems = listOf(DEFAULT_SEND_ITEM), + sendItems = listOf( + DEFAULT_SEND_ITEM, + DEFAULT_SEND_ITEM.copy(id = "mockId-2"), + ), ), ) } composeTestRule.assertNoDialogExists() + // We scroll to the last item but click the first one to avoid clicking the FAB by mistake composeTestRule - .onNodeWithContentDescription("Options") + .onAllNodesWithContentDescription("Options") + .apply { onLast().performScrollTo() } + .onFirst() .assertIsDisplayed() .performClick()