[#808] Use Dimens Across App

* [#808] Use Dimens across the app

- Shorter Onboarding screen design enhanced

* Shorter New Wallet Backup screen UI enhanced

* Home screen UI enhance

* Seed screen UI enhance

* Link SendArgumentsWrapper issue

* Move custom buttons paddings to PaddingValues

- Optional Modifier parameter should have a default value of Modifier
- Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings
- Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs.

* Settings screen enhance

* Support screen UI enhance

* About screen UI enhance

* Scan screen UI enhance

- Move modifiers to the caller’s side
- Match texts to predefined styles
- Add color to Small text component + reorder parameters

* Send screen UI enahnce

- Lifted out some modifiers arguments to the caller’s side
- Fixed button’s horizontal paddings

* Scan move BackHandler to Android class

* Update screen UI enhance

* Receive screen UI enhance

* Address screen UI enhance

* Ignore AndroidUpdate back action test

- Will be refactored soon

* Remove TODO as already implemented

* Restore screens UI enhance

* Create UpdateViewAndroidTest

* [#807][Design system] Remove deprecated Paddings

* [#705] Instrumentation coverage generation fails locally

* [#808] Use Dimens across the app

- Shorter Onboarding screen design enhanced

* Shorter New Wallet Backup screen UI enhanced

* Home screen UI enhance

* Seed screen UI enhance

* Link SendArgumentsWrapper issue

* Move custom buttons paddings to PaddingValues

- Optional Modifier parameter should have a default value of Modifier
- Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings
- Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs.

* Settings screen enhance

* Support screen UI enhance

* About screen UI enhance

* Scan screen UI enhance

- Move modifiers to the caller’s side
- Match texts to predefined styles
- Add color to Small text component + reorder parameters

* Send screen UI enahnce

- Lifted out some modifiers arguments to the caller’s side
- Fixed button’s horizontal paddings

* Scan move BackHandler to Android class

* Update screen UI enhance

* Receive screen UI enhance

* Address screen UI enhance

* Ignore AndroidUpdate back action test

- Will be refactored soon

* Remove TODO as already implemented

* Restore screens UI enhance

* Create UpdateViewAndroidTest

* [#807][Design system] Remove deprecated Paddings

* Address review comments
This commit is contained in:
Honza Rychnovsky 2023-04-04 14:21:18 +02:00 committed by GitHub
parent 859ec1591e
commit cb98cc048f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 662 additions and 311 deletions

View File

@ -1,6 +1,7 @@
package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
@ -34,6 +35,10 @@ fun PrimaryButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true
) {
Button(
@ -41,7 +46,7 @@ fun PrimaryButton(
modifier = modifier.then(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(outerPaddingValues)
),
enabled = enabled,
colors = buttonColors(containerColor = MaterialTheme.colorScheme.primary)
@ -59,6 +64,10 @@ fun SecondaryButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true
) {
Button(
@ -66,7 +75,7 @@ fun SecondaryButton(
modifier = modifier.then(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(outerPaddingValues)
),
enabled = enabled,
colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
@ -83,13 +92,17 @@ fun SecondaryButton(
fun NavigationButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) {
Button(
onClick = onClick,
modifier = modifier.then(
Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(outerPaddingValues)
),
colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
) {
@ -102,6 +115,10 @@ fun TertiaryButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true
) {
Button(
@ -109,7 +126,7 @@ fun TertiaryButton(
modifier = modifier.then(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(outerPaddingValues)
),
enabled = enabled,
elevation = ButtonDefaults.buttonElevation(0.dp, 0.dp, 0.dp),
@ -127,14 +144,18 @@ fun TertiaryButton(
fun DangerousButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) {
Button(
onClick = onClick,
modifier = modifier.then(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.padding(outerPaddingValues)
),
colors = buttonColors(containerColor = ZcashTheme.colors.dangerous)
) {

View File

@ -46,7 +46,7 @@ fun SwitchWithLabel(
)
Spacer(
modifier = Modifier
.width(ZcashTheme.paddings.padding)
.width(ZcashTheme.dimens.spacingDefault)
.constrainAs(spacer) {
top.linkTo(parent.top)
bottom.linkTo(parent.top)

View File

@ -22,10 +22,10 @@ fun Header(
) {
Text(
text = text,
textAlign = textAlign,
style = MaterialTheme.typography.headlineLarge,
color = color,
modifier = modifier
textAlign = textAlign,
modifier = modifier,
style = MaterialTheme.typography.headlineLarge,
)
}
@ -34,14 +34,14 @@ fun Body(
text: String,
modifier: Modifier = Modifier,
textAlign: TextAlign = TextAlign.Start,
color: Color = MaterialTheme.colorScheme.onBackground
color: Color = MaterialTheme.colorScheme.onBackground,
) {
Text(
text = text,
textAlign = textAlign,
style = MaterialTheme.typography.bodyLarge,
color = color,
modifier = modifier
textAlign = textAlign,
modifier = modifier,
style = MaterialTheme.typography.bodyLarge,
)
}
@ -49,14 +49,15 @@ fun Body(
fun Small(
text: String,
modifier: Modifier = Modifier,
textAlign: TextAlign = TextAlign.Start
textAlign: TextAlign = TextAlign.Start,
color: Color = MaterialTheme.colorScheme.onBackground,
) {
Text(
text = text,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground,
color = color,
textAlign = textAlign,
modifier = modifier,
textAlign = textAlign
style = MaterialTheme.typography.bodyMedium,
)
}
@ -81,7 +82,7 @@ fun ListHeader(
Text(
text = text,
style = ZcashTheme.typography.listItem,
color = MaterialTheme.colorScheme.onBackground,
color = ZcashTheme.colors.onBackgroundHeader,
modifier = modifier
)
}

View File

@ -1,5 +1,6 @@
package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TextField
@ -8,6 +9,7 @@ import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.input.KeyboardType
@Suppress("LongParameterList")
@ -23,7 +25,9 @@ fun FormTextField(
keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
colors: TextFieldColors = TextFieldDefaults.textFieldColors(
containerColor = Color.Transparent
)
),
keyboardActions: KeyboardActions = KeyboardActions.Default,
shape: Shape = TextFieldDefaults.filledShape
) {
TextField(
value = value,
@ -33,6 +37,8 @@ fun FormTextField(
colors = colors,
modifier = modifier,
leadingIcon = leadingIcon,
trailingIcon = trailingIcon
trailingIcon = trailingIcon,
keyboardActions = keyboardActions,
shape = shape
)
}

View File

@ -10,6 +10,7 @@ import androidx.compose.ui.unit.dp
@Immutable
data class Dimens(
// Default spacings:
val spacingNone: Dp,
val spacingXtiny: Dp,
val spacingTiny: Dp,
val spacingSmall: Dp,
@ -22,6 +23,7 @@ data class Dimens(
)
private val defaultDimens = Dimens(
spacingNone = 0.dp,
spacingXtiny = 2.dp,
spacingTiny = 4.dp,
spacingSmall = 8.dp,

View File

@ -11,7 +11,6 @@ import co.electriccoin.zcash.ui.design.theme.internal.LightColorPalette
import co.electriccoin.zcash.ui.design.theme.internal.LightExtendedColorPalette
import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedColors
import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedTypography
import co.electriccoin.zcash.ui.design.theme.internal.Paddings
import co.electriccoin.zcash.ui.design.theme.internal.Typography
@Composable
@ -57,10 +56,4 @@ object ZcashTheme {
val dimens: Dimens
@Composable
get() = LocalDimens.current
// TODO [#807]: [Design system] Remove deprecated Paddings class
// TODO [#807]: https://github.com/zcash/secant-android-wallet/issues/807
val paddings: Paddings
@Composable
get() = Paddings()
}

View File

@ -1,10 +0,0 @@
package co.electriccoin.zcash.ui.design.theme.internal
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
data class Paddings(
val paddingHalf: Dp = 8.dp,
val padding: Dp = 16.dp,
val paddingDouble: Dp = 32.dp
)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.update.view
package co.electriccoin.zcash.ui.screen.update.util
import android.app.Activity
import android.content.Context

View File

@ -0,0 +1,75 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.espresso.Espresso
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.fixture.UpdateInfoFixture
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Rule
import org.junit.Test
// Non-multiplatform tests that require interacting with the Android system (e.g. system back navigation)
// These don't have persistent state, so they are still unit tests.
class UpdateViewAndroidTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
private fun newTestSetup(updateInfo: UpdateInfo) = UpdateViewAndroidTestSetup(
composeTestRule,
updateInfo
).apply {
setDefaultContent()
}
@Test
@MediumTest
fun postpone_optional_update_test() {
val updateInfo = UpdateInfoFixture.new(
priority = AppUpdateChecker.Priority.LOW,
force = false,
appUpdateInfo = null,
state = UpdateState.Prepared
)
newTestSetup(updateInfo)
composeTestRule.onNodeWithText(getStringResource(R.string.update_header)).also {
it.assertExists()
}
Espresso.pressBack()
composeTestRule.onNodeWithText(getStringResource(R.string.update_header)).also {
it.assertDoesNotExist()
}
}
@Test
@MediumTest
fun postpone_force_update_test() {
val updateInfo = UpdateInfoFixture.new(
priority = AppUpdateChecker.Priority.HIGH,
force = true,
appUpdateInfo = null,
state = UpdateState.Prepared
)
newTestSetup(updateInfo)
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header)).also {
it.assertExists()
}
Espresso.pressBack()
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header)).also {
it.assertExists()
}
}
}

View File

@ -0,0 +1,29 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.update.WrapUpdate
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
class UpdateViewAndroidTestSetup(
private val composeTestRule: AndroidComposeTestRule<*, *>,
private val updateInfo: UpdateInfo
) {
@Composable
@Suppress("TestFunctionName")
fun DefaultContent() {
WrapUpdate(
composeTestRule.activity,
updateInfo
)
}
fun setDefaultContent() {
composeTestRule.setContent {
ZcashTheme {
DefaultContent()
}
}
}
}

View File

@ -6,7 +6,6 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
@ -108,10 +107,6 @@ class UpdateViewTest : UiTestPrerequisites() {
composeTestRule.clickLater()
assertEquals(0, testSetup.getOnLaterCount())
Espresso.pressBack()
assertEquals(0, testSetup.getOnLaterCount())
}
@Test

View File

@ -1,8 +1,8 @@
package co.electriccoin.zcash.ui.screen.about.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.build.gitSha
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
@ -57,9 +56,19 @@ fun About(
AboutTopAppBar(onBack = goBack)
}) { paddingValues ->
AboutMainContent(
paddingValues,
versionInfo,
configInfo
configInfo,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
}
@ -83,33 +92,33 @@ private fun AboutTopAppBar(onBack: () -> Unit) {
}
@Composable
fun AboutMainContent(paddingValues: PaddingValues, versionInfo: VersionInfo, configInfo: ConfigInfo) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(top = paddingValues.calculateTopPadding())
) {
fun AboutMainContent(
versionInfo: VersionInfo,
configInfo: ConfigInfo,
modifier: Modifier = Modifier
) {
Column(modifier) {
Icon(painterResource(id = R.drawable.ic_launcher_adaptive_foreground), contentDescription = null)
Text(stringResource(id = R.string.app_name))
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_version_header))
Body(stringResource(R.string.about_version_format, versionInfo.versionName, versionInfo.versionCode))
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_build_header))
Body(gitSha)
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
configInfo.configurationUpdatedAt?.let { updatedAt ->
Header(stringResource(id = R.string.about_build_configuration))
Body(updatedAt.toString())
}
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_legal_header))
Body(stringResource(id = R.string.about_legal_info))

View File

@ -12,9 +12,13 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDownCircle
@ -66,7 +70,12 @@ fun ComposablePreview() {
fun WalletAddresses(walletAddresses: WalletAddresses, onBack: () -> Unit) {
Column {
WalletDetailTopAppBar(onBack)
WalletDetailAddresses(walletAddresses)
WalletDetailAddresses(
walletAddresses,
Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
)
}
}
@ -96,8 +105,11 @@ private val BIG_INDICATOR_WIDTH = 24.dp
private val SMALL_INDICATOR_WIDTH = 16.dp
@Composable
private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
Column(Modifier.fillMaxWidth()) {
private fun WalletDetailAddresses(
walletAddresses: WalletAddresses,
modifier: Modifier = Modifier
) {
Column(modifier) {
Row(
Modifier
.fillMaxWidth()
@ -115,16 +127,36 @@ private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
ExpandableRow(
title = stringResource(R.string.wallet_address_unified),
content = walletAddresses.unified.address,
isInitiallyExpanded = true
isInitiallyExpanded = true,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
)
Box(Modifier.height(IntrinsicSize.Min)) {
Divider(modifier = Modifier.fillMaxHeight())
ListHeader(text = stringResource(R.string.wallet_address_header_includes))
ListHeader(
text = stringResource(R.string.wallet_address_header_includes),
modifier = Modifier.padding(all = ZcashTheme.dimens.spacingSmall)
)
}
SaplingAddress(walletAddresses.sapling.address)
TransparentAddress(walletAddresses.transparent.address)
SaplingAddress(
saplingAddress = walletAddresses.sapling.address,
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
)
TransparentAddress(
transparentAddress = walletAddresses.transparent.address,
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
)
}
}
}
@ -135,34 +167,44 @@ private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
// of row position will be needed.
@Composable
private fun SaplingAddress(saplingAddress: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
private fun SaplingAddress(
saplingAddress: String,
modifier: Modifier = Modifier
) {
Row(modifier) {
SmallIndicator(ZcashTheme.colors.addressHighlightSapling)
ExpandableRow(
title = stringResource(R.string.wallet_address_sapling),
content = saplingAddress,
isInitiallyExpanded = false
isInitiallyExpanded = false,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
)
}
}
@Composable
private fun TransparentAddress(transparentAddress: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
private fun TransparentAddress(
transparentAddress: String,
modifier: Modifier = Modifier
) {
Row(modifier) {
SmallIndicator(ZcashTheme.colors.addressHighlightTransparent)
ExpandableRow(
title = stringResource(R.string.wallet_address_transparent),
content = transparentAddress,
isInitiallyExpanded = false
isInitiallyExpanded = false,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
)
}
}
@ -177,11 +219,9 @@ private fun ExpandableRow(
var expandedState by rememberSaveable { mutableStateOf(isInitiallyExpanded) }
Column(
modifier
.fillMaxWidth()
.clickable {
expandedState = !expandedState
}
Modifier
.clickable { expandedState = !expandedState }
.then(modifier) // To have proper ripple effect
) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.defaultMinSize(minHeight = 48.dp)) {
ListItem(text = title)

View File

@ -201,7 +201,7 @@ private fun SeedPhrase(persistableWallet: PersistableWallet) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding)
.padding(vertical = ZcashTheme.dimens.spacingDefault)
) {
Body(stringResource(R.string.new_wallet_3_body_1))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
@ -248,7 +248,7 @@ private fun TestInProgress(
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding)
.padding(vertical = ZcashTheme.dimens.spacingDefault)
) {
splitSeedPhrase.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk ->
Row(Modifier.fillMaxWidth()) {

View File

@ -3,7 +3,7 @@
package co.electriccoin.zcash.ui.screen.backup.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@ -74,23 +74,29 @@ fun ShortNewWalletBackup(
}
) { paddingValues ->
ShortNewWalletMainContent(
paddingValues = paddingValues,
wallet = wallet,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
}
@Composable
private fun ShortNewWalletMainContent(
paddingValues: PaddingValues,
wallet: PersistableWallet,
modifier: Modifier = Modifier,
) {
Column(
Modifier
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
SeedPhrase(wallet)
}
@ -99,11 +105,7 @@ private fun ShortNewWalletMainContent(
@Composable
private fun SeedPhrase(persistableWallet: PersistableWallet) {
SecureScreen()
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding)
) {
Column {
Body(stringResource(R.string.new_wallet_short_body))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
}

View File

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
@ -145,7 +146,6 @@ fun Home(
)
}) { paddingValues ->
HomeMainContent(
paddingValues,
walletSnapshot,
transactionHistory,
isUpdateAvailable = isUpdateAvailable,
@ -154,6 +154,12 @@ fun Home(
isCircularProgressBarEnabled = isCircularProgressBarEnabled,
goReceive = goReceive,
goSend = goSend,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
}
@ -241,7 +247,7 @@ private fun HomeDrawer(
ModalDrawerSheet(
modifier = Modifier.testTag(HomeTag.DRAWER_MENU)
) {
Spacer(Modifier.height(12.dp))
Spacer(Modifier.height(ZcashTheme.dimens.spacingDefault))
NavigationDrawerItem(
icon = { Icon(Icons.Default.Password, contentDescription = null) },
label = { Text(stringResource(id = R.string.home_menu_seed_phrase)) },
@ -288,7 +294,6 @@ private fun HomeDrawer(
@Suppress("LongParameterList")
@Composable
private fun HomeMainContent(
paddingValues: PaddingValues,
walletSnapshot: WalletSnapshot,
transactionHistory: ImmutableList<CommonTransaction>,
isUpdateAvailable: Boolean,
@ -297,18 +302,41 @@ private fun HomeMainContent(
isCircularProgressBarEnabled: Boolean,
goReceive: () -> Unit,
goSend: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(top = paddingValues.calculateTopPadding())
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled, isCircularProgressBarEnabled)
Spacer(modifier = Modifier.height(24.dp))
if (isSyncing(walletSnapshot.status)) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Body(text = stringResource(id = R.string.home_information))
}
PrimaryButton(onClick = goReceive, text = stringResource(R.string.home_button_receive))
PrimaryButton(onClick = goSend, text = stringResource(R.string.home_button_send))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
PrimaryButton(
onClick = goSend,
text = stringResource(R.string.home_button_send),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
PrimaryButton(
onClick = goReceive,
text = stringResource(R.string.home_button_receive),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
History(transactionHistory)
@ -386,7 +414,7 @@ private fun Status(
.wrapContentSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
if (walletDisplayValues.zecAmountText.isNotEmpty()) {
HeaderWithZecIcon(amount = walletDisplayValues.zecAmountText)
@ -394,7 +422,7 @@ private fun Status(
if (isFiatConversionEnabled) {
Column(Modifier.testTag(HomeTag.FIAT_CONVERSION)) {
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
when (walletDisplayValues.fiatCurrencyAmountState) {
is FiatCurrencyConversionRateState.Current -> {
@ -415,7 +443,7 @@ private fun Status(
}
}
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
if (walletDisplayValues.statusText.isNotEmpty()) {
Body(

View File

@ -245,16 +245,16 @@ private fun Wallet(paddingValues: PaddingValues) {
Column(
Modifier
.padding(
start = ZcashTheme.paddings.padding,
end = ZcashTheme.paddings.padding,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding()
)
.fillMaxWidth()
) {
Header(
modifier = Modifier.padding(
top = ZcashTheme.paddings.padding,
bottom = ZcashTheme.paddings.paddingHalf
top = ZcashTheme.dimens.spacingDefault,
bottom = ZcashTheme.dimens.spacingSmall
),
text = stringResource(R.string.onboarding_4_header)
)
@ -280,8 +280,8 @@ private fun Content(
) {
Column(
Modifier.padding(
start = ZcashTheme.paddings.padding,
end = ZcashTheme.paddings.padding,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding()
)
) {

View File

@ -4,8 +4,12 @@ package co.electriccoin.zcash.ui.screen.onboarding.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
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.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu
@ -25,6 +29,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.PrimaryButton
@ -64,9 +70,14 @@ fun ShortOnboarding(
}
) { paddingValues ->
OnboardingMainContent(
paddingValues,
onImportWallet = onImportWallet,
onCreateWallet = onCreateWallet
onCreateWallet = onCreateWallet,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
}
@ -109,35 +120,47 @@ private fun DebugMenu(onFixtureWallet: () -> Unit) {
@Composable
private fun OnboardingMainContent(
paddingValues: PaddingValues,
onImportWallet: () -> Unit,
onCreateWallet: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier.padding(top = paddingValues.calculateTopPadding())
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Column(
Modifier
.padding(
start = ZcashTheme.paddings.padding,
end = ZcashTheme.paddings.padding,
bottom = paddingValues.calculateBottomPadding()
)
.fillMaxWidth()
) {
Header(
modifier = Modifier.padding(
top = ZcashTheme.paddings.padding,
bottom = ZcashTheme.paddings.paddingHalf
),
text = stringResource(R.string.onboarding_short_header)
Header(text = stringResource(R.string.onboarding_short_header))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingXlarge))
Body(text = stringResource(R.string.onboarding_short_information))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton(
onClick = onCreateWallet,
text = stringResource(R.string.onboarding_short_create_new_wallet),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
PrimaryButton(onCreateWallet, stringResource(R.string.onboarding_short_create_new_wallet), Modifier.fillMaxWidth())
TertiaryButton(
onImportWallet,
stringResource(R.string.onboarding_short_import_existing_wallet),
Modifier.fillMaxWidth()
)
TertiaryButton(
onImportWallet,
stringResource(R.string.onboarding_short_import_existing_wallet),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
}
)
}
}

View File

@ -2,6 +2,11 @@ package co.electriccoin.zcash.ui.screen.receive.view
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
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.material.icons.Icons
@ -26,8 +31,9 @@ import cash.z.ecc.android.sdk.model.WalletAddress
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.BrightenScreen
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.receive.util.AndroidQrCodeImageGenerator
@ -61,6 +67,10 @@ fun Receive(
ReceiveContents(
walletAddress = walletAddress,
onAddressDetails = onAddressDetails,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.padding(all = ZcashTheme.dimens.spacingDefault)
)
}
}
@ -90,21 +100,42 @@ private val DEFAULT_QR_CODE_SIZE = 320.dp
private fun ReceiveContents(
walletAddress: WalletAddress,
onAddressDetails: () -> Unit,
modifier: Modifier = Modifier
) {
Column(Modifier.verticalScroll(rememberScrollState())) {
Column(modifier) {
QrCode(data = walletAddress.address, DEFAULT_QR_CODE_SIZE, Modifier.align(Alignment.CenterHorizontally))
Body(text = stringResource(id = R.string.wallet_address_unified), Modifier.align(Alignment.CenterHorizontally))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(
text = stringResource(id = R.string.wallet_address_unified),
Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
// TODO [#163]: Ellipsize center of the string
// TODO [#163]: https://github.com/zcash/secant-android-wallet/issues/163
Text(
text = walletAddress.address,
style = MaterialTheme.typography.headlineLarge,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.align(Alignment.CenterHorizontally),
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
PrimaryButton(onClick = onAddressDetails, text = stringResource(id = R.string.receive_see_address_details))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton(
onClick = onAddressDetails,
text = stringResource(id = R.string.receive_see_address_details),
outerPaddingValues = PaddingValues(all = ZcashTheme.dimens.spacingNone)
)
}
}

View File

@ -5,10 +5,12 @@ package co.electriccoin.zcash.ui.screen.restore.view
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
@ -64,6 +66,7 @@ import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.CHIP_GRID_ROW_SIZE
import co.electriccoin.zcash.ui.design.component.Chip
import co.electriccoin.zcash.ui.design.component.FormTextField
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.NavigationButton
@ -123,7 +126,6 @@ fun PreviewRestoreComplete() {
}
}
// TODO [#409]: https://github.com/zcash/secant-android-wallet/issues/409
/**
* Note that the restore review doesn't allow the user to go back once the seed is entered correctly.
*
@ -182,6 +184,15 @@ fun RestoreWallet(
}
},
content = { paddingValues ->
val commonModifier = Modifier
// We intentionally set the bottom smaller to save space in case of the software keyboard is visible
.padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + dimens.spacingSmall,
start = dimens.spacingDefault,
end = dimens.spacingDefault
)
when (currentStage) {
RestoreStage.Seed -> {
SecureScreen()
@ -194,10 +205,7 @@ fun RestoreWallet(
parseResult = parseResult,
paste = paste,
goNext = { restoreState.goNext() },
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
modifier = commonModifier
)
}
RestoreStage.Birthday -> {
@ -206,10 +214,7 @@ fun RestoreWallet(
initialRestoreHeight = restoreHeight,
setRestoreHeight = setRestoreHeight,
onNext = { restoreState.goNext() },
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
modifier = commonModifier
)
}
RestoreStage.Complete -> {
@ -219,10 +224,7 @@ fun RestoreWallet(
RestoreComplete(
onComplete = onFinished,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
modifier = commonModifier
)
}
}
@ -281,12 +283,14 @@ private fun RestoreSeedMainContent(
val isSeedValid = userWordList.wordValidation().collectAsState(null).value is SeedPhraseValidation.Valid
Column(
modifier.then(Modifier.verticalScroll(scrollState))
Modifier
.fillMaxHeight()
.verticalScroll(scrollState)
.then(modifier)
) {
Body(
modifier = Modifier.padding(dimens.spacingDefault),
text = stringResource(id = R.string.restore_seed_instructions)
)
Body(text = stringResource(id = R.string.restore_seed_instructions))
Spacer(Modifier.height(dimens.spacingSmall))
ChipGridWithText(currentUserWordList)
@ -299,12 +303,17 @@ private fun RestoreSeedMainContent(
)
}
Spacer(modifier = Modifier.weight(MINIMAL_WEIGHT))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton(
onClick = goNext,
text = stringResource(id = R.string.restore_seed_button_restore),
enabled = isSeedValid
enabled = isSeedValid,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
@ -338,7 +347,17 @@ private fun RestoreSeedBottomBar(
// the user can hit the clear button
if (!isSeedValid) {
Column(modifier) {
Warn(parseResult)
Warn(
parseResult = parseResult,
modifier = Modifier
.fillMaxWidth()
// Note we don't set the top, as it's set by the confirm button above
.padding(
bottom = dimens.spacingDefault,
start = dimens.spacingDefault,
end = dimens.spacingDefault
)
)
Autocomplete(parseResult = parseResult, {
setTextState("")
userWordList.append(listOf(it))
@ -352,11 +371,7 @@ private fun RestoreSeedBottomBar(
private fun ChipGridWithText(
userWordList: ImmutableList<String>
) {
Column(
Modifier
.padding(start = 12.dp, end = 12.dp)
.testTag(RestoreTag.CHIP_LAYOUT)
) {
Column(Modifier.testTag(RestoreTag.CHIP_LAYOUT)) {
userWordList.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk ->
Row(Modifier.fillMaxWidth(), verticalAlignment = CenterVertically) {
val remainder = (chunk.size % CHIP_GRID_ROW_SIZE)
@ -452,11 +467,20 @@ private fun Autocomplete(
}
@Suppress("ModifierReused")
LazyRow(highlightModifier.testTag(RestoreTag.AUTOCOMPLETE_LAYOUT)) {
LazyRow(
modifier = highlightModifier.testTag(RestoreTag.AUTOCOMPLETE_LAYOUT),
// Note we don't set the top, as it's set by the confirm button above
// And we also set the bottom smaller, as the keyboard will be always visible
contentPadding = PaddingValues(
bottom = dimens.spacingDefault,
start = dimens.spacingDefault,
end = dimens.spacingSmall
)
) {
items(it) {
Chip(
text = it,
modifier = modifier
modifier = Modifier
.testTag(RestoreTag.AUTOCOMPLETE_ITEM)
.clickable { onSuggestionSelected(it) }
)
@ -466,12 +490,13 @@ private fun Autocomplete(
}
@Composable
private fun Warn(parseResult: ParseResult) {
private fun Warn(
parseResult: ParseResult,
modifier: Modifier = Modifier
) {
if (parseResult is ParseResult.Warn) {
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(dimens.spacingTiny),
modifier = modifier,
shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.secondary,
shadowElevation = 4.dp
@ -491,8 +516,8 @@ private fun Warn(parseResult: ParseResult) {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Suppress("LongMethod")
private fun RestoreBirthday(
zcashNetwork: ZcashNetwork,
initialRestoreHeight: BlockHeight?,
@ -503,12 +528,22 @@ private fun RestoreBirthday(
val (height, setHeight) = rememberSaveable {
mutableStateOf(initialRestoreHeight?.value?.toString() ?: "")
}
val scrollState = rememberScrollState()
Column(modifier.verticalScroll(scrollState)) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.then(modifier)
) {
Header(stringResource(R.string.restore_birthday_header))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Body(stringResource(R.string.restore_birthday_body))
TextField(
Spacer(modifier = Modifier.height(dimens.spacingDefault))
FormTextField(
value = height,
onValueChange = { heightString ->
val filteredHeightString = heightString.filter { it.isDigit() }
@ -528,8 +563,10 @@ private fun RestoreBirthday(
keyboardActions = KeyboardActions(onAny = {}),
shape = RoundedCornerShape(8.dp),
)
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
@ -543,28 +580,50 @@ private fun RestoreBirthday(
onNext()
},
text = stringResource(R.string.restore_birthday_button_restore),
enabled = isBirthdayValid
enabled = isBirthdayValid,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
TertiaryButton(
onClick = {
setRestoreHeight(null)
onNext()
},
text = stringResource(R.string.restore_birthday_button_skip)
text = stringResource(R.string.restore_birthday_button_skip),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
}
@Composable
private fun RestoreComplete(onComplete: () -> Unit, modifier: Modifier = Modifier) {
Column(modifier) {
private fun RestoreComplete(
onComplete: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.then(modifier)
) {
Header(stringResource(R.string.restore_complete_header))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Body(stringResource(R.string.restore_complete_info))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton(onComplete, stringResource(R.string.restore_button_see_wallet))
PrimaryButton(
onClick = onComplete,
text = stringResource(R.string.restore_button_see_wallet),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
}

View File

@ -14,8 +14,10 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@ -37,7 +39,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged
@ -47,19 +48,19 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.core.content.ContextCompat
import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.SecondaryButton
import co.electriccoin.zcash.ui.design.component.Small
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.scan.ScanTag
import co.electriccoin.zcash.ui.screen.scan.model.ScanState
@ -108,12 +109,20 @@ fun Scan(
snackbarHost = { SnackbarHost(snackbarHostState) }
) { paddingValues ->
ScanMainContent(
paddingValues,
onScanned,
onOpenSettings,
onBack,
onScanStateChanged,
snackbarHostState
snackbarHostState,
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding(),
start = ZcashTheme.dimens.spacingNone,
end = ZcashTheme.dimens.spacingNone
)
)
}
}
@ -121,41 +130,37 @@ fun Scan(
@Composable
fun ScanBottomItems(
scanState: ScanState,
onOpenSettings: () -> Unit
onOpenSettings: () -> Unit,
modifier: Modifier = Modifier
) {
Column(modifier = Modifier.fillMaxWidth()) {
Text(
Column(modifier) {
Body(
text = stringResource(id = R.string.scan_hint),
color = Color.White,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.padding(horizontal = 24.dp, vertical = 8.dp)
color = Color.White
)
Text(
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
Small(
text = when (scanState) {
ScanState.Permission -> stringResource(id = R.string.scan_state_permission)
ScanState.Scanning -> stringResource(id = R.string.scan_state_scanning)
ScanState.Failed -> stringResource(id = R.string.scan_state_failed)
},
color = Color.White,
fontSize = 16.sp,
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.padding(horizontal = 24.dp, vertical = 8.dp)
.testTag(ScanTag.TEXT_STATE)
modifier = Modifier.testTag(ScanTag.TEXT_STATE)
)
if (scanState == ScanState.Permission) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
SecondaryButton(
onClick = onOpenSettings,
text = stringResource(id = R.string.scan_settings_button),
modifier = Modifier
.padding(horizontal = 24.dp, vertical = 12.dp)
outerPaddingValues = PaddingValues(
vertical = ZcashTheme.dimens.spacingSmall,
horizontal = ZcashTheme.dimens.spacingNone
)
)
}
}
@ -183,12 +188,12 @@ private fun ScanTopAppBar(onBack: () -> Unit) {
@Suppress("MagicNumber", "LongMethod", "LongParameterList")
@Composable
private fun ScanMainContent(
paddingValues: PaddingValues,
onScanned: (String) -> Unit,
onOpenSettings: () -> Unit,
onBack: () -> Unit,
onScanStateChanged: (ScanState) -> Unit,
snackbarHostState: SnackbarHostState
snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier
) {
val context = LocalContext.current
@ -234,12 +239,7 @@ private fun ScanMainContent(
(framePossibleSize.value.width * 0.7).roundToInt()
}
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.padding(top = paddingValues.calculateTopPadding())
) {
ConstraintLayout(modifier) {
val (frame, bottomItems) = createRefs()
when (scanState) {
@ -292,7 +292,16 @@ private fun ScanMainContent(
}
Box(modifier = Modifier.constrainAs(bottomItems) { bottom.linkTo(parent.bottom) }) {
ScanBottomItems(scanState, onOpenSettings)
ScanBottomItems(
scanState = scanState,
onOpenSettings = onOpenSettings,
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = ZcashTheme.dimens.spacingDefault,
horizontal = ZcashTheme.dimens.spacingDefault
)
)
}
}
}

View File

@ -2,6 +2,9 @@ package co.electriccoin.zcash.ui.screen.seed.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@ -58,9 +61,14 @@ fun Seed(
SeedTopAppBar(onBack = onBack)
}) { paddingValues ->
SeedMainContent(
paddingValues,
persistableWallet = persistableWallet,
onCopyToClipboard = onCopyToClipboard
onCopyToClipboard = onCopyToClipboard,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
}
@ -85,19 +93,31 @@ private fun SeedTopAppBar(onBack: () -> Unit) {
@Composable
private fun SeedMainContent(
paddingValues: PaddingValues,
persistableWallet: PersistableWallet,
onCopyToClipboard: () -> Unit
onCopyToClipboard: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(top = paddingValues.calculateTopPadding())
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Body(stringResource(R.string.seed_body))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
TertiaryButton(onClick = onCopyToClipboard, text = stringResource(R.string.seed_copy))
TertiaryButton(
onClick = onCopyToClipboard,
text = stringResource(R.string.seed_copy),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
}
}

View File

@ -1,6 +1,7 @@
package co.electriccoin.zcash.ui.screen.send.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
@ -108,6 +109,10 @@ fun Send(
onQrScannerOpen = onQrScannerOpen,
hasCameraFeature = hasCameraFeature,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + dimens.spacingDefault,
@ -238,6 +243,8 @@ private fun SendForm(
mutableStateOf<Set<ZecSendExt.ZecSendValidation.Invalid.ValidationError>>(emptySet())
}
// TODO [#826]: SendArgumentsWrapper object properties validation
// TODO [#826]: https://github.com/zcash/secant-android-wallet/issues/826
if (sendArgumentsWrapper?.recipientAddress != null) {
recipientAddressString = sendArgumentsWrapper.recipientAddress
}
@ -248,11 +255,7 @@ private fun SendForm(
memoString = sendArgumentsWrapper.memo
}
Column(
modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
) {
Column(modifier) {
Header(
text = stringResource(id = R.string.send_balance, myBalance.toZecString()),
textAlign = TextAlign.Center,
@ -314,7 +317,11 @@ private fun SendForm(
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.fillMaxHeight(MINIMAL_WEIGHT))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
if (validation.isNotEmpty()) {
/*
@ -348,7 +355,8 @@ private fun SendForm(
text = stringResource(id = R.string.send_create),
// Check for ABBREVIATION_INDEX goes away once proper address validation is in place.
// For now, it just prevents a crash on the confirmation screen.
enabled = amountZecString.isNotBlank() && recipientAddressString.length > ABBREVIATION_INDEX
enabled = amountZecString.isNotBlank() && recipientAddressString.length > ABBREVIATION_INDEX,
outerPaddingValues = PaddingValues(top = dimens.spacingNone)
)
}
}
@ -359,14 +367,7 @@ private fun Confirmation(
onConfirmation: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Column(modifier) {
Body(
stringResource(
R.string.send_confirmation_amount_and_address_format,
@ -392,7 +393,8 @@ private fun Confirmation(
PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall),
onClick = onConfirmation,
text = stringResource(id = R.string.send_confirmation_button)
text = stringResource(id = R.string.send_confirmation_button),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
}
@ -402,14 +404,7 @@ private fun Sending(
zecSend: ZecSend,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Column(modifier) {
Header(
text = stringResource(
R.string.send_in_progress_amount_format,
@ -458,14 +453,7 @@ private fun SendSuccessful(
onDone: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Column(modifier) {
Header(
text = stringResource(R.string.send_successful_title),
textAlign = TextAlign.Center,
@ -496,7 +484,8 @@ private fun SendSuccessful(
PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall),
text = stringResource(R.string.send_successful_button),
onClick = onDone
onClick = onDone,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
}
@ -507,14 +496,7 @@ private fun SendFailure(
onDone: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Column(modifier) {
Header(
text = stringResource(R.string.send_failure_title),
textAlign = TextAlign.Center,
@ -545,7 +527,8 @@ private fun SendFailure(
PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall),
text = stringResource(R.string.send_failure_button),
onClick = onDone
onClick = onDone,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
}
}

View File

@ -85,7 +85,7 @@ fun Settings(
)
.padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = dimens.spacingDefault,
bottom = paddingValues.calculateTopPadding() + dimens.spacingDefault,
start = dimens.spacingDefault,
end = dimens.spacingDefault
)

View File

@ -1,9 +1,13 @@
package co.electriccoin.zcash.ui.screen.support.view
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
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.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Send
@ -17,7 +21,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
@ -26,6 +29,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.FormTextField
import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
@ -68,9 +73,14 @@ fun Support(
}
) { paddingValues ->
SupportMainContent(
paddingValues,
message,
setMessage
message = message,
setMessage = setMessage,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
if (isShowingDialog) {
@ -101,17 +111,24 @@ private fun SupportTopAppBar(onBack: () -> Unit) {
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun SupportMainContent(
paddingValues: PaddingValues,
message: String,
setMessage: (String) -> Unit
setMessage: (String) -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.padding(top = paddingValues.calculateTopPadding())
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
TextField(
Body(stringResource(id = R.string.support_information))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
FormTextField(
value = message,
onValueChange = setMessage,
modifier = Modifier
@ -119,7 +136,9 @@ private fun SupportMainContent(
label = { Text(text = stringResource(id = R.string.support_hint)) }
)
Text(stringResource(id = R.string.support_disclaimer))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Body(stringResource(id = R.string.support_disclaimer))
}
}

View File

@ -2,6 +2,7 @@ package co.electriccoin.zcash.ui.screen.update
import android.content.Context
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.compose.material3.SnackbarHostState
@ -28,7 +29,7 @@ internal fun MainActivity.WrapCheckForUpdate() {
@Composable
private fun WrapCheckForUpdate(activity: ComponentActivity) {
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382
// TODO [#403]: Manual testing of already implemented in-app update mechanisms
// TODO [#403]: https://github.com/zcash/secant-android-wallet/issues/403
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
@ -53,8 +54,9 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
}
}
@VisibleForTesting
@Composable
private fun WrapUpdate(
internal fun WrapUpdate(
activity: ComponentActivity,
inputUpdateInfo: UpdateInfo
) {
@ -85,6 +87,16 @@ private fun WrapUpdate(
}
}
val onLaterAction = {
if (!updateInfo.isForce && updateInfo.state != UpdateState.Running) {
viewModel.remindLater()
}
}
BackHandler {
onLaterAction()
}
Update(
snackbarHostState,
updateInfo,
@ -97,9 +109,7 @@ private fun WrapUpdate(
updateInfo.appUpdateInfo
)
},
onLater = {
viewModel.remindLater()
},
onLater = onLaterAction,
onReference = {
openPlayStoreAppPage(
activity.applicationContext,

View File

@ -1,6 +1,5 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -63,12 +62,6 @@ fun Update(
onLater: () -> Unit,
onReference: () -> Unit
) {
BackHandler(enabled = true) {
if (updateInfo.isForce) {
return@BackHandler
}
onLater()
}
Scaffold(
topBar = {
UpdateTopAppBar(updateInfo)
@ -80,13 +73,26 @@ fun Update(
UpdateBottomAppBar(
updateInfo,
onDownload,
onLater
onLater,
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = ZcashTheme.dimens.spacingDefault,
horizontal = ZcashTheme.dimens.spacingDefault
)
)
}
) { paddingValues ->
UpdateContentNormal(
paddingValues,
onReference
onReference,
modifier = Modifier
.fillMaxWidth()
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding(),
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
}
UpdateOverlayRunning(updateInfo)
@ -134,16 +140,16 @@ private fun UpdateTopAppBar(updateInfo: UpdateInfo) {
private fun UpdateBottomAppBar(
updateInfo: UpdateInfo,
onDownload: (state: UpdateState) -> Unit,
onLater: () -> Unit
onLater: () -> Unit,
modifier: Modifier = Modifier
) {
Column {
Column(modifier) {
PrimaryButton(
onClick = { onDownload(UpdateState.Running) },
text = stringResource(R.string.update_download_button),
modifier = Modifier
.fillMaxWidth()
.testTag(UpdateTag.BTN_DOWNLOAD),
enabled = updateInfo.state != UpdateState.Running
modifier = Modifier.testTag(UpdateTag.BTN_DOWNLOAD),
enabled = updateInfo.state != UpdateState.Running,
outerPaddingValues = PaddingValues(all = ZcashTheme.dimens.spacingNone)
)
TertiaryButton(
@ -157,23 +163,20 @@ private fun UpdateBottomAppBar(
}
}
),
modifier = Modifier
.fillMaxWidth()
.testTag(UpdateTag.BTN_LATER),
enabled = !updateInfo.isForce && updateInfo.state != UpdateState.Running
modifier = Modifier.testTag(UpdateTag.BTN_LATER),
enabled = !updateInfo.isForce && updateInfo.state != UpdateState.Running,
outerPaddingValues = PaddingValues(top = ZcashTheme.dimens.spacingSmall)
)
}
}
@Composable
private fun UpdateContentNormal(
paddingValues: PaddingValues,
onReference: () -> Unit
onReference: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxWidth()
.padding(top = paddingValues.calculateTopPadding()),
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
// TODO [#17]: This suppression and magic number will get replaced once we have real assets

View File

@ -2,6 +2,7 @@
<string name="home_menu_content_description">Open menu</string>
<string name="home_button_receive">Receive</string>
<string name="home_button_send">Send</string>
<string name="home_information">You wont be able to transfer funds until your wallet is finished syncing. Please keep your device plugged in and the app open.</string>
<string name="home_menu_seed_phrase">My secret phrase</string>
<string name="home_menu_settings">Settings</string>

View File

@ -20,6 +20,7 @@
<string name="onboarding_4_import_existing_wallet">Import an Existing Wallet</string>
<string name="onboarding_short_header">Its time to set up your no-frills wallet, powered by Zcash</string>
<string name="onboarding_short_information">We need to create a new wallet or restore an existing one. Select your path:</string>
<string name="onboarding_short_create_new_wallet">Create New Wallet</string>
<string name="onboarding_short_import_existing_wallet">Import an Existing Wallet</string>
</resources>

View File

@ -9,6 +9,7 @@
<string name="support_confirmation_explanation"><xliff:g id="app_name" example="Zcash">%1$s</xliff:g> is about to open your email app with a pre-filled message.\n\nBe sure to hit send within your email app.</string>
<!-- This is replaced by a resource overlay via app/build.gradle.kts -->
<string name="support_email_address"></string>
<string name="support_information">Please let us know about any problems you have had, or features you want to see in the future.</string>
<string name="support_disclaimer">Information provided is handled in accordance with our Privacy Policy.</string>
<string name="support_unable_to_open_email">Unable to launch email app.</string>
</resources>