[#1048] Seed Recovery Screen
[#1048] Seed Recovery Screen Open Seed Recovery from the Settings screen [#1014] Unify screen name with Figma [#1048] UI + logic + tests Improve click action effect on TopAppBar [#1049] Add Changelog Closes #1049 Update PR template
This commit is contained in:
parent
cf45a0ef34
commit
771dc114da
|
@ -10,7 +10,7 @@
|
||||||
- [ ] Add **automated tests** as appropriate
|
- [ ] Add **automated tests** as appropriate
|
||||||
- [ ] Update the [**manual tests**](../blob/main/docs/testing/manual_testing)[^2] as appropriate
|
- [ ] Update the [**manual tests**](../blob/main/docs/testing/manual_testing)[^2] as appropriate
|
||||||
- [ ] Check the **code coverage**[^3] report for the automated tests
|
- [ ] Check the **code coverage**[^3] report for the automated tests
|
||||||
- [ ] Update **documentation** as appropriate (e.g [README.md](../blob/main/README.md), and [**Architecture.md**](../blob/main/docs/Architecture.md), etc.)
|
- [ ] Update **documentation** as appropriate (e.g [**README.md**](../blob/main/README.md), [**Architecture.md**](../blob/main/docs/Architecture.md), [**CHANGELOG.md**](../blob/main/CHANGELOG.md), etc.)
|
||||||
- [ ] **Run the app** and try the changes
|
- [ ] **Run the app** and try the changes
|
||||||
- [ ] Pull in the latest changes from the **main** branch and **squash** your commits before assigning a reviewer[^4]
|
- [ ] Pull in the latest changes from the **main** branch and **squash** your commits before assigning a reviewer[^4]
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
- [ ] Perform an **ad hoc review**[^5]
|
- [ ] Perform an **ad hoc review**[^5]
|
||||||
- [ ] Review the **automated tests**
|
- [ ] Review the **automated tests**
|
||||||
- [ ] Review the **manual tests**
|
- [ ] Review the **manual tests**
|
||||||
- [ ] Review the **documentation**, [**README.md**](../blob/main/README.md), and [**Architecture.md**](../blob/main/docs/Architecture.md) as appropriate
|
- [ ] Review the **documentation**, [**README.md**](../blob/main/README.md), [**Architecture.md**](../blob/main/docs/Architecture.md), etc. as appropriate
|
||||||
- [ ] **Run the app** and try the changes[^6]
|
- [ ] **Run the app** and try the changes[^6]
|
||||||
|
|
||||||
[^1]: _Code often looks different when reviewing the diff in a browser, making it easier to spot potential bugs._
|
[^1]: _Code often looks different when reviewing the diff in a browser, making it easier to spot potential bugs._
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Changelog
|
||||||
|
All notable changes to this application will be documented in this file.
|
||||||
|
|
||||||
|
Please be aware that this changelog primarily focuses on user-related modifications, emphasizing changes that can
|
||||||
|
directly impact users rather than highlighting other crucial architectural updates.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this application adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The user interface for both the Recovery Seed screen within the New Wallet Screens flow and the one accessible from
|
||||||
|
Settings has been updated.
|
|
@ -1,13 +1,16 @@
|
||||||
package co.electriccoin.zcash.ui.design.component
|
package co.electriccoin.zcash.ui.design.component
|
||||||
|
|
||||||
import androidx.compose.foundation.text.ClickableText
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
@ -143,8 +146,14 @@ fun Reference(
|
||||||
textAlign: TextAlign = TextAlign.Start,
|
textAlign: TextAlign = TextAlign.Start,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
ClickableText(
|
Box(
|
||||||
text = AnnotatedString(text),
|
modifier = Modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner))
|
||||||
|
.clickable { onClick() }
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
style = MaterialTheme.typography.bodyLarge
|
style = MaterialTheme.typography.bodyLarge
|
||||||
.merge(
|
.merge(
|
||||||
TextStyle(
|
TextStyle(
|
||||||
|
@ -153,12 +162,10 @@ fun Reference(
|
||||||
textDecoration = TextDecoration.Underline
|
textDecoration = TextDecoration.Underline
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
onClick = {
|
|
||||||
onClick()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass amount of ZECs you want to display and the component appends ZEC symbol to it. We're using
|
* Pass amount of ZECs you want to display and the component appends ZEC symbol to it. We're using
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package co.electriccoin.zcash.ui.design.component
|
package co.electriccoin.zcash.ui.design.component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
|
@ -22,6 +29,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
@ -209,24 +217,25 @@ fun SmallTopAppBar(
|
||||||
},
|
},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
backText?.let {
|
backText?.let {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner))
|
||||||
|
.clickable { onBack?.run { onBack() } }
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
modifier = Modifier.padding(all = ZcashTheme.dimens.spacingDefault),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
IconButton(
|
Image(
|
||||||
onClick = if (onBack != null) {
|
|
||||||
onBack
|
|
||||||
} else {
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
contentDescription = backContentDescriptionText
|
contentDescription = backContentDescriptionText
|
||||||
)
|
)
|
||||||
}
|
Spacer(modifier = Modifier.size(size = ZcashTheme.dimens.spacingSmall))
|
||||||
Text(text = backText.uppercase())
|
Text(text = backText.uppercase())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions = hamburgerMenuActions ?: regularActions ?: {},
|
actions = hamburgerMenuActions ?: regularActions ?: {},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
@ -30,6 +30,7 @@ data class Dimens(
|
||||||
|
|
||||||
// TopAppBar:
|
// TopAppBar:
|
||||||
val topAppBarZcashLogoHeight: Dp,
|
val topAppBarZcashLogoHeight: Dp,
|
||||||
|
val topAppBarActionRippleCorner: Dp,
|
||||||
|
|
||||||
// In screen custom spacings:
|
// In screen custom spacings:
|
||||||
val inScreenZcashLogoHeight: Dp,
|
val inScreenZcashLogoHeight: Dp,
|
||||||
|
@ -52,6 +53,7 @@ private val defaultDimens = Dimens(
|
||||||
defaultButtonWidth = 230.dp,
|
defaultButtonWidth = 230.dp,
|
||||||
defaultButtonHeight = 50.dp,
|
defaultButtonHeight = 50.dp,
|
||||||
topAppBarZcashLogoHeight = 24.dp,
|
topAppBarZcashLogoHeight = 24.dp,
|
||||||
|
topAppBarActionRippleCorner = 28.dp,
|
||||||
inScreenZcashLogoHeight = 100.dp,
|
inScreenZcashLogoHeight = 100.dp,
|
||||||
inScreenZcashLogoWidth = 60.dp,
|
inScreenZcashLogoWidth = 60.dp,
|
||||||
inScreenZcashTextLogoHeight = 30.dp,
|
inScreenZcashTextLogoHeight = 30.dp,
|
||||||
|
|
|
@ -40,7 +40,7 @@ android {
|
||||||
"src/main/res/ui/request",
|
"src/main/res/ui/request",
|
||||||
"src/main/res/ui/restore",
|
"src/main/res/ui/restore",
|
||||||
"src/main/res/ui/scan",
|
"src/main/res/ui/scan",
|
||||||
"src/main/res/ui/seed",
|
"src/main/res/ui/seed_recovery",
|
||||||
"src/main/res/ui/send",
|
"src/main/res/ui/send",
|
||||||
"src/main/res/ui/settings",
|
"src/main/res/ui/settings",
|
||||||
"src/main/res/ui/support",
|
"src/main/res/ui/support",
|
||||||
|
|
|
@ -10,7 +10,6 @@ import androidx.test.filters.SmallTest
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.fixture.ConfigInfoFixture
|
import co.electriccoin.zcash.ui.fixture.ConfigInfoFixture
|
||||||
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
|
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
|
||||||
import co.electriccoin.zcash.ui.screen.about.AboutTag
|
|
||||||
import co.electriccoin.zcash.ui.test.getStringResource
|
import co.electriccoin.zcash.ui.test.getStringResource
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.compose.ui.test.performScrollTo
|
||||||
import androidx.test.filters.MediumTest
|
import androidx.test.filters.MediumTest
|
||||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
|
||||||
import co.electriccoin.zcash.ui.design.component.CommonTag
|
import co.electriccoin.zcash.ui.design.component.CommonTag
|
||||||
import co.electriccoin.zcash.ui.test.getStringResource
|
import co.electriccoin.zcash.ui.test.getStringResource
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
@ -34,7 +35,7 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
|
||||||
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
assertEquals(0, testSetup.getOnCompleteCount())
|
assertEquals(0, testSetup.getOnCompleteCount())
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_copy)).also {
|
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_recovery_copy)).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag(NewWalletRecoveryTag.WALLET_BIRTHDAY).also {
|
composeTestRule.onNodeWithTag(WALLET_BIRTHDAY).also {
|
||||||
it.performScrollTo()
|
it.performScrollTo()
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,7 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
|
||||||
|
|
||||||
assertEquals(0, testSetup.getOnSeedCopyCount())
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_copy)).also { menuButton ->
|
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_recovery_copy)).also { menuButton ->
|
||||||
menuButton.performClick()
|
menuButton.performClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ class NewWalletRecoveryViewTest : UiTestPrerequisites() {
|
||||||
|
|
||||||
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag(NewWalletRecoveryTag.WALLET_BIRTHDAY).also {
|
composeTestRule.onNodeWithTag(WALLET_BIRTHDAY).also {
|
||||||
it.performScrollTo()
|
it.performScrollTo()
|
||||||
it.performClick()
|
it.performClick()
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,7 +158,7 @@ class RestoreViewAndroidTest : UiTestPrerequisites() {
|
||||||
private fun copyToClipboard(context: Context, text: String) {
|
private fun copyToClipboard(context: Context, text: String) {
|
||||||
val clipboardManager = context.getSystemService(ClipboardManager::class.java)
|
val clipboardManager = context.getSystemService(ClipboardManager::class.java)
|
||||||
val data = ClipData.newPlainText(
|
val data = ClipData.newPlainText(
|
||||||
context.getString(R.string.new_wallet_seed_clipboard_tag),
|
context.getString(R.string.new_wallet_recovery_seed_clipboard_tag),
|
||||||
text
|
text
|
||||||
)
|
)
|
||||||
clipboardManager.setPrimaryClip(data)
|
clipboardManager.setPrimaryClip(data)
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
package co.electriccoin.zcash.ui.screen.seed.view
|
|
||||||
|
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
|
||||||
import androidx.compose.ui.test.junit4.createComposeRule
|
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
|
||||||
import androidx.compose.ui.test.performClick
|
|
||||||
import androidx.compose.ui.test.performScrollTo
|
|
||||||
import androidx.test.filters.MediumTest
|
|
||||||
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
|
||||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
|
||||||
import co.electriccoin.zcash.ui.R
|
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
|
||||||
import co.electriccoin.zcash.ui.test.getStringResource
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.test.runTest
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
class SeedViewTest : UiTestPrerequisites() {
|
|
||||||
@get:Rule
|
|
||||||
val composeTestRule = createComposeRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@MediumTest
|
|
||||||
fun back() = runTest {
|
|
||||||
val testSetup = TestSetup(composeTestRule)
|
|
||||||
|
|
||||||
assertEquals(0, testSetup.getOnBackCount())
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.seed_back_content_description)).also {
|
|
||||||
it.performClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(1, testSetup.getOnBackCount())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@MediumTest
|
|
||||||
fun copyToClipboard() = runTest {
|
|
||||||
val testSetup = TestSetup(composeTestRule)
|
|
||||||
|
|
||||||
assertEquals(0, testSetup.getCopyToClipboardCount())
|
|
||||||
|
|
||||||
composeTestRule.onNodeWithText(getStringResource(R.string.seed_copy)).also {
|
|
||||||
it.performScrollTo()
|
|
||||||
it.performClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(1, testSetup.getCopyToClipboardCount())
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestSetup(private val composeTestRule: ComposeContentTestRule) {
|
|
||||||
|
|
||||||
private val onBackCount = AtomicInteger(0)
|
|
||||||
private val onCopyToClipboardCount = AtomicInteger(0)
|
|
||||||
|
|
||||||
fun getOnBackCount(): Int {
|
|
||||||
composeTestRule.waitForIdle()
|
|
||||||
return onBackCount.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCopyToClipboardCount(): Int {
|
|
||||||
composeTestRule.waitForIdle()
|
|
||||||
return onCopyToClipboardCount.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
composeTestRule.setContent {
|
|
||||||
ZcashTheme {
|
|
||||||
Seed(
|
|
||||||
PersistableWalletFixture.new(),
|
|
||||||
onBack = {
|
|
||||||
onBackCount.incrementAndGet()
|
|
||||||
},
|
|
||||||
onCopyToClipboard = {
|
|
||||||
onCopyToClipboardCount.incrementAndGet()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.seedrecovery.view
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
|
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
||||||
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
class SeedRecoveryBackupTestSetup(
|
||||||
|
private val composeTestRule: ComposeContentTestRule,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val onSeedCopyCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
private val onBirthdayCopyCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
private val onCompleteCallbackCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
private val onBackCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
fun getOnSeedCopyCount(): Int {
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
return onSeedCopyCount.get()
|
||||||
|
}
|
||||||
|
fun getOnBirthdayCopyCount(): Int {
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
return onBirthdayCopyCount.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOnCompleteCount(): Int {
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
return onCompleteCallbackCount.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOnBackCount(): Int {
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
return onBackCount.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Suppress("TestFunctionName")
|
||||||
|
fun DefaultContent() {
|
||||||
|
ZcashTheme {
|
||||||
|
SeedRecovery(
|
||||||
|
PersistableWalletFixture.new(),
|
||||||
|
onBack = { onBackCount.incrementAndGet() },
|
||||||
|
onSeedCopy = { onSeedCopyCount.incrementAndGet() },
|
||||||
|
onBirthdayCopy = { onBirthdayCopyCount.incrementAndGet() },
|
||||||
|
onDone = { onCompleteCallbackCount.incrementAndGet() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDefaultContent() {
|
||||||
|
composeTestRule.setContent {
|
||||||
|
DefaultContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.seedrecovery.view
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.test.performScrollTo
|
||||||
|
import androidx.test.filters.MediumTest
|
||||||
|
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||||
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
|
||||||
|
import co.electriccoin.zcash.ui.design.component.CommonTag
|
||||||
|
import co.electriccoin.zcash.ui.test.getStringResource
|
||||||
|
import org.junit.Rule
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class SeedRecoveryViewTest : UiTestPrerequisites() {
|
||||||
|
@get:Rule
|
||||||
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
|
private fun newTestSetup(): SeedRecoveryBackupTestSetup {
|
||||||
|
return SeedRecoveryBackupTestSetup(composeTestRule).apply {
|
||||||
|
setDefaultContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun default_ui_state_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnCompleteCount())
|
||||||
|
assertEquals(0, testSetup.getOnBackCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.seed_recovery_back_content_description))
|
||||||
|
.also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_copy)).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithContentDescription(
|
||||||
|
label = getStringResource(R.string.zcash_logo_content_description)
|
||||||
|
).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_header)).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_description)).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithTag(CommonTag.CHIP_LAYOUT).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithTag(WALLET_BIRTHDAY).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_button_finished).uppercase())
|
||||||
|
.also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnCompleteCount())
|
||||||
|
assertEquals(0, testSetup.getOnBackCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun back_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnBackCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.seed_recovery_back_content_description))
|
||||||
|
.also {
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnBackCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun copy_seed_to_clipboard_from_app_bar_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.seed_recovery_copy)).also { menuButton ->
|
||||||
|
menuButton.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnSeedCopyCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun copy_seed_to_clipboard_content_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithTag(CommonTag.CHIP_LAYOUT).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnSeedCopyCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun copy_birthday_to_clipboard_content_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithTag(WALLET_BIRTHDAY).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnBirthdayCopyCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun click_finish_test() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnCompleteCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(
|
||||||
|
text = getStringResource(R.string.seed_recovery_button_finished),
|
||||||
|
ignoreCase = true
|
||||||
|
).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnSeedCopyCount())
|
||||||
|
assertEquals(0, testSetup.getOnBirthdayCopyCount())
|
||||||
|
assertEquals(1, testSetup.getOnCompleteCount())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package co.electriccoin.zcash.ui.screen.seed.view
|
package co.electriccoin.zcash.ui.screen.seedrecovery.view
|
||||||
|
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
|
@ -14,31 +14,38 @@ import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class SeedViewSecuredScreenTest : UiTestPrerequisites() {
|
class SeedRecoveryViewsSecuredScreenTest : UiTestPrerequisites() {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeTestRule = createComposeRule()
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
|
private fun newTestSetup() =
|
||||||
|
TestSetup(composeTestRule).apply {
|
||||||
|
setContentView()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun acquireScreenSecurity() = runTest {
|
fun acquireScreenSecurity() = runTest {
|
||||||
val testSetup = TestSetup(composeTestRule)
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
assertEquals(1, testSetup.getSecureScreenCount())
|
assertEquals(1, testSetup.getSecureScreenCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
private class TestSetup(private val composeTestRule: ComposeContentTestRule) {
|
||||||
private val screenSecurity = ScreenSecurity()
|
private val screenSecurity = ScreenSecurity()
|
||||||
|
|
||||||
fun getSecureScreenCount() = screenSecurity.referenceCount.value
|
fun getSecureScreenCount() = screenSecurity.referenceCount.value
|
||||||
|
|
||||||
init {
|
fun setContentView() {
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
CompositionLocalProvider(LocalScreenSecurity provides screenSecurity) {
|
CompositionLocalProvider(LocalScreenSecurity provides screenSecurity) {
|
||||||
ZcashTheme {
|
ZcashTheme {
|
||||||
Seed(
|
SeedRecovery(
|
||||||
persistableWallet = PersistableWalletFixture.new(),
|
PersistableWalletFixture.new(),
|
||||||
onBack = {},
|
onBack = {},
|
||||||
onCopyToClipboard = {}
|
onSeedCopy = {},
|
||||||
|
onBirthdayCopy = {},
|
||||||
|
onDone = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -85,7 +85,7 @@ class SettingsViewTestSetup(
|
||||||
onBack = {
|
onBack = {
|
||||||
onBackCount.incrementAndGet()
|
onBackCount.incrementAndGet()
|
||||||
},
|
},
|
||||||
onBackup = {
|
onSeedRecovery = {
|
||||||
onBackupCount.incrementAndGet()
|
onBackupCount.incrementAndGet()
|
||||||
},
|
},
|
||||||
onDocumentation = {
|
onDocumentation = {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import co.electriccoin.zcash.ui.NavigationTargets.HOME
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.RECEIVE
|
import co.electriccoin.zcash.ui.NavigationTargets.RECEIVE
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.SCAN
|
import co.electriccoin.zcash.ui.NavigationTargets.SCAN
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.SEED
|
import co.electriccoin.zcash.ui.NavigationTargets.SEED_RECOVERY
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.SEND
|
import co.electriccoin.zcash.ui.NavigationTargets.SEND
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
|
import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
|
import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT
|
||||||
|
@ -33,7 +33,7 @@ import co.electriccoin.zcash.ui.screen.home.WrapHome
|
||||||
import co.electriccoin.zcash.ui.screen.receive.WrapReceive
|
import co.electriccoin.zcash.ui.screen.receive.WrapReceive
|
||||||
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
||||||
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
|
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
|
||||||
import co.electriccoin.zcash.ui.screen.seed.WrapSeed
|
import co.electriccoin.zcash.ui.screen.seedrecovery.WrapSeedRecovery
|
||||||
import co.electriccoin.zcash.ui.screen.send.WrapSend
|
import co.electriccoin.zcash.ui.screen.send.WrapSend
|
||||||
import co.electriccoin.zcash.ui.screen.send.model.SendArgumentsWrapper
|
import co.electriccoin.zcash.ui.screen.send.model.SendArgumentsWrapper
|
||||||
import co.electriccoin.zcash.ui.screen.settings.WrapSettings
|
import co.electriccoin.zcash.ui.screen.settings.WrapSettings
|
||||||
|
@ -55,7 +55,7 @@ internal fun MainActivity.Navigation() {
|
||||||
goAbout = { navController.navigateJustOnce(ABOUT) },
|
goAbout = { navController.navigateJustOnce(ABOUT) },
|
||||||
goHistory = { navController.navigateJustOnce(HISTORY) },
|
goHistory = { navController.navigateJustOnce(HISTORY) },
|
||||||
goReceive = { navController.navigateJustOnce(RECEIVE) },
|
goReceive = { navController.navigateJustOnce(RECEIVE) },
|
||||||
goSeedPhrase = { navController.navigateJustOnce(SEED) },
|
goSeedPhrase = { navController.navigateJustOnce(SEED_RECOVERY) },
|
||||||
goSend = { navController.navigateJustOnce(SEND) },
|
goSend = { navController.navigateJustOnce(SEND) },
|
||||||
goSettings = { navController.navigateJustOnce(SETTINGS) },
|
goSettings = { navController.navigateJustOnce(SETTINGS) },
|
||||||
goSupport = { navController.navigateJustOnce(SUPPORT) },
|
goSupport = { navController.navigateJustOnce(SUPPORT) },
|
||||||
|
@ -82,13 +82,19 @@ internal fun MainActivity.Navigation() {
|
||||||
},
|
},
|
||||||
goExportPrivateData = {
|
goExportPrivateData = {
|
||||||
navController.navigateJustOnce(EXPORT_PRIVATE_DATA)
|
navController.navigateJustOnce(EXPORT_PRIVATE_DATA)
|
||||||
|
},
|
||||||
|
goSeedRecovery = {
|
||||||
|
navController.navigateJustOnce(SEED_RECOVERY)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(SEED) {
|
composable(SEED_RECOVERY) {
|
||||||
WrapSeed(
|
WrapSeedRecovery(
|
||||||
goBack = {
|
goBack = {
|
||||||
navController.popBackStackJustOnce(SEED)
|
navController.popBackStackJustOnce(SEED_RECOVERY)
|
||||||
|
},
|
||||||
|
onDone = {
|
||||||
|
navController.popBackStackJustOnce(SEED_RECOVERY)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -193,7 +199,7 @@ object NavigationTargets {
|
||||||
const val RECEIVE = "receive"
|
const val RECEIVE = "receive"
|
||||||
const val REQUEST = "request"
|
const val REQUEST = "request"
|
||||||
const val SCAN = "scan"
|
const val SCAN = "scan"
|
||||||
const val SEED = "seed"
|
const val SEED_RECOVERY = "seed_recovery"
|
||||||
const val SEND = "send"
|
const val SEND = "send"
|
||||||
const val SETTINGS = "settings"
|
const val SETTINGS = "settings"
|
||||||
const val SUPPORT = "support"
|
const val SUPPORT = "support"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package co.electriccoin.zcash.ui.common.test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are only used for automated testing.
|
||||||
|
*/
|
||||||
|
object CommonTag {
|
||||||
|
const val WALLET_BIRTHDAY = "wallet_birthday"
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package co.electriccoin.zcash.ui.screen.about
|
package co.electriccoin.zcash.ui.screen.about.view
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are only used for automated testing.
|
* These are only used for automated testing.
|
|
@ -38,7 +38,6 @@ import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import co.electriccoin.zcash.ui.fixture.ConfigInfoFixture
|
import co.electriccoin.zcash.ui.fixture.ConfigInfoFixture
|
||||||
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
|
import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
|
||||||
import co.electriccoin.zcash.ui.screen.about.AboutTag
|
|
||||||
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
|
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
|
||||||
|
|
||||||
@Preview("About")
|
@Preview("About")
|
||||||
|
|
|
@ -16,45 +16,28 @@ fun MainActivity.WrapNewWalletRecovery(
|
||||||
WrapNewWalletRecovery(this, persistableWallet, onBackupComplete)
|
WrapNewWalletRecovery(this, persistableWallet, onBackupComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This layer of indirection allows for activity re-creation tests
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun WrapNewWalletRecovery(
|
private fun WrapNewWalletRecovery(
|
||||||
activity: ComponentActivity,
|
activity: ComponentActivity,
|
||||||
persistableWallet: PersistableWallet,
|
persistableWallet: PersistableWallet,
|
||||||
onBackupComplete: () -> Unit
|
onBackupComplete: () -> Unit
|
||||||
) {
|
) {
|
||||||
WrapNewWalletRecovery(
|
NewWalletRecovery(
|
||||||
persistableWallet,
|
persistableWallet,
|
||||||
onSeedCopyToClipboard = {
|
onSeedCopy = {
|
||||||
ClipboardManagerUtil.copyToClipboard(
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
activity.applicationContext,
|
activity.applicationContext,
|
||||||
activity.getString(R.string.new_wallet_seed_clipboard_tag),
|
activity.getString(R.string.new_wallet_recovery_seed_clipboard_tag),
|
||||||
persistableWallet.seedPhrase.joinToString()
|
persistableWallet.seedPhrase.joinToString()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onBirthdayCopyToClipboard = {
|
onBirthdayCopy = {
|
||||||
ClipboardManagerUtil.copyToClipboard(
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
activity.applicationContext,
|
activity.applicationContext,
|
||||||
activity.getString(R.string.new_wallet_birthday_clipboard_tag),
|
activity.getString(R.string.new_wallet_recovery_birthday_clipboard_tag),
|
||||||
persistableWallet.birthday?.value.toString()
|
persistableWallet.birthday?.value.toString()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onNewWalletComplete = onBackupComplete
|
onComplete = onBackupComplete
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This extra layer of indirection allows unit tests to validate the screen state retention
|
|
||||||
@Composable
|
|
||||||
private fun WrapNewWalletRecovery(
|
|
||||||
persistableWallet: PersistableWallet,
|
|
||||||
onSeedCopyToClipboard: () -> Unit,
|
|
||||||
onBirthdayCopyToClipboard: () -> Unit,
|
|
||||||
onNewWalletComplete: () -> Unit
|
|
||||||
) {
|
|
||||||
NewWalletRecovery(
|
|
||||||
persistableWallet,
|
|
||||||
onSeedCopy = onSeedCopyToClipboard,
|
|
||||||
onBirthdayCopy = onBirthdayCopyToClipboard,
|
|
||||||
onComplete = onNewWalletComplete,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
@file:Suppress("TooManyFunctions")
|
|
||||||
|
|
||||||
package co.electriccoin.zcash.ui.screen.newwalletrecovery.view
|
package co.electriccoin.zcash.ui.screen.newwalletrecovery.view
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
@ -32,6 +30,7 @@ import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.common.SecureScreen
|
import co.electriccoin.zcash.ui.common.SecureScreen
|
||||||
import co.electriccoin.zcash.ui.common.shouldSecureScreen
|
import co.electriccoin.zcash.ui.common.shouldSecureScreen
|
||||||
|
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
|
||||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||||
import co.electriccoin.zcash.ui.design.component.BodySmall
|
import co.electriccoin.zcash.ui.design.component.BodySmall
|
||||||
import co.electriccoin.zcash.ui.design.component.ChipGrid
|
import co.electriccoin.zcash.ui.design.component.ChipGrid
|
||||||
|
@ -41,7 +40,6 @@ import co.electriccoin.zcash.ui.design.component.Reference
|
||||||
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||||
import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle
|
import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import co.electriccoin.zcash.ui.screen.newwalletrecovery.view.NewWalletRecoveryTag.WALLET_BIRTHDAY
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
@Preview(name = "NewWalletRecovery", device = Devices.PIXEL_4)
|
@Preview(name = "NewWalletRecovery", device = Devices.PIXEL_4)
|
||||||
|
@ -115,7 +113,7 @@ private fun NewWalletRecoveryCopyToBufferMenuItem(
|
||||||
onCopyToClipboard: () -> Unit,
|
onCopyToClipboard: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Reference(
|
Reference(
|
||||||
text = stringResource(id = R.string.new_wallet_copy),
|
text = stringResource(id = R.string.new_wallet_recovery_copy),
|
||||||
onClick = onCopyToClipboard,
|
onClick = onCopyToClipboard,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
modifier = modifier.then(
|
modifier = modifier.then(
|
||||||
|
@ -224,7 +222,6 @@ private fun NewWalletRecoverySeedPhrase(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NewWalletRecoveryBottomNav(
|
private fun NewWalletRecoveryBottomNav(
|
||||||
onComplete: () -> Unit,
|
onComplete: () -> Unit,
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
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
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
|
||||||
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
|
||||||
import co.electriccoin.zcash.ui.R
|
|
||||||
import co.electriccoin.zcash.ui.common.SecureScreen
|
|
||||||
import co.electriccoin.zcash.ui.common.shouldSecureScreen
|
|
||||||
import co.electriccoin.zcash.ui.design.component.Body
|
|
||||||
import co.electriccoin.zcash.ui.design.component.ChipGrid
|
|
||||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
|
||||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
|
|
||||||
@Preview("Seed")
|
|
||||||
@Composable
|
|
||||||
private fun PreviewSeed() {
|
|
||||||
ZcashTheme(forceDarkMode = false) {
|
|
||||||
GradientSurface {
|
|
||||||
Seed(
|
|
||||||
persistableWallet = PersistableWalletFixture.new(),
|
|
||||||
onBack = {},
|
|
||||||
onCopyToClipboard = {}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note we have some things to determine regarding locking of the secrets for persistableWallet
|
|
||||||
* (e.g. seed phrase and spending keys) which should require additional authorization to view.
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun Seed(
|
|
||||||
persistableWallet: PersistableWallet,
|
|
||||||
onBack: () -> Unit,
|
|
||||||
onCopyToClipboard: () -> Unit
|
|
||||||
) {
|
|
||||||
if (shouldSecureScreen) {
|
|
||||||
SecureScreen()
|
|
||||||
}
|
|
||||||
Scaffold(topBar = {
|
|
||||||
SeedTopAppBar(onBack = onBack)
|
|
||||||
}) { paddingValues ->
|
|
||||||
SeedMainContent(
|
|
||||||
persistableWallet = persistableWallet,
|
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
private fun SeedTopAppBar(onBack: () -> Unit) {
|
|
||||||
TopAppBar(
|
|
||||||
title = { Text(text = stringResource(id = R.string.seed_title)) },
|
|
||||||
navigationIcon = {
|
|
||||||
IconButton(
|
|
||||||
onClick = onBack
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.ArrowBack,
|
|
||||||
contentDescription = stringResource(R.string.seed_back_content_description)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun SeedMainContent(
|
|
||||||
persistableWallet: PersistableWallet,
|
|
||||||
onCopyToClipboard: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
Modifier
|
|
||||||
.fillMaxHeight()
|
|
||||||
.verticalScroll(
|
|
||||||
rememberScrollState()
|
|
||||||
)
|
|
||||||
.then(modifier)
|
|
||||||
) {
|
|
||||||
Body(stringResource(R.string.seed_body))
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
|
||||||
|
|
||||||
ChipGrid(
|
|
||||||
wordList = persistableWallet.seedPhrase.split.toPersistentList(),
|
|
||||||
onGridClick = {}
|
|
||||||
)
|
|
||||||
|
|
||||||
TertiaryButton(
|
|
||||||
onClick = onCopyToClipboard,
|
|
||||||
text = stringResource(R.string.seed_copy),
|
|
||||||
outerPaddingValues = PaddingValues(
|
|
||||||
horizontal = ZcashTheme.dimens.spacingNone,
|
|
||||||
vertical = ZcashTheme.dimens.spacingSmall
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
@file:Suppress("ktlint:filename")
|
package co.electriccoin.zcash.ui.screen.seedrecovery
|
||||||
|
|
||||||
package co.electriccoin.zcash.ui.screen.seed
|
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
@ -11,19 +9,21 @@ import co.electriccoin.zcash.ui.MainActivity
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
import co.electriccoin.zcash.ui.screen.seed.view.Seed
|
import co.electriccoin.zcash.ui.screen.seedrecovery.view.SeedRecovery
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MainActivity.WrapSeed(
|
internal fun MainActivity.WrapSeedRecovery(
|
||||||
goBack: () -> Unit
|
goBack: () -> Unit,
|
||||||
|
onDone: () -> Unit
|
||||||
) {
|
) {
|
||||||
WrapSeed(this, goBack)
|
WrapSeedRecovery(this, goBack, onDone)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun WrapSeed(
|
private fun WrapSeedRecovery(
|
||||||
activity: ComponentActivity,
|
activity: ComponentActivity,
|
||||||
goBack: () -> Unit
|
goBack: () -> Unit,
|
||||||
|
onDone: () -> Unit
|
||||||
) {
|
) {
|
||||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
@ -40,16 +40,24 @@ private fun WrapSeed(
|
||||||
if (null == synchronizer || null == persistableWallet) {
|
if (null == synchronizer || null == persistableWallet) {
|
||||||
// Display loading indicator
|
// Display loading indicator
|
||||||
} else {
|
} else {
|
||||||
Seed(
|
SeedRecovery(
|
||||||
persistableWallet = persistableWallet,
|
persistableWallet,
|
||||||
onBack = goBack,
|
onBack = goBack,
|
||||||
onCopyToClipboard = {
|
onSeedCopy = {
|
||||||
ClipboardManagerUtil.copyToClipboard(
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
activity.applicationContext,
|
activity.applicationContext,
|
||||||
activity.getString(R.string.new_wallet_seed_clipboard_tag),
|
activity.getString(R.string.seed_recovery_seed_clipboard_tag),
|
||||||
persistableWallet.seedPhrase.joinToString()
|
persistableWallet.seedPhrase.joinToString()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
onBirthdayCopy = {
|
||||||
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
|
activity.applicationContext,
|
||||||
|
activity.getString(R.string.seed_recovery_birthday_clipboard_tag),
|
||||||
|
persistableWallet.birthday?.value.toString()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDone = onDone
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,244 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.seedrecovery.view
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.basicMarquee
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
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.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Devices
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||||
|
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
||||||
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.common.SecureScreen
|
||||||
|
import co.electriccoin.zcash.ui.common.shouldSecureScreen
|
||||||
|
import co.electriccoin.zcash.ui.common.test.CommonTag.WALLET_BIRTHDAY
|
||||||
|
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||||
|
import co.electriccoin.zcash.ui.design.component.BodySmall
|
||||||
|
import co.electriccoin.zcash.ui.design.component.ChipGrid
|
||||||
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||||
|
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||||
|
import co.electriccoin.zcash.ui.design.component.Reference
|
||||||
|
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||||
|
import co.electriccoin.zcash.ui.design.component.TopScreenLogoTitle
|
||||||
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
|
@Preview(name = "SeedRecovery", device = Devices.PIXEL_4)
|
||||||
|
@Composable
|
||||||
|
private fun ComposablePreview() {
|
||||||
|
ZcashTheme(forceDarkMode = false) {
|
||||||
|
GradientSurface {
|
||||||
|
SeedRecovery(
|
||||||
|
PersistableWalletFixture.new(),
|
||||||
|
onBack = {},
|
||||||
|
onBirthdayCopy = {},
|
||||||
|
onDone = {},
|
||||||
|
onSeedCopy = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO [#998]: Check and enhance screen dark mode
|
||||||
|
// TODO [#998]: https://github.com/Electric-Coin-Company/zashi-android/issues/998
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param onDone Callback when the user has confirmed viewing the seed phrase.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SeedRecovery(
|
||||||
|
wallet: PersistableWallet,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
onBirthdayCopy: () -> Unit,
|
||||||
|
onDone: () -> Unit,
|
||||||
|
onSeedCopy: () -> Unit,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
SeedRecoveryTopAppBar(
|
||||||
|
onBack = onBack,
|
||||||
|
onSeedCopy = onSeedCopy,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
SeedRecoveryMainContent(
|
||||||
|
wallet = wallet,
|
||||||
|
onDone = onDone,
|
||||||
|
onSeedCopy = onSeedCopy,
|
||||||
|
onBirthdayCopy = onBirthdayCopy,
|
||||||
|
// Horizontal paddings will be part of each UI element to minimize a possible truncation on very
|
||||||
|
// small screens
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
top = paddingValues.calculateTopPadding(),
|
||||||
|
bottom = paddingValues.calculateBottomPadding()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SeedRecoveryTopAppBar(
|
||||||
|
onBack: () -> Unit,
|
||||||
|
onSeedCopy: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
SmallTopAppBar(
|
||||||
|
modifier = modifier,
|
||||||
|
backText = stringResource(id = R.string.seed_recovery_back).uppercase(),
|
||||||
|
backContentDescriptionText = stringResource(R.string.seed_recovery_back_content_description),
|
||||||
|
onBack = onBack,
|
||||||
|
regularActions = {
|
||||||
|
SeedRecoveryCopyToBufferMenuItem(
|
||||||
|
onCopyToClipboard = onSeedCopy
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SeedRecoveryCopyToBufferMenuItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onCopyToClipboard: () -> Unit,
|
||||||
|
) {
|
||||||
|
Reference(
|
||||||
|
text = stringResource(id = R.string.seed_recovery_copy),
|
||||||
|
onClick = onCopyToClipboard,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = modifier.then(
|
||||||
|
Modifier.padding(all = ZcashTheme.dimens.spacingDefault)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SeedRecoveryMainContent(
|
||||||
|
wallet: PersistableWallet,
|
||||||
|
onSeedCopy: () -> Unit,
|
||||||
|
onBirthdayCopy: () -> Unit,
|
||||||
|
onDone: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.then(modifier),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
TopScreenLogoTitle(
|
||||||
|
title = stringResource(R.string.seed_recovery_header),
|
||||||
|
logoContentDescription = stringResource(R.string.zcash_logo_content_description),
|
||||||
|
modifier = Modifier.padding(horizontal = ZcashTheme.dimens.spacingHuge)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||||
|
|
||||||
|
BodySmall(
|
||||||
|
text = stringResource(R.string.seed_recovery_description),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.padding(horizontal = ZcashTheme.dimens.spacingHuge)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||||
|
|
||||||
|
SeedRecoverySeedPhrase(
|
||||||
|
persistableWallet = wallet,
|
||||||
|
onSeedCopy = onSeedCopy,
|
||||||
|
onBirthdayCopy = onBirthdayCopy,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.weight(MINIMAL_WEIGHT)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||||
|
|
||||||
|
SeedRecoveryBottomNav(
|
||||||
|
onDone = onDone,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
bottom = ZcashTheme.dimens.spacingHuge
|
||||||
|
)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun SeedRecoverySeedPhrase(
|
||||||
|
persistableWallet: PersistableWallet,
|
||||||
|
onSeedCopy: () -> Unit,
|
||||||
|
onBirthdayCopy: () -> Unit,
|
||||||
|
) {
|
||||||
|
if (shouldSecureScreen) {
|
||||||
|
SecureScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
ChipGrid(
|
||||||
|
wordList = persistableWallet.seedPhrase.split.toPersistentList(),
|
||||||
|
onGridClick = onSeedCopy
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||||
|
|
||||||
|
persistableWallet.birthday?.let {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
BodySmall(
|
||||||
|
text = stringResource(R.string.seed_recovery_birthday_height, it.value),
|
||||||
|
modifier = Modifier
|
||||||
|
.testTag(WALLET_BIRTHDAY)
|
||||||
|
.padding(horizontal = ZcashTheme.dimens.spacingDefault)
|
||||||
|
.basicMarquee()
|
||||||
|
// Apply click callback to the text only as the wrapping layout can be much wider
|
||||||
|
.clickable(
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = null, // Disable ripple
|
||||||
|
onClick = onBirthdayCopy
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
|
@Composable
|
||||||
|
private fun SeedRecoveryBottomNav(
|
||||||
|
onDone: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
PrimaryButton(onClick = onDone, text = stringResource(R.string.seed_recovery_button_finished))
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,12 +20,14 @@ internal fun MainActivity.WrapSettings(
|
||||||
goAbout: () -> Unit,
|
goAbout: () -> Unit,
|
||||||
goBack: () -> Unit,
|
goBack: () -> Unit,
|
||||||
goExportPrivateData: () -> Unit,
|
goExportPrivateData: () -> Unit,
|
||||||
|
goSeedRecovery: () -> Unit,
|
||||||
) {
|
) {
|
||||||
WrapSettings(
|
WrapSettings(
|
||||||
activity = this,
|
activity = this,
|
||||||
goAbout = goAbout,
|
goAbout = goAbout,
|
||||||
goBack = goBack,
|
goBack = goBack,
|
||||||
goExportPrivateData = goExportPrivateData
|
goExportPrivateData = goExportPrivateData,
|
||||||
|
goSeedRecovery = goSeedRecovery
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +37,7 @@ private fun WrapSettings(
|
||||||
goBack: () -> Unit,
|
goBack: () -> Unit,
|
||||||
goAbout: () -> Unit,
|
goAbout: () -> Unit,
|
||||||
goExportPrivateData: () -> Unit,
|
goExportPrivateData: () -> Unit,
|
||||||
|
goSeedRecovery: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||||
|
@ -63,7 +66,7 @@ private fun WrapSettings(
|
||||||
isRescanEnabled = ConfigurationEntries.IS_RESCAN_ENABLED.getValue(RemoteConfig.current),
|
isRescanEnabled = ConfigurationEntries.IS_RESCAN_ENABLED.getValue(RemoteConfig.current),
|
||||||
),
|
),
|
||||||
onBack = goBack,
|
onBack = goBack,
|
||||||
onBackup = {},
|
onSeedRecovery = goSeedRecovery,
|
||||||
onDocumentation = {},
|
onDocumentation = {},
|
||||||
onPrivacyPolicy = {},
|
onPrivacyPolicy = {},
|
||||||
onFeedback = {},
|
onFeedback = {},
|
||||||
|
|
|
@ -53,7 +53,7 @@ private fun PreviewSettings() {
|
||||||
isRescanEnabled = false
|
isRescanEnabled = false
|
||||||
),
|
),
|
||||||
onBack = {},
|
onBack = {},
|
||||||
onBackup = {},
|
onSeedRecovery = {},
|
||||||
onDocumentation = {},
|
onDocumentation = {},
|
||||||
onPrivacyPolicy = {},
|
onPrivacyPolicy = {},
|
||||||
onFeedback = {},
|
onFeedback = {},
|
||||||
|
@ -73,7 +73,7 @@ private fun PreviewSettings() {
|
||||||
fun Settings(
|
fun Settings(
|
||||||
troubleshootingParameters: TroubleshootingParameters,
|
troubleshootingParameters: TroubleshootingParameters,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onBackup: () -> Unit,
|
onSeedRecovery: () -> Unit,
|
||||||
onDocumentation: () -> Unit,
|
onDocumentation: () -> Unit,
|
||||||
onPrivacyPolicy: () -> Unit,
|
onPrivacyPolicy: () -> Unit,
|
||||||
onFeedback: () -> Unit,
|
onFeedback: () -> Unit,
|
||||||
|
@ -105,7 +105,7 @@ fun Settings(
|
||||||
start = dimens.spacingHuge,
|
start = dimens.spacingHuge,
|
||||||
end = dimens.spacingHuge
|
end = dimens.spacingHuge
|
||||||
),
|
),
|
||||||
onBackup = onBackup,
|
onSeedRecovery = onSeedRecovery,
|
||||||
onDocumentation = onDocumentation,
|
onDocumentation = onDocumentation,
|
||||||
onPrivacyPolicy = onPrivacyPolicy,
|
onPrivacyPolicy = onPrivacyPolicy,
|
||||||
onFeedback = onFeedback,
|
onFeedback = onFeedback,
|
||||||
|
@ -229,7 +229,7 @@ private fun TroubleshootingMenu(
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongParameterList", "LongMethod")
|
@Suppress("LongParameterList", "LongMethod")
|
||||||
private fun SettingsMainContent(
|
private fun SettingsMainContent(
|
||||||
onBackup: () -> Unit,
|
onSeedRecovery: () -> Unit,
|
||||||
onDocumentation: () -> Unit,
|
onDocumentation: () -> Unit,
|
||||||
onPrivacyPolicy: () -> Unit,
|
onPrivacyPolicy: () -> Unit,
|
||||||
onFeedback: () -> Unit,
|
onFeedback: () -> Unit,
|
||||||
|
@ -245,7 +245,7 @@ private fun SettingsMainContent(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onClick = onBackup,
|
onClick = onSeedRecovery,
|
||||||
text = stringResource(R.string.settings_backup_wallet),
|
text = stringResource(R.string.settings_backup_wallet),
|
||||||
outerPaddingValues = PaddingValues(
|
outerPaddingValues = PaddingValues(
|
||||||
horizontal = dimens.spacingNone,
|
horizontal = dimens.spacingNone,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<string name="new_wallet_recovery_birthday_height" formatted="true">Wallet birthday height: <xliff:g example="419200"
|
<string name="new_wallet_recovery_birthday_height" formatted="true">Wallet birthday height: <xliff:g example="419200"
|
||||||
id="birthday_height">%1$d</xliff:g></string>
|
id="birthday_height">%1$d</xliff:g></string>
|
||||||
<string name="new_wallet_recovery_button_finished">I got it!</string>
|
<string name="new_wallet_recovery_button_finished">I got it!</string>
|
||||||
<string name="new_wallet_copy">Tap to Copy</string>
|
<string name="new_wallet_recovery_copy">Tap to Copy</string>
|
||||||
<string name="new_wallet_seed_clipboard_tag">Zcash Seed Phrase</string>
|
<string name="new_wallet_recovery_seed_clipboard_tag">Zcash Seed Phrase</string>
|
||||||
<string name="new_wallet_birthday_clipboard_tag">Zcash Wallet Birthday</string>
|
<string name="new_wallet_recovery_birthday_clipboard_tag">Zcash Wallet Birthday</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<resources>
|
|
||||||
<string name="seed_title">My secret phrase</string>
|
|
||||||
<string name="seed_back_content_description">Back</string>
|
|
||||||
|
|
||||||
<string name="seed_header">Your Secret Recovery Phrase</string>
|
|
||||||
<string name="seed_body">These words represent your funds and the security used to protect them.</string>
|
|
||||||
<string name="seed_copy">Copy to buffer</string>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
|
<string name="seed_recovery_back">Back</string>
|
||||||
|
<string name="seed_recovery_back_content_description">Back</string>
|
||||||
|
<string name="seed_recovery_header">Your secret recovery phrase</string>
|
||||||
|
<string name="seed_recovery_description">The following 24 words are the keys to your funds and are the only way to
|
||||||
|
recover your funds if you get locked out or get a new device. Protect your ZEC by storing this phrase in a
|
||||||
|
place you trust and never share it with anyone!</string>
|
||||||
|
<string name="seed_recovery_birthday_height" formatted="true">Wallet birthday height: <xliff:g example="419200"
|
||||||
|
id="birthday_height">%1$d</xliff:g></string>
|
||||||
|
<string name="seed_recovery_button_finished">I got it!</string>
|
||||||
|
<string name="seed_recovery_copy">Tap to Copy</string>
|
||||||
|
<string name="seed_recovery_seed_clipboard_tag">Zcash Seed Phrase</string>
|
||||||
|
<string name="seed_recovery_birthday_clipboard_tag">Zcash Wallet Birthday</string>
|
||||||
|
</resources>
|
|
@ -296,7 +296,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
|
|
||||||
// These are the hamburger menu items
|
// These are the hamburger menu items
|
||||||
// We could manually click on each one, which is a better integration test but a worse screenshot test
|
// We could manually click on each one, which is a better integration test but a worse screenshot test
|
||||||
navigateTo(NavigationTargets.SEED)
|
navigateTo(NavigationTargets.SEED_RECOVERY)
|
||||||
seedScreenshots(resContext, tag, composeTestRule)
|
seedScreenshots(resContext, tag, composeTestRule)
|
||||||
|
|
||||||
navigateTo(NavigationTargets.SETTINGS)
|
navigateTo(NavigationTargets.SETTINGS)
|
||||||
|
@ -588,7 +588,7 @@ private fun aboutScreenshots(resContext: Context, tag: String, composeTestRule:
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun seedScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
private fun seedScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||||
composeTestRule.onNode(hasText(resContext.getString(R.string.seed_title))).also {
|
composeTestRule.onNode(hasText(resContext.getString(R.string.seed_recovery_header))).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue