[#762] Simplified seed backup UX
Because we’re making UI text changes, I’ve forked our views into “long” and “short” versions that preserve the previous experience while allowing the shorter experience to be different. One limitation of the current approach is that the screenshot tests are limited to the current configuration, so there is some risk of ‘bit rot’ with the screenshot tests for the longer onboarding. For this PR, I manually switched the feature flags and re-ran the screenshot tests to make sure they still worked. --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
4acd5d3593
commit
8e17d07ced
|
@ -0,0 +1,28 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name=":ui-benchmark-test:connectedBenchmarkAndroidTest" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="ORG_GRADLE_PROJECT_IS_SIGN_RELEASE_BUILD_WITH_DEBUG_KEY" value="true" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":ui-benchmark-test:connectedBenchmarkAndroidTest" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
|
@ -0,0 +1,28 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name=":ui-screenshot-test:connectedCheck" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="ORG_GRADLE_PROJECT_IS_SIGN_RELEASE_BUILD_WITH_DEBUG_KEY" value="true" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":ui-screenshot-test:connectedCheck" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,55 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ui-benchmark-test:connectedBenchmarkAndroidTest" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||
<module name="zcash-android-app.ui-benchmark-test" />
|
||||
<option name="TESTING_TYPE" value="0" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="CLASS_NAME" value="" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="TEST_NAME_REGEX" value="" />
|
||||
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||
<option name="EXTRA_OPTIONS" value="" />
|
||||
<option name="RETENTION_ENABLED" value="No" />
|
||||
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
|
||||
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Hybrid>
|
||||
<Java />
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,20 +1,17 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ui-design-lib:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||
<module name="zcash-android-app.ui-design-lib" />
|
||||
<option name="TESTING_TYPE" value="0" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="CLASS_NAME" value="" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="TEST_NAME_REGEX" value="" />
|
||||
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||
<option name="EXTRA_OPTIONS" value="" />
|
||||
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
|
||||
<option name="RETENTION_ENABLED" value="No" />
|
||||
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
|
||||
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ui-screenshot-test:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||
<module name="zcash-android-app.ui-screenshot-test" />
|
||||
<option name="TESTING_TYPE" value="0" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="CLASS_NAME" value="" />
|
||||
<option name="PACKAGE_NAME" value="" />
|
||||
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||
<option name="EXTRA_OPTIONS" value="" />
|
||||
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
|
||||
<option name="RETENTION_ENABLED" value="No" />
|
||||
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
|
||||
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Hybrid>
|
||||
<Java />
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
|
@ -31,10 +31,10 @@ android {
|
|||
"src/main/res/ui/common",
|
||||
"src/main/res/ui/home",
|
||||
"src/main/res/ui/onboarding",
|
||||
"src/main/res/ui/scan",
|
||||
"src/main/res/ui/receive",
|
||||
"src/main/res/ui/restore",
|
||||
"src/main/res/ui/request",
|
||||
"src/main/res/ui/restore",
|
||||
"src/main/res/ui/scan",
|
||||
"src/main/res/ui/seed",
|
||||
"src/main/res/ui/send",
|
||||
"src/main/res/ui/settings",
|
||||
|
|
|
@ -9,7 +9,7 @@ import co.electriccoin.zcash.ui.R
|
|||
import co.electriccoin.zcash.ui.common.UiTestingActivity
|
||||
import co.electriccoin.zcash.ui.fixture.TestChoicesFixture
|
||||
import co.electriccoin.zcash.ui.screen.backup.model.BackupStage
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.BackupTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.LongBackupTestSetup
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
|
@ -20,8 +20,8 @@ class BackupActivityTest : UiTestPrerequisites() {
|
|||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<UiTestingActivity>()
|
||||
|
||||
private fun newTestSetup(): BackupTestSetup {
|
||||
return BackupTestSetup(
|
||||
private fun newTestSetup(): LongBackupTestSetup {
|
||||
return LongBackupTestSetup(
|
||||
composeTestRule,
|
||||
BackupStage.EducationOverview,
|
||||
TestChoicesFixture.new(TestChoicesFixture.INITIAL_CHOICES)
|
||||
|
|
|
@ -16,7 +16,7 @@ import co.electriccoin.zcash.ui.R
|
|||
import co.electriccoin.zcash.ui.fixture.TestChoicesFixture
|
||||
import co.electriccoin.zcash.ui.screen.backup.BackupTag
|
||||
import co.electriccoin.zcash.ui.screen.backup.model.BackupStage
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.BackupTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.LongBackupTestSetup
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Rule
|
||||
import kotlin.test.Test
|
||||
|
@ -29,8 +29,8 @@ class BackupIntegrationTest : UiTestPrerequisites() {
|
|||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(initialStage: BackupStage): BackupTestSetup {
|
||||
return BackupTestSetup(
|
||||
private fun newTestSetup(initialStage: BackupStage): LongBackupTestSetup {
|
||||
return LongBackupTestSetup(
|
||||
composeTestRule,
|
||||
initialStage,
|
||||
TestChoicesFixture.new(TestChoicesFixture.INITIAL_CHOICES)
|
||||
|
|
|
@ -9,7 +9,7 @@ import co.electriccoin.zcash.ui.screen.backup.state.BackupState
|
|||
import co.electriccoin.zcash.ui.screen.backup.state.TestChoices
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class BackupTestSetup(
|
||||
class LongBackupTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
initialStage: BackupStage,
|
||||
private val initialChoices: TestChoices
|
||||
|
@ -53,7 +53,7 @@ class BackupTestSetup(
|
|||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
ZcashTheme {
|
||||
BackupWallet(
|
||||
LongNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
state,
|
||||
initialChoices,
|
|
@ -21,12 +21,12 @@ import org.junit.Assert.assertEquals
|
|||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class BackupViewTest : UiTestPrerequisites() {
|
||||
class LongBackupViewTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(initialStage: BackupStage): BackupTestSetup {
|
||||
return BackupTestSetup(composeTestRule, initialStage, TestChoicesFixture.new(mutableMapOf())).apply {
|
||||
private fun newTestSetup(initialStage: BackupStage): LongBackupTestSetup {
|
||||
return LongBackupTestSetup(composeTestRule, initialStage, TestChoicesFixture.new(mutableMapOf())).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
@ -212,17 +212,18 @@ class BackupViewTest : UiTestPrerequisites() {
|
|||
}
|
||||
}
|
||||
|
||||
fun ComposeContentTestRule.clickCopyToBuffer() {
|
||||
private fun ComposeContentTestRule.clickCopyToBuffer() {
|
||||
// open menu
|
||||
onNodeWithContentDescription(
|
||||
getStringResource(R.string.new_wallet_toolbar_more_button_content_description)
|
||||
).also { moreMenu ->
|
||||
moreMenu.performClick()
|
||||
// click menu button
|
||||
onNodeWithText(
|
||||
getStringResource(R.string.new_wallet_3_button_copy)
|
||||
).also { menuButton ->
|
||||
menuButton.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
// click menu button
|
||||
onNodeWithText(
|
||||
getStringResource(R.string.new_wallet_3_button_copy)
|
||||
).also { menuButton ->
|
||||
menuButton.performClick()
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import org.junit.Test
|
|||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class BackupViewsSecuredScreenTest : UiTestPrerequisites() {
|
||||
class LongBackupViewsSecuredScreenTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
|
@ -54,7 +54,7 @@ class BackupViewsSecuredScreenTest : UiTestPrerequisites() {
|
|||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalScreenSecurity provides screenSecurity) {
|
||||
ZcashTheme {
|
||||
BackupWallet(
|
||||
LongNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
state,
|
||||
TestChoicesFixture.new(mutableMapOf()),
|
|
@ -0,0 +1,44 @@
|
|||
package co.electriccoin.zcash.ui.screen.backup.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 ShortBackupTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
) {
|
||||
|
||||
private val onCopyToClipboardCount = AtomicInteger(0)
|
||||
|
||||
private val onCompleteCallbackCount = AtomicInteger(0)
|
||||
|
||||
fun getOnCopyToClipboardCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onCopyToClipboardCount.get()
|
||||
}
|
||||
|
||||
fun getOnCompleteCallbackCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onCompleteCallbackCount.get()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
ZcashTheme {
|
||||
ShortNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
onCopyToClipboard = { onCopyToClipboardCount.incrementAndGet() },
|
||||
onComplete = { onCompleteCallbackCount.incrementAndGet() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDefaultContent() {
|
||||
composeTestRule.setContent {
|
||||
DefaultContent()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package co.electriccoin.zcash.ui.screen.backup.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.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ShortBackupViewTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(): ShortBackupTestSetup {
|
||||
return ShortBackupTestSetup(composeTestRule).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun copy_to_clipboard() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
composeTestRule.copyToClipboard()
|
||||
|
||||
assertEquals(1, testSetup.getOnCopyToClipboardCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_finish() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.new_wallet_short_button_finished)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(0, testSetup.getOnCopyToClipboardCount())
|
||||
assertEquals(1, testSetup.getOnCompleteCallbackCount())
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComposeContentTestRule.copyToClipboard() {
|
||||
// open menu
|
||||
onNodeWithContentDescription(
|
||||
getStringResource(R.string.new_wallet_toolbar_more_button_content_description)
|
||||
).also { moreMenu ->
|
||||
moreMenu.performClick()
|
||||
}
|
||||
|
||||
// click menu button
|
||||
onNodeWithText(
|
||||
getStringResource(R.string.new_wallet_short_copy)
|
||||
).also { menuButton ->
|
||||
menuButton.performClick()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package co.electriccoin.zcash.ui.screen.backup.view
|
||||
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.common.LocalScreenSecurity
|
||||
import co.electriccoin.zcash.ui.common.ScreenSecurity
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ShortBackupViewsSecuredScreenTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup() =
|
||||
TestSetup(composeTestRule).apply {
|
||||
setContentView()
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun acquireScreenSecurity() = runTest {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(1, testSetup.getSecureScreenCount())
|
||||
}
|
||||
|
||||
private class TestSetup(private val composeTestRule: ComposeContentTestRule) {
|
||||
private val screenSecurity = ScreenSecurity()
|
||||
|
||||
fun getSecureScreenCount() = screenSecurity.referenceCount.value
|
||||
|
||||
fun setContentView() {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalScreenSecurity provides screenSecurity) {
|
||||
ZcashTheme {
|
||||
ShortNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
onCopyToClipboard = { },
|
||||
onComplete = { }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,21 +5,13 @@ import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
|||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.LongOnboarding
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class OnboardingTestSetup(
|
||||
class LongOnboardingTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
private val isFullOnboardingEnabled: Boolean,
|
||||
initialStage: OnboardingStage
|
||||
) {
|
||||
init {
|
||||
if (!isFullOnboardingEnabled) {
|
||||
require(initialStage == OnboardingStage.Wallet) {
|
||||
"When full onboarding is disabled, the initial stage must be Wallet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val onboardingState = OnboardingState(initialStage)
|
||||
|
||||
|
@ -45,8 +37,7 @@ class OnboardingTestSetup(
|
|||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
ZcashTheme {
|
||||
Onboarding(
|
||||
isFullOnboardingEnabled,
|
||||
LongOnboarding(
|
||||
onboardingState,
|
||||
isDebugMenuEnabled = false,
|
||||
onCreateWallet = { onCreateWalletCallbackCount.incrementAndGet() },
|
|
@ -0,0 +1,44 @@
|
|||
package co.electriccoin.zcash.ui.screen.onboarding
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.ShortOnboarding
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ShortOnboardingTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
) {
|
||||
private val onCreateWalletCallbackCount = AtomicInteger(0)
|
||||
private val onImportWalletCallbackCount = AtomicInteger(0)
|
||||
|
||||
fun getOnCreateWalletCallbackCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onCreateWalletCallbackCount.get()
|
||||
}
|
||||
|
||||
fun getOnImportWalletCallbackCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onImportWalletCallbackCount.get()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
ZcashTheme {
|
||||
ShortOnboarding(
|
||||
isDebugMenuEnabled = false,
|
||||
onCreateWallet = { onCreateWalletCallbackCount.incrementAndGet() },
|
||||
onImportWallet = { onImportWalletCallbackCount.incrementAndGet() },
|
||||
// We aren't testing this because it is for debug builds only.
|
||||
onFixtureWallet = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDefaultContent() {
|
||||
composeTestRule.setContent {
|
||||
DefaultContent()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import androidx.test.filters.MediumTest
|
|||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.UiTestingActivity
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.OnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.LongOnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
|
@ -18,9 +18,8 @@ class OnboardingActivityTest : UiTestPrerequisites() {
|
|||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<UiTestingActivity>()
|
||||
|
||||
private fun newTestSetup() = OnboardingTestSetup(
|
||||
private fun newTestSetup() = LongOnboardingTestSetup(
|
||||
composeTestRule,
|
||||
isFullOnboardingEnabled = true,
|
||||
OnboardingStage.ShieldedByDefault
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import androidx.compose.ui.test.performClick
|
|||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.OnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.LongOnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
|
@ -18,9 +18,8 @@ class OnboardingIntegrationTest : UiTestPrerequisites() {
|
|||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(initialStage: OnboardingStage) = OnboardingTestSetup(
|
||||
private fun newTestSetup(initialStage: OnboardingStage) = LongOnboardingTestSetup(
|
||||
composeTestRule,
|
||||
isFullOnboardingEnabled = true,
|
||||
initialStage
|
||||
)
|
||||
|
||||
|
|
|
@ -9,19 +9,19 @@ import androidx.compose.ui.test.performClick
|
|||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.OnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.LongOnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class OnboardingViewTest : UiTestPrerequisites() {
|
||||
class LongOnboardingViewTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(isFullOnboardingEnabled: Boolean = true, initialStage: OnboardingStage): OnboardingTestSetup {
|
||||
return OnboardingTestSetup(composeTestRule, isFullOnboardingEnabled, initialStage).apply {
|
||||
private fun newTestSetup(initialStage: OnboardingStage): LongOnboardingTestSetup {
|
||||
return LongOnboardingTestSetup(composeTestRule, initialStage).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
@ -180,36 +180,6 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun stage_4_layout_short_onboarding() {
|
||||
newTestSetup(isFullOnboardingEnabled = false, initialStage = OnboardingStage.Wallet)
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_skip)).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_next)).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.onboarding_back)).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_create_new_wallet)).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_4_import_existing_wallet)).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun stage_1_skip() {
|
|
@ -0,0 +1,68 @@
|
|||
package co.electriccoin.zcash.ui.screen.onboarding.view
|
||||
|
||||
import androidx.compose.ui.test.assertHasClickAction
|
||||
import androidx.compose.ui.test.assertIsEnabled
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.ShortOnboardingTestSetup
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ShortOnboardingViewTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(): ShortOnboardingTestSetup {
|
||||
return ShortOnboardingTestSetup(composeTestRule).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun layout() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_short_import_existing_wallet)).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_short_create_new_wallet)).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_create_wallet() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_short_create_new_wallet))
|
||||
newWalletButton.performClick()
|
||||
|
||||
assertEquals(1, testSetup.getOnCreateWalletCallbackCount())
|
||||
assertEquals(0, testSetup.getOnImportWalletCallbackCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_import_wallet() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
val newWalletButton = composeTestRule.onNodeWithText(getStringResource(R.string.onboarding_short_import_existing_wallet))
|
||||
newWalletButton.performClick()
|
||||
|
||||
assertEquals(1, testSetup.getOnImportWalletCallbackCount())
|
||||
assertEquals(0, testSetup.getOnCreateWalletCallbackCount())
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ import co.electriccoin.zcash.ui.design.component.ConfigurationOverride
|
|||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.Override
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.backup.WrapBackup
|
||||
import co.electriccoin.zcash.ui.screen.backup.WrapNewWallet
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.HomeViewModel
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.SecretState
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||
|
@ -129,7 +129,7 @@ class MainActivity : ComponentActivity() {
|
|||
WrapOnboarding()
|
||||
}
|
||||
is SecretState.NeedsBackup -> {
|
||||
WrapBackup(
|
||||
WrapNewWallet(
|
||||
secretState.persistableWallet,
|
||||
onBackupComplete = { walletViewModel.persistBackupComplete() }
|
||||
)
|
||||
|
|
|
@ -9,7 +9,12 @@ object ConfigurationEntries {
|
|||
/*
|
||||
* The full onboarding flow is functional and tested, but it is disabled by default for an initially minimal feature set.
|
||||
*/
|
||||
val IS_FULL_ONBOARDING_ENABLED = BooleanConfigurationEntry(ConfigKey("is_full_onboarding_enabled"), false)
|
||||
val IS_SHORT_ONBOARDING_UX = BooleanConfigurationEntry(ConfigKey("is_short_onboarding_ux"), true)
|
||||
|
||||
/*
|
||||
* The full new wallet flow is functional and tested, but it is disabled by default for an initially minimal feature set.
|
||||
*/
|
||||
val IS_SHORT_NEW_WALLET_BACKUP_UX = BooleanConfigurationEntry(ConfigKey("is_short_new_wallet_backup_ux"), true)
|
||||
|
||||
/*
|
||||
* A troubleshooting step. If we fix our bugs, this should be unnecessary.
|
||||
|
|
|
@ -11,27 +11,48 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||
import co.electriccoin.zcash.ui.screen.backup.ext.Saver
|
||||
import co.electriccoin.zcash.ui.screen.backup.state.BackupState
|
||||
import co.electriccoin.zcash.ui.screen.backup.state.TestChoices
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.BackupWallet
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.LongNewWalletBackup
|
||||
import co.electriccoin.zcash.ui.screen.backup.view.ShortNewWalletBackup
|
||||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapBackup(
|
||||
internal fun MainActivity.WrapNewWallet(
|
||||
persistableWallet: PersistableWallet,
|
||||
onBackupComplete: () -> Unit
|
||||
) {
|
||||
WrapBackup(this, persistableWallet, onBackupComplete)
|
||||
if (ConfigurationEntries.IS_SHORT_NEW_WALLET_BACKUP_UX.getValue(RemoteConfig.current)) {
|
||||
WrapShortNewWallet(
|
||||
persistableWallet,
|
||||
onBackupComplete = onBackupComplete
|
||||
)
|
||||
} else {
|
||||
WrapLongNewWallet(
|
||||
persistableWallet,
|
||||
onBackupComplete = onBackupComplete
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapLongNewWallet(
|
||||
persistableWallet: PersistableWallet,
|
||||
onBackupComplete: () -> Unit
|
||||
) {
|
||||
WrapLongNewWallet(this, persistableWallet, onBackupComplete)
|
||||
}
|
||||
|
||||
// This layer of indirection allows for activity re-creation tests
|
||||
@Composable
|
||||
internal fun WrapBackup(
|
||||
internal fun WrapLongNewWallet(
|
||||
activity: ComponentActivity,
|
||||
persistableWallet: PersistableWallet,
|
||||
onBackupComplete: () -> Unit
|
||||
) {
|
||||
WrapBackup(
|
||||
WrapLongNewWallet(
|
||||
persistableWallet,
|
||||
onCopyToClipboard = { copyToClipboard(activity.applicationContext, persistableWallet) },
|
||||
onBackupComplete = onBackupComplete
|
||||
|
@ -40,7 +61,7 @@ internal fun WrapBackup(
|
|||
|
||||
// This extra layer of indirection allows unit tests to validate the screen state retention.
|
||||
@Composable
|
||||
internal fun WrapBackup(
|
||||
internal fun WrapLongNewWallet(
|
||||
persistableWallet: PersistableWallet,
|
||||
onCopyToClipboard: () -> Unit,
|
||||
onBackupComplete: () -> Unit
|
||||
|
@ -48,7 +69,7 @@ internal fun WrapBackup(
|
|||
val testChoices by rememberSaveable(stateSaver = TestChoices.Saver) { mutableStateOf(TestChoices()) }
|
||||
val backupState by rememberSaveable(stateSaver = BackupState.Saver) { mutableStateOf(BackupState()) }
|
||||
|
||||
BackupWallet(
|
||||
LongNewWalletBackup(
|
||||
persistableWallet,
|
||||
backupState,
|
||||
testChoices,
|
||||
|
@ -58,7 +79,41 @@ internal fun WrapBackup(
|
|||
)
|
||||
}
|
||||
|
||||
fun copyToClipboard(context: Context, persistableWallet: PersistableWallet) {
|
||||
@Composable
|
||||
private fun MainActivity.WrapShortNewWallet(
|
||||
persistableWallet: PersistableWallet,
|
||||
onBackupComplete: () -> Unit
|
||||
) {
|
||||
WrapShortNewWallet(this, persistableWallet, onBackupComplete)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WrapShortNewWallet(
|
||||
activity: ComponentActivity,
|
||||
persistableWallet: PersistableWallet,
|
||||
onBackupComplete: () -> Unit
|
||||
) {
|
||||
WrapShortNewWallet(
|
||||
persistableWallet,
|
||||
onCopyToClipboard = { copyToClipboard(activity.applicationContext, persistableWallet) },
|
||||
onNewWalletComplete = onBackupComplete
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WrapShortNewWallet(
|
||||
persistableWallet: PersistableWallet,
|
||||
onCopyToClipboard: () -> Unit,
|
||||
onNewWalletComplete: () -> Unit
|
||||
) {
|
||||
ShortNewWalletBackup(
|
||||
persistableWallet,
|
||||
onCopyToClipboard = onCopyToClipboard,
|
||||
onComplete = onNewWalletComplete,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun copyToClipboard(context: Context, persistableWallet: PersistableWallet) {
|
||||
val clipboardManager = context.getSystemService(ClipboardManager::class.java)
|
||||
val data = ClipData.newPlainText(
|
||||
context.getString(R.string.new_wallet_clipboard_tag),
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package co.electriccoin.zcash.ui.screen.backup.view
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -22,6 +23,7 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
@ -38,6 +40,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
* @param onChoiceSelected Callback with the positional index of the item the user selected from [choices].
|
||||
*/
|
||||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
fun ChipDropDown(
|
||||
chipIndex: Index,
|
||||
dropdownText: String,
|
||||
|
@ -58,7 +61,11 @@ fun ChipDropDown(
|
|||
tonalElevation = 8.dp,
|
||||
shadowElevation = 8.dp
|
||||
) {
|
||||
Row(modifier = Modifier.padding(8.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = (chipIndex.value + 1).toString(),
|
||||
style = ZcashTheme.typography.chipIndex,
|
||||
|
|
|
@ -65,7 +65,7 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
fun ComposablePreview() {
|
||||
ZcashTheme(darkTheme = false) {
|
||||
GradientSurface {
|
||||
BackupWallet(
|
||||
LongNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
BackupState(BackupStage.EducationOverview),
|
||||
TestChoicesFixture.new(mutableMapOf()),
|
||||
|
@ -83,7 +83,7 @@ fun ComposablePreview() {
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
fun BackupWallet(
|
||||
fun LongNewWalletBackup(
|
||||
wallet: PersistableWallet,
|
||||
backupState: BackupState,
|
||||
choices: TestChoices,
|
|
@ -0,0 +1,159 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
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.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
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
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.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.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.PrimaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
@Preview(device = Devices.PIXEL_4)
|
||||
@Composable
|
||||
fun ComposablePreviewShort() {
|
||||
ZcashTheme(darkTheme = false) {
|
||||
GradientSurface {
|
||||
ShortNewWalletBackup(
|
||||
PersistableWalletFixture.new(),
|
||||
onCopyToClipboard = {},
|
||||
onComplete = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param onComplete Callback when the user has confirmed viewing the seed phrase.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ShortNewWalletBackup(
|
||||
wallet: PersistableWallet,
|
||||
onCopyToClipboard: () -> Unit,
|
||||
onComplete: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
ShortNewWalletTopAppBar(
|
||||
onCopyToClipboard = onCopyToClipboard,
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
ShortNewWalletBottomNav(
|
||||
onComplete = onComplete,
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
ShortNewWalletMainContent(
|
||||
paddingValues = paddingValues,
|
||||
wallet = wallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShortNewWalletMainContent(
|
||||
paddingValues: PaddingValues,
|
||||
wallet: PersistableWallet,
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding()
|
||||
)
|
||||
) {
|
||||
SeedPhrase(wallet)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SeedPhrase(persistableWallet: PersistableWallet) {
|
||||
SecureScreen()
|
||||
Column(
|
||||
Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(vertical = ZcashTheme.paddings.padding)
|
||||
) {
|
||||
Body(stringResource(R.string.new_wallet_short_body))
|
||||
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private fun ShortNewWalletTopAppBar(
|
||||
onCopyToClipboard: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.new_wallet_short_header)) },
|
||||
actions = {
|
||||
CopySeedMenu(onCopyToClipboard)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CopySeedMenu(onCopyToClipboard: () -> Unit) {
|
||||
Column {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(
|
||||
Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(R.string.new_wallet_toolbar_more_button_content_description)
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.new_wallet_short_copy)) },
|
||||
onClick = {
|
||||
expanded = false
|
||||
onCopyToClipboard()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun ShortNewWalletBottomNav(
|
||||
onComplete: () -> Unit
|
||||
) {
|
||||
Column {
|
||||
PrimaryButton(onClick = onComplete, text = stringResource(R.string.new_wallet_short_button_finished))
|
||||
}
|
||||
}
|
|
@ -21,9 +21,8 @@ import co.electriccoin.zcash.ui.MainActivity
|
|||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.model.OnboardingStage
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.LongOnboarding
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.view.ShortOnboarding
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restore.view.RestoreWallet
|
||||
import co.electriccoin.zcash.ui.screen.restore.viewmodel.CompleteWordSetState
|
||||
|
@ -51,55 +50,58 @@ internal fun WrapOnboarding(
|
|||
|
||||
// TODO [#383]: https://github.com/zcash/secant-android-wallet/issues/383
|
||||
if (!onboardingViewModel.isImporting.collectAsStateWithLifecycle().value) {
|
||||
val isFullOnboardingEnabled = ConfigurationEntries.IS_FULL_ONBOARDING_ENABLED.getValue(RemoteConfig.current)
|
||||
val onboardingState = if (isFullOnboardingEnabled) {
|
||||
onboardingViewModel.onboardingState
|
||||
} else {
|
||||
// Force to the last screen, which is the "create wallet" screen.
|
||||
// This simplifies the implementation inside the Onboarding composable.
|
||||
OnboardingState(OnboardingStage.values().last())
|
||||
}
|
||||
|
||||
Onboarding(
|
||||
isFullOnboardingEnabled = isFullOnboardingEnabled,
|
||||
onboardingState = onboardingState,
|
||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||
onImportWallet = {
|
||||
// In the case of the app currently being messed with by the robo test runner on
|
||||
// Firebase Test Lab or Google Play pre-launch report, we want to skip creating
|
||||
// a new or restoring an existing wallet screens by persisting an existing wallet
|
||||
// with a mock seed.
|
||||
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
||||
persistExistingWalletWithSeedPhrase(
|
||||
applicationContext,
|
||||
walletViewModel,
|
||||
SeedPhraseFixture.new()
|
||||
)
|
||||
return@Onboarding
|
||||
}
|
||||
|
||||
onboardingViewModel.setIsImporting(true)
|
||||
},
|
||||
onCreateWallet = {
|
||||
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
||||
persistExistingWalletWithSeedPhrase(
|
||||
applicationContext,
|
||||
walletViewModel,
|
||||
SeedPhraseFixture.new()
|
||||
)
|
||||
return@Onboarding
|
||||
}
|
||||
|
||||
walletViewModel.persistNewWallet()
|
||||
},
|
||||
onFixtureWallet = {
|
||||
val onCreateWallet = {
|
||||
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
||||
persistExistingWalletWithSeedPhrase(
|
||||
applicationContext,
|
||||
walletViewModel,
|
||||
SeedPhraseFixture.new()
|
||||
)
|
||||
} else {
|
||||
walletViewModel.persistNewWallet()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val onImportWallet = {
|
||||
// In the case of the app currently being messed with by the robo test runner on
|
||||
// Firebase Test Lab or Google Play pre-launch report, we want to skip creating
|
||||
// a new or restoring an existing wallet screens by persisting an existing wallet
|
||||
// with a mock seed.
|
||||
if (FirebaseTestLabUtil.isFirebaseTestLab(applicationContext)) {
|
||||
persistExistingWalletWithSeedPhrase(
|
||||
applicationContext,
|
||||
walletViewModel,
|
||||
SeedPhraseFixture.new()
|
||||
)
|
||||
} else {
|
||||
onboardingViewModel.setIsImporting(true)
|
||||
}
|
||||
}
|
||||
|
||||
val onFixtureWallet = {
|
||||
persistExistingWalletWithSeedPhrase(
|
||||
applicationContext,
|
||||
walletViewModel,
|
||||
SeedPhraseFixture.new()
|
||||
)
|
||||
}
|
||||
|
||||
if (ConfigurationEntries.IS_SHORT_ONBOARDING_UX.getValue(RemoteConfig.current)) {
|
||||
ShortOnboarding(
|
||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||
onImportWallet = onImportWallet,
|
||||
onCreateWallet = onCreateWallet,
|
||||
onFixtureWallet = onFixtureWallet
|
||||
)
|
||||
} else {
|
||||
LongOnboarding(
|
||||
onboardingState = onboardingViewModel.onboardingState,
|
||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||
onImportWallet = onImportWallet,
|
||||
onCreateWallet = onCreateWallet,
|
||||
onFixtureWallet = onFixtureWallet
|
||||
)
|
||||
}
|
||||
|
||||
activity.reportFullyDrawn()
|
||||
} else {
|
||||
|
|
|
@ -54,8 +54,7 @@ import co.electriccoin.zcash.ui.screen.onboarding.state.OnboardingState
|
|||
fun ComposablePreview() {
|
||||
ZcashTheme(darkTheme = true) {
|
||||
GradientSurface {
|
||||
Onboarding(
|
||||
isFullOnboardingEnabled = true,
|
||||
LongOnboarding(
|
||||
OnboardingState(OnboardingStage.Wallet),
|
||||
isDebugMenuEnabled = false,
|
||||
onImportWallet = {},
|
||||
|
@ -67,16 +66,13 @@ fun ComposablePreview() {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param isFullOnboardingEnabled Feature toggle to control whether the full onboarding flow is enabled. If disabled, then an abbreviated flow is shown
|
||||
* and the onboarding state is treated effectively as if it is [OnboardingStage.Wallet].
|
||||
* @param onImportWallet Callback when the user decides to import an existing wallet.
|
||||
* @param onCreateWallet Callback when the user decides to create a new wallet.
|
||||
*/
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("LongParameterList")
|
||||
fun Onboarding(
|
||||
isFullOnboardingEnabled: Boolean,
|
||||
fun LongOnboarding(
|
||||
onboardingState: OnboardingState,
|
||||
isDebugMenuEnabled: Boolean,
|
||||
onImportWallet: () -> Unit,
|
||||
|
@ -86,7 +82,7 @@ fun Onboarding(
|
|||
val currentStage = onboardingState.current.collectAsStateWithLifecycle().value
|
||||
Scaffold(
|
||||
topBar = {
|
||||
OnboardingTopAppBar(isFullOnboardingEnabled, onboardingState, isDebugMenuEnabled, onFixtureWallet)
|
||||
OnboardingTopAppBar(onboardingState, isDebugMenuEnabled, onFixtureWallet)
|
||||
},
|
||||
bottomBar = {
|
||||
BottomNav(currentStage, onboardingState::goNext, onCreateWallet, onImportWallet)
|
||||
|
@ -102,7 +98,6 @@ fun Onboarding(
|
|||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private fun OnboardingTopAppBar(
|
||||
isFullOnboardingEnabled: Boolean,
|
||||
onboardingState: OnboardingState,
|
||||
isDebugMenuEnabled: Boolean,
|
||||
onFixtureWallet: () -> Unit
|
||||
|
@ -112,14 +107,12 @@ private fun OnboardingTopAppBar(
|
|||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.app_name)) },
|
||||
navigationIcon = {
|
||||
if (isFullOnboardingEnabled) {
|
||||
if (currentStage.hasPrevious()) {
|
||||
IconButton(onboardingState::goPrevious) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.onboarding_back)
|
||||
)
|
||||
}
|
||||
if (currentStage.hasPrevious()) {
|
||||
IconButton(onboardingState::goPrevious) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.onboarding_back)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
|
@ -0,0 +1,143 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
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.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
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.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
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.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.Header
|
||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ShortOnboardingComposablePreview() {
|
||||
ZcashTheme(darkTheme = true) {
|
||||
GradientSurface {
|
||||