[#774] Redesign home with hamburger menu
--------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
9b966b4087
commit
6d01f210fe
|
@ -31,8 +31,8 @@ android {
|
|||
"src/main/res/ui/common",
|
||||
"src/main/res/ui/home",
|
||||
"src/main/res/ui/onboarding",
|
||||
"src/main/res/ui/profile",
|
||||
"src/main/res/ui/scan",
|
||||
"src/main/res/ui/receive",
|
||||
"src/main/res/ui/restore",
|
||||
"src/main/res/ui/request",
|
||||
"src/main/res/ui/seed",
|
||||
|
|
|
@ -10,21 +10,37 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
class HomeTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
private val walletSnapshot: WalletSnapshot,
|
||||
private val isRequestZecButtonEnabled: Boolean,
|
||||
) {
|
||||
private val onScanCount = AtomicInteger(0)
|
||||
private val onProfileCount = AtomicInteger(0)
|
||||
private val onAboutCount = AtomicInteger(0)
|
||||
private val onSeedCount = AtomicInteger(0)
|
||||
private val onSettingsCount = AtomicInteger(0)
|
||||
private val onSupportCount = AtomicInteger(0)
|
||||
private val onReceiveCount = AtomicInteger(0)
|
||||
private val onSendCount = AtomicInteger(0)
|
||||
private val onRequestCount = AtomicInteger(0)
|
||||
|
||||
fun getOnScanCount(): Int {
|
||||
fun getOnAboutCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onScanCount.get()
|
||||
return onAboutCount.get()
|
||||
}
|
||||
|
||||
fun getOnProfileCount(): Int {
|
||||
fun getOnSettingsCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onProfileCount.get()
|
||||
return onSettingsCount.get()
|
||||
}
|
||||
|
||||
fun getOnSupportCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onSupportCount.get()
|
||||
}
|
||||
|
||||
fun getOnSeedCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onSeedCount.get()
|
||||
}
|
||||
|
||||
fun getOnReceiveCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onReceiveCount.get()
|
||||
}
|
||||
|
||||
fun getOnSendCount(): Int {
|
||||
|
@ -32,11 +48,6 @@ class HomeTestSetup(
|
|||
return onSendCount.get()
|
||||
}
|
||||
|
||||
fun getOnRequestCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onRequestCount.get()
|
||||
}
|
||||
|
||||
fun getWalletSnapshot(): WalletSnapshot {
|
||||
composeTestRule.waitForIdle()
|
||||
return walletSnapshot
|
||||
|
@ -46,24 +57,29 @@ class HomeTestSetup(
|
|||
fun getDefaultContent() {
|
||||
Home(
|
||||
walletSnapshot,
|
||||
isKeepScreenOnDuringSync = false,
|
||||
isRequestZecButtonEnabled = isRequestZecButtonEnabled,
|
||||
transactionHistory = emptyList(),
|
||||
goScan = {
|
||||
onScanCount.incrementAndGet()
|
||||
isKeepScreenOnDuringSync = false,
|
||||
isUpdateAvailable = false,
|
||||
goSettings = {
|
||||
onSettingsCount.incrementAndGet()
|
||||
},
|
||||
goProfile = {
|
||||
onProfileCount.incrementAndGet()
|
||||
goSeedPhrase = {
|
||||
onSeedCount.incrementAndGet()
|
||||
},
|
||||
goSupport = {
|
||||
onSupportCount.incrementAndGet()
|
||||
},
|
||||
goAbout = {
|
||||
onAboutCount.incrementAndGet()
|
||||
},
|
||||
goReceive = {
|
||||
onReceiveCount.incrementAndGet()
|
||||
},
|
||||
goSend = {
|
||||
onSendCount.incrementAndGet()
|
||||
},
|
||||
goRequest = {
|
||||
onRequestCount.incrementAndGet()
|
||||
},
|
||||
resetSdk = {},
|
||||
isDebugMenuEnabled = false,
|
||||
updateAvailable = false
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,7 @@ class HomeViewIntegrationTest : UiTestPrerequisites() {
|
|||
|
||||
private fun newTestSetup(walletSnapshot: WalletSnapshot) = HomeTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot,
|
||||
isRequestZecButtonEnabled = false
|
||||
walletSnapshot
|
||||
)
|
||||
|
||||
// This is just basic sanity check that we still have UI set up as expected after the state restore
|
||||
|
|
|
@ -28,11 +28,7 @@ class HomeViewTest : UiTestPrerequisites() {
|
|||
fun check_all_elementary_ui_elements_displayed() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.home_scan_content_description)).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.home_profile_content_description)).also {
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.home_menu_content_description)).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
|
@ -44,43 +40,21 @@ class HomeViewTest : UiTestPrerequisites() {
|
|||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_button_request)).also {
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_button_receive)).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hide_request_zec() {
|
||||
newTestSetup(isRequestZecButtonEnabled = false)
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_button_request)).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_scan_button() {
|
||||
fun click_receive_button() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnScanCount())
|
||||
assertEquals(0, testSetup.getOnReceiveCount())
|
||||
|
||||
composeTestRule.clickScan()
|
||||
composeTestRule.clickReceive()
|
||||
|
||||
assertEquals(1, testSetup.getOnScanCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_profile_button() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnProfileCount())
|
||||
|
||||
composeTestRule.clickProfile()
|
||||
|
||||
assertEquals(1, testSetup.getOnProfileCount())
|
||||
assertEquals(1, testSetup.getOnReceiveCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -97,33 +71,84 @@ class HomeViewTest : UiTestPrerequisites() {
|
|||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_request_button() {
|
||||
fun hamburger_seed() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnRequestCount())
|
||||
assertEquals(0, testSetup.getOnReceiveCount())
|
||||
|
||||
composeTestRule.clickRequest()
|
||||
composeTestRule.openNavigationDrawer()
|
||||
|
||||
assertEquals(1, testSetup.getOnRequestCount())
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_menu_seed_phrase)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSeedCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(isRequestZecButtonEnabled: Boolean = true) = HomeTestSetup(
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hamburger_settings() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnReceiveCount())
|
||||
|
||||
composeTestRule.openNavigationDrawer()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_menu_settings)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSettingsCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hamburger_support() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnReceiveCount())
|
||||
|
||||
composeTestRule.openNavigationDrawer()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_menu_support)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSupportCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hamburger_about() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals(0, testSetup.getOnReceiveCount())
|
||||
|
||||
composeTestRule.openNavigationDrawer()
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.home_menu_about)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnAboutCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup() = HomeTestSetup(
|
||||
composeTestRule,
|
||||
WalletSnapshotFixture.new(),
|
||||
isRequestZecButtonEnabled = isRequestZecButtonEnabled
|
||||
WalletSnapshotFixture.new()
|
||||
).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
||||
fun ComposeContentTestRule.clickScan() {
|
||||
onNodeWithContentDescription(getStringResource(R.string.home_scan_content_description)).also {
|
||||
private fun ComposeContentTestRule.openNavigationDrawer() {
|
||||
onNodeWithContentDescription(getStringResource(R.string.home_menu_content_description)).also {
|
||||
it.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComposeContentTestRule.clickProfile() {
|
||||
onNodeWithContentDescription(getStringResource(R.string.home_profile_content_description)).also {
|
||||
private fun ComposeContentTestRule.clickReceive() {
|
||||
onNodeWithText(getStringResource(R.string.home_button_receive)).also {
|
||||
it.performClick()
|
||||
}
|
||||
}
|
||||
|
@ -134,10 +159,3 @@ private fun ComposeContentTestRule.clickSend() {
|
|||
it.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComposeContentTestRule.clickRequest() {
|
||||
onNodeWithText(getStringResource(R.string.home_button_request)).also {
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,231 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.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.android.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.profile.util.ProfileConfiguration
|
||||
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
|
||||
|
||||
/*
|
||||
* Note: It is difficult to test the QR code from automated tests. There is a manual test case
|
||||
* for that currently. A future enhancement could take a screenshot and try to analyze the
|
||||
* screenshot contents.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ProfileViewTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun setup() = runTest {
|
||||
val walletAddress = WalletAddressFixture.unified()
|
||||
newTestSetup(walletAddress)
|
||||
|
||||
// Enable substring for ellipsizing
|
||||
composeTestRule.onNodeWithText(walletAddress.address, substring = true).also {
|
||||
it.assertExists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun back() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.profile_back_content_description)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnBackCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun address_details() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnAddressDetailsCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_see_address_details)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnAddressDetailsCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun address_book() = runTest {
|
||||
if (ProfileConfiguration.IS_ADDRESS_BOOK_ENABLED) {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnAddressBookCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_address_book)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnAddressBookCount())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun settings() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnSettingsCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_settings)).also {
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSettingsCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun coinholder_vote() = runTest {
|
||||
if (ProfileConfiguration.IS_COINHOLDER_VOTE_ENABLED) {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnCoinholderVoteCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_coinholder_vote)).also {
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnCoinholderVoteCount())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun support() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnSupportCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_support)).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSupportCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun about() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnAboutCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.profile_about)).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnAboutCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(walletAddress: WalletAddress) = TestSetup(composeTestRule, walletAddress)
|
||||
|
||||
private class TestSetup(private val composeTestRule: ComposeContentTestRule, walletAddress: WalletAddress) {
|
||||
|
||||
private val onBackCount = AtomicInteger(0)
|
||||
private val onAddressDetailsCount = AtomicInteger(0)
|
||||
private val onAddressBookCount = AtomicInteger(0)
|
||||
private val onSettingsCount = AtomicInteger(0)
|
||||
private val onCoinholderVoteCount = AtomicInteger(0)
|
||||
private val onSupportCount = AtomicInteger(0)
|
||||
private val onAboutCount = AtomicInteger(0)
|
||||
|
||||
fun getOnBackCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onBackCount.get()
|
||||
}
|
||||
|
||||
fun getOnAddressDetailsCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onAddressDetailsCount.get()
|
||||
}
|
||||
|
||||
fun getOnAddressBookCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onAddressBookCount.get()
|
||||
}
|
||||
|
||||
fun getOnSettingsCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onSettingsCount.get()
|
||||
}
|
||||
|
||||
fun getOnCoinholderVoteCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onCoinholderVoteCount.get()
|
||||
}
|
||||
|
||||
fun getOnSupportCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onSupportCount.get()
|
||||
}
|
||||
|
||||
fun getOnAboutCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onAboutCount.get()
|
||||
}
|
||||
|
||||
init {
|
||||
composeTestRule.setContent {
|
||||
ZcashTheme {
|
||||
Profile(
|
||||
walletAddress,
|
||||
onBack = {
|
||||
onBackCount.getAndIncrement()
|
||||
},
|
||||
onAddressDetails = {
|
||||
onAddressDetailsCount.getAndIncrement()
|
||||
},
|
||||
onAddressBook = {
|
||||
onAddressBookCount.getAndIncrement()
|
||||
},
|
||||
onSettings = {
|
||||
onSettingsCount.getAndIncrement()
|
||||
},
|
||||
onCoinholderVote = {
|
||||
onCoinholderVoteCount.getAndIncrement()
|
||||
},
|
||||
onSupport = {
|
||||
onSupportCount.getAndIncrement()
|
||||
},
|
||||
onAbout = {
|
||||
onAboutCount.getAndIncrement()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.view
|
||||
package co.electriccoin.zcash.ui.screen.receive.view
|
||||
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
|
@ -17,7 +17,7 @@ import org.junit.Test
|
|||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ProfileViewScreenBrightnessTest : UiTestPrerequisites() {
|
||||
class ReceiveViewScreenBrightnessTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
|
@ -41,15 +41,10 @@ class ProfileViewScreenBrightnessTest : UiTestPrerequisites() {
|
|||
CompositionLocalProvider(LocalScreenBrightness provides screenBrightness) {
|
||||
ZcashTheme {
|
||||
ZcashTheme {
|
||||
Profile(
|
||||
Receive(
|
||||
walletAddress,
|
||||
onBack = { },
|
||||
onAddressDetails = { },
|
||||
onAddressBook = { },
|
||||
onSettings = { },
|
||||
onCoinholderVote = {},
|
||||
onSupport = { },
|
||||
onAbout = { }
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.view
|
||||
package co.electriccoin.zcash.ui.screen.receive.view
|
||||
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
|
@ -17,7 +17,7 @@ import org.junit.Test
|
|||
import kotlin.test.assertEquals
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ProfileViewScreenTimeoutTest : UiTestPrerequisites() {
|
||||
class ReceiveViewScreenTimeoutTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
|
@ -42,15 +42,10 @@ class ProfileViewScreenTimeoutTest : UiTestPrerequisites() {
|
|||
CompositionLocalProvider(LocalScreenTimeout provides screenTimeout) {
|
||||
ZcashTheme {
|
||||
ZcashTheme {
|
||||
Profile(
|
||||
Receive(
|
||||
walletAddress,
|
||||
onBack = { },
|
||||
onAddressDetails = { },
|
||||
onAddressBook = { },
|
||||
onSettings = { },
|
||||
onCoinholderVote = {},
|
||||
onSupport = { },
|
||||
onAbout = { }
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package co.electriccoin.zcash.ui.screen.receive.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 cash.z.ecc.android.sdk.fixture.WalletAddressFixture
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
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
|
||||
|
||||
/*
|
||||
* Note: It is difficult to test the QR code from automated tests. There is a manual test case
|
||||
* for that currently. A future enhancement could take a screenshot and try to analyze the
|
||||
* screenshot contents.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ReceiveViewTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun setup() = runTest {
|
||||
val walletAddress = WalletAddressFixture.unified()
|
||||
newTestSetup(walletAddress)
|
||||
|
||||
// Enable substring for ellipsizing
|
||||
composeTestRule.onNodeWithText(walletAddress.address, substring = true).also {
|
||||
it.assertExists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun back() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.receive_back_content_description)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnBackCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun address_details() = runTest {
|
||||
val testSetup = newTestSetup(WalletAddressFixture.unified())
|
||||
|
||||
assertEquals(0, testSetup.getOnAddressDetailsCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.receive_see_address_details)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnAddressDetailsCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(walletAddress: WalletAddress) = TestSetup(composeTestRule, walletAddress)
|
||||
|
||||
private class TestSetup(private val composeTestRule: ComposeContentTestRule, walletAddress: WalletAddress) {
|
||||
|
||||
private val onBackCount = AtomicInteger(0)
|
||||
private val onAddressDetailsCount = AtomicInteger(0)
|
||||
|
||||
fun getOnBackCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onBackCount.get()
|
||||
}
|
||||
|
||||
fun getOnAddressDetailsCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onAddressDetailsCount.get()
|
||||
}
|
||||
|
||||
init {
|
||||
composeTestRule.setContent {
|
||||
ZcashTheme {
|
||||
Receive(
|
||||
walletAddress,
|
||||
onBack = {
|
||||
onBackCount.getAndIncrement()
|
||||
},
|
||||
onAddressDetails = {
|
||||
onAddressDetailsCount.getAndIncrement()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,10 +6,13 @@ 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.configuration.model.map.StringConfiguration
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.test.getStringResource
|
||||
import kotlinx.collections.immutable.toPersistentMap
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
|
@ -36,32 +39,24 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
assertEquals(1, testSetup.getOnBackCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun backup() = runTest {
|
||||
val testSetup = TestSetup(composeTestRule)
|
||||
|
||||
assertEquals(0, testSetup.getBackupCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.settings_backup)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getBackupCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun rescan() = runTest {
|
||||
val testSetup = TestSetup(composeTestRule)
|
||||
|
||||
assertEquals(0, testSetup.getRescanCount())
|
||||
if (ConfigurationEntries.IS_RESCAN_ENABLED.getValue(StringConfiguration(emptyMap<String, String>().toPersistentMap(), null))) {
|
||||
assertEquals(0, testSetup.getRescanCount())
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.settings_rescan)).also {
|
||||
it.performClick()
|
||||
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.settings_overflow_content_description)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.settings_rescan)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getRescanCount())
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getRescanCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -152,12 +147,10 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
isBackgroundSyncEnabled = true,
|
||||
isKeepScreenOnDuringSyncEnabled = true,
|
||||
isAnalyticsEnabled = true,
|
||||
isRescanEnabled = true,
|
||||
onBack = {
|
||||
onBackCount.incrementAndGet()
|
||||
},
|
||||
onBackupWallet = {
|
||||
onBackupCount.incrementAndGet()
|
||||
},
|
||||
onRescanWallet = {
|
||||
onRescanCount.incrementAndGet()
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.HOME
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.PROFILE
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.RECEIVE
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.SCAN
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.SEED
|
||||
|
@ -22,7 +22,7 @@ import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
|||
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
||||
import co.electriccoin.zcash.ui.screen.address.WrapWalletAddresses
|
||||
import co.electriccoin.zcash.ui.screen.home.WrapHome
|
||||
import co.electriccoin.zcash.ui.screen.profile.WrapProfile
|
||||
import co.electriccoin.zcash.ui.screen.receive.WrapReceive
|
||||
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
||||
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
|
||||
import co.electriccoin.zcash.ui.screen.seed.WrapSeed
|
||||
|
@ -43,27 +43,18 @@ internal fun MainActivity.Navigation() {
|
|||
NavHost(navController = navController, startDestination = HOME) {
|
||||
composable(HOME) {
|
||||
WrapHome(
|
||||
goScan = { navController.navigateJustOnce(SCAN) },
|
||||
goProfile = { navController.navigateJustOnce(PROFILE) },
|
||||
goSeedPhrase = { navController.navigateJustOnce(SEED) },
|
||||
goSettings = { navController.navigateJustOnce(SETTINGS) },
|
||||
goSupport = { navController.navigateJustOnce(SUPPORT) },
|
||||
goAbout = { navController.navigateJustOnce(ABOUT) },
|
||||
goReceive = { navController.navigateJustOnce(RECEIVE) },
|
||||
goSend = { navController.navigateJustOnce(SEND) },
|
||||
goRequest = { navController.navigateJustOnce(REQUEST) }
|
||||
)
|
||||
|
||||
if (ConfigurationEntries.IS_APP_UPDATE_CHECK_ENABLED.getValue(RemoteConfig.current)) {
|
||||
WrapCheckForUpdate()
|
||||
}
|
||||
}
|
||||
composable(PROFILE) {
|
||||
WrapProfile(
|
||||
onBack = { navController.popBackStackJustOnce(PROFILE) },
|
||||
onAddressDetails = { navController.navigateJustOnce(WALLET_ADDRESS_DETAILS) },
|
||||
onAddressBook = { },
|
||||
onSettings = { navController.navigateJustOnce(SETTINGS) },
|
||||
onCoinholderVote = { },
|
||||
onSupport = { navController.navigateJustOnce(SUPPORT) },
|
||||
onAbout = { navController.navigateJustOnce(ABOUT) }
|
||||
)
|
||||
}
|
||||
composable(WALLET_ADDRESS_DETAILS) {
|
||||
WrapWalletAddresses(
|
||||
goBack = {
|
||||
|
@ -75,9 +66,6 @@ internal fun MainActivity.Navigation() {
|
|||
WrapSettings(
|
||||
goBack = {
|
||||
navController.popBackStackJustOnce(SETTINGS)
|
||||
},
|
||||
goWalletBackup = {
|
||||
navController.navigateJustOnce(SEED)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -88,6 +76,12 @@ internal fun MainActivity.Navigation() {
|
|||
}
|
||||
)
|
||||
}
|
||||
composable(RECEIVE) {
|
||||
WrapReceive(
|
||||
onBack = { navController.popBackStackJustOnce(RECEIVE) },
|
||||
onAddressDetails = { navController.navigateJustOnce(WALLET_ADDRESS_DETAILS) }
|
||||
)
|
||||
}
|
||||
composable(REQUEST) {
|
||||
WrapRequest(goBack = { navController.popBackStackJustOnce(REQUEST) })
|
||||
}
|
||||
|
@ -146,14 +140,14 @@ private fun NavHostController.popBackStackJustOnce(currentRouteToBePopped: Strin
|
|||
object NavigationTargets {
|
||||
const val HOME = "home"
|
||||
|
||||
const val PROFILE = "profile"
|
||||
|
||||
const val WALLET_ADDRESS_DETAILS = "wallet_address_details"
|
||||
|
||||
const val SETTINGS = "settings"
|
||||
|
||||
const val SEED = "seed"
|
||||
|
||||
const val RECEIVE = "receive"
|
||||
|
||||
const val REQUEST = "request"
|
||||
|
||||
const val SEND = "send"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package co.electriccoin.zcash.ui.common
|
||||
|
||||
import androidx.compose.material3.DrawerState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal fun DrawerState.openDrawerMenu(scope: CoroutineScope) {
|
||||
if (isOpen) {
|
||||
return
|
||||
}
|
||||
scope.launch { open() }
|
||||
}
|
||||
|
||||
internal fun DrawerState.closeDrawerMenu(scope: CoroutineScope) {
|
||||
if (isClosed) {
|
||||
return
|
||||
}
|
||||
scope.launch { close() }
|
||||
}
|
|
@ -6,13 +6,13 @@ import co.electriccoin.zcash.configuration.model.entry.ConfigKey
|
|||
object ConfigurationEntries {
|
||||
val IS_APP_UPDATE_CHECK_ENABLED = BooleanConfigurationEntry(ConfigKey("is_update_check_enabled"), true)
|
||||
|
||||
/*
|
||||
* Disabled because we don't have the URI parser support in the SDK yet.
|
||||
*/
|
||||
val IS_REQUEST_ZEC_ENABLED = BooleanConfigurationEntry(ConfigKey("is_request_zec_enabled"), false)
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
/*
|
||||
* A troubleshooting step. If we fix our bugs, this should be unnecessary.
|
||||
*/
|
||||
val IS_RESCAN_ENABLED = BooleanConfigurationEntry(ConfigKey("is_rescan_enabled"), true)
|
||||
}
|
||||
|
|
|
@ -3,16 +3,19 @@
|
|||
package co.electriccoin.zcash.ui.screen.home
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||
import co.electriccoin.zcash.ui.BuildConfig
|
||||
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.common.closeDrawerMenu
|
||||
import co.electriccoin.zcash.ui.screen.home.view.Home
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.CheckUpdateViewModel
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||
|
@ -21,28 +24,36 @@ import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
|||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun MainActivity.WrapHome(
|
||||
goScan: () -> Unit,
|
||||
goProfile: () -> Unit,
|
||||
goSeedPhrase: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
goSupport: () -> Unit,
|
||||
goAbout: () -> Unit,
|
||||
goReceive: () -> Unit,
|
||||
goSend: () -> Unit,
|
||||
goRequest: () -> Unit
|
||||
) {
|
||||
WrapHome(
|
||||
this,
|
||||
goScan = goScan,
|
||||
goProfile = goProfile,
|
||||
goSeedPhrase = goSeedPhrase,
|
||||
goSettings = goSettings,
|
||||
goSupport = goSupport,
|
||||
goAbout = goAbout,
|
||||
goReceive = goReceive,
|
||||
goSend = goSend,
|
||||
goRequest = goRequest
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun WrapHome(
|
||||
activity: ComponentActivity,
|
||||
goScan: () -> Unit,
|
||||
goProfile: () -> Unit,
|
||||
goSeedPhrase: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
goSupport: () -> Unit,
|
||||
goAbout: () -> Unit,
|
||||
goReceive: () -> Unit,
|
||||
goSend: () -> Unit,
|
||||
goRequest: () -> Unit
|
||||
) {
|
||||
// we want to show information about app update, if available
|
||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||
|
@ -75,20 +86,29 @@ internal fun WrapHome(
|
|||
|
||||
val transactionSnapshot = walletViewModel.transactionSnapshot.collectAsStateWithLifecycle().value
|
||||
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// override Android back navigation action to close drawer, if opened
|
||||
BackHandler(drawerState.isOpen) {
|
||||
drawerState.closeDrawerMenu(scope)
|
||||
}
|
||||
|
||||
Home(
|
||||
walletSnapshot,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnWhileSyncing,
|
||||
isRequestZecButtonEnabled = ConfigurationEntries.IS_REQUEST_ZEC_ENABLED.getValue(RemoteConfig.current),
|
||||
transactionSnapshot,
|
||||
goScan = goScan,
|
||||
goRequest = goRequest,
|
||||
goSend = goSend,
|
||||
goProfile = goProfile,
|
||||
isUpdateAvailable = updateAvailable,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnWhileSyncing,
|
||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||
goSeedPhrase = goSeedPhrase,
|
||||
goSettings = goSettings,
|
||||
goSupport = goSupport,
|
||||
goAbout = goAbout,
|
||||
goReceive = goReceive,
|
||||
goSend = goSend,
|
||||
resetSdk = {
|
||||
walletViewModel.resetSdk()
|
||||
},
|
||||
updateAvailable = updateAvailable
|
||||
}
|
||||
)
|
||||
|
||||
activity.reportFullyDrawn()
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package co.electriccoin.zcash.ui.screen.home.view
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -16,21 +17,32 @@ import androidx.compose.foundation.lazy.items
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ContactSupport
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||
import androidx.compose.material.icons.filled.Password
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.DrawerValue
|
||||
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.ModalDrawerSheet
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationDrawerItem
|
||||
import androidx.compose.material3.NavigationDrawerItemDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -48,19 +60,20 @@ import cash.z.ecc.android.sdk.model.PercentDecimal
|
|||
import co.electriccoin.zcash.crash.android.GlobalCrashReporter
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.common.closeDrawerMenu
|
||||
import co.electriccoin.zcash.ui.common.openDrawerMenu
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.BodyWithFiatCurrencySymbol
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.HeaderWithZecIcon
|
||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.home.HomeTag
|
||||
import co.electriccoin.zcash.ui.screen.home.model.CommonTransaction
|
||||
import co.electriccoin.zcash.ui.screen.home.model.WalletDisplayValues
|
||||
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
|
@ -68,17 +81,18 @@ fun ComposablePreview() {
|
|||
ZcashTheme(darkTheme = true) {
|
||||
GradientSurface {
|
||||
Home(
|
||||
WalletSnapshotFixture.new(),
|
||||
walletSnapshot = WalletSnapshotFixture.new(),
|
||||
transactionHistory = emptyList(),
|
||||
isUpdateAvailable = false,
|
||||
isKeepScreenOnDuringSync = false,
|
||||
isRequestZecButtonEnabled = false,
|
||||
emptyList(),
|
||||
goScan = {},
|
||||
goProfile = {},
|
||||
goSend = {},
|
||||
goRequest = {},
|
||||
resetSdk = {},
|
||||
isDebugMenuEnabled = false,
|
||||
updateAvailable = false
|
||||
goSeedPhrase = {},
|
||||
goSettings = {},
|
||||
goSupport = {},
|
||||
goAbout = {},
|
||||
goReceive = {},
|
||||
goSend = {},
|
||||
resetSdk = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -89,43 +103,72 @@ fun ComposablePreview() {
|
|||
@Composable
|
||||
fun Home(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
isRequestZecButtonEnabled: Boolean,
|
||||
transactionHistory: List<CommonTransaction>,
|
||||
goScan: () -> Unit,
|
||||
goProfile: () -> Unit,
|
||||
goSend: () -> Unit,
|
||||
goRequest: () -> Unit,
|
||||
resetSdk: () -> Unit,
|
||||
isUpdateAvailable: Boolean,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
isDebugMenuEnabled: Boolean,
|
||||
updateAvailable: Boolean
|
||||
goSeedPhrase: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
goSupport: () -> Unit,
|
||||
goAbout: () -> Unit,
|
||||
goReceive: () -> Unit,
|
||||
goSend: () -> Unit,
|
||||
resetSdk: () -> Unit,
|
||||
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
|
||||
scope: CoroutineScope = rememberCoroutineScope()
|
||||
) {
|
||||
Scaffold(topBar = {
|
||||
HomeTopAppBar(isDebugMenuEnabled, resetSdk)
|
||||
}) { paddingValues ->
|
||||
HomeMainContent(
|
||||
paddingValues,
|
||||
walletSnapshot,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnDuringSync,
|
||||
isRequestZecButtonEnabled = isRequestZecButtonEnabled,
|
||||
transactionHistory,
|
||||
goScan = goScan,
|
||||
goProfile = goProfile,
|
||||
goSend = goSend,
|
||||
goRequest = goRequest,
|
||||
updateAvailable = updateAvailable
|
||||
)
|
||||
}
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
HomeDrawer(
|
||||
onCloseDrawer = { drawerState.closeDrawerMenu(scope) },
|
||||
goSeedPhrase = goSeedPhrase,
|
||||
goSettings = goSettings,
|
||||
goSupport = goSupport,
|
||||
goAbout = goAbout
|
||||
)
|
||||
},
|
||||
content = {
|
||||
Scaffold(topBar = {
|
||||
HomeTopAppBar(
|
||||
isDebugMenuEnabled = isDebugMenuEnabled,
|
||||
openDrawer = { drawerState.openDrawerMenu(scope) },
|
||||
resetSdk = resetSdk
|
||||
)
|
||||
}) { paddingValues ->
|
||||
HomeMainContent(
|
||||
paddingValues,
|
||||
walletSnapshot,
|
||||
transactionHistory,
|
||||
isUpdateAvailable = isUpdateAvailable,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnDuringSync,
|
||||
goReceive = goReceive,
|
||||
goSend = goSend,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private fun HomeTopAppBar(
|
||||
isDebugMenuEnabled: Boolean,
|
||||
openDrawer: () -> Unit,
|
||||
resetSdk: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.app_name)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = openDrawer
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Menu,
|
||||
contentDescription = stringResource(R.string.home_menu_content_description)
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (isDebugMenuEnabled) {
|
||||
DebugMenu(resetSdk)
|
||||
|
@ -174,55 +217,82 @@ private fun DebugMenu(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeDrawer(
|
||||
onCloseDrawer: () -> Unit,
|
||||
goSeedPhrase: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
goSupport: () -> Unit,
|
||||
goAbout: () -> Unit,
|
||||
) {
|
||||
ModalDrawerSheet {
|
||||
Spacer(Modifier.height(12.dp))
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.Password, contentDescription = null) },
|
||||
label = { Text(stringResource(id = R.string.home_menu_seed_phrase)) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
onCloseDrawer()
|
||||
goSeedPhrase()
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
||||
label = { Text(stringResource(id = R.string.home_menu_settings)) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
onCloseDrawer()
|
||||
goSettings()
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.ContactSupport, contentDescription = null) },
|
||||
label = { Text(stringResource(id = R.string.home_menu_support)) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
onCloseDrawer()
|
||||
goSupport()
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(Icons.Default.Info, contentDescription = null) },
|
||||
label = { Text(stringResource(id = R.string.home_menu_about)) },
|
||||
selected = false,
|
||||
onClick = {
|
||||
onCloseDrawer()
|
||||
goAbout()
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun HomeMainContent(
|
||||
paddingValues: PaddingValues,
|
||||
walletSnapshot: WalletSnapshot,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
isRequestZecButtonEnabled: Boolean,
|
||||
transactionHistory: List<CommonTransaction>,
|
||||
goScan: () -> Unit,
|
||||
goProfile: () -> Unit,
|
||||
isUpdateAvailable: Boolean,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
goReceive: () -> Unit,
|
||||
goSend: () -> Unit,
|
||||
goRequest: () -> Unit,
|
||||
updateAvailable: Boolean
|
||||
) {
|
||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = paddingValues.calculateTopPadding())
|
||||
) {
|
||||
IconButton(goScan) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.QrCodeScanner,
|
||||
contentDescription = stringResource(R.string.home_scan_content_description)
|
||||
)
|
||||
}
|
||||
Spacer(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(MINIMAL_WEIGHT)
|
||||
)
|
||||
IconButton(goProfile) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Person,
|
||||
contentDescription = stringResource(R.string.home_profile_content_description)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Status(walletSnapshot, updateAvailable)
|
||||
Column(
|
||||
Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(top = paddingValues.calculateTopPadding())
|
||||
) {
|
||||
Status(walletSnapshot, isUpdateAvailable)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
PrimaryButton(onClick = goReceive, text = stringResource(R.string.home_button_receive))
|
||||
PrimaryButton(onClick = goSend, text = stringResource(R.string.home_button_send))
|
||||
|
||||
if (isRequestZecButtonEnabled) {
|
||||
TertiaryButton(onClick = goRequest, text = stringResource(R.string.home_button_request))
|
||||
}
|
||||
|
||||
History(transactionHistory)
|
||||
|
||||
if (isKeepScreenOnDuringSync == true && isSyncing(walletSnapshot.status)) {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.util
|
||||
|
||||
object ProfileConfiguration {
|
||||
const val IS_ADDRESS_BOOK_ENABLED = false
|
||||
const val IS_COINHOLDER_VOTE_ENABLED = false
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
@file:Suppress("ktlint:filename")
|
||||
|
||||
package co.electriccoin.zcash.ui.screen.profile
|
||||
package co.electriccoin.zcash.ui.screen.receive
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.viewModels
|
||||
|
@ -9,82 +9,52 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import cash.z.ecc.android.sdk.model.WalletAddresses
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.profile.view.Profile
|
||||
import co.electriccoin.zcash.ui.screen.receive.view.Receive
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun MainActivity.WrapProfile(
|
||||
internal fun MainActivity.WrapReceive(
|
||||
onBack: () -> Unit,
|
||||
onAddressDetails: () -> Unit,
|
||||
onAddressBook: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onCoinholderVote: () -> Unit,
|
||||
onSupport: () -> Unit,
|
||||
onAbout: () -> Unit
|
||||
) {
|
||||
WrapProfile(
|
||||
WrapReceive(
|
||||
this,
|
||||
onBack = onBack,
|
||||
onAddressDetails = onAddressDetails,
|
||||
onAddressBook = onAddressBook,
|
||||
onSettings = onSettings,
|
||||
onCoinholderVote = onCoinholderVote,
|
||||
onSupport = onSupport,
|
||||
onAbout = onAbout
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun WrapProfile(
|
||||
internal fun WrapReceive(
|
||||
activity: ComponentActivity,
|
||||
onBack: () -> Unit,
|
||||
onAddressDetails: () -> Unit,
|
||||
onAddressBook: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onCoinholderVote: () -> Unit,
|
||||
onSupport: () -> Unit,
|
||||
onAbout: () -> Unit
|
||||
) {
|
||||
val viewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletAddresses = viewModel.addresses.collectAsStateWithLifecycle().value
|
||||
|
||||
WrapProfile(
|
||||
WrapReceive(
|
||||
walletAddresses,
|
||||
onBack = onBack,
|
||||
onAddressDetails = onAddressDetails,
|
||||
onAddressBook = onAddressBook,
|
||||
onSettings = onSettings,
|
||||
onCoinholderVote = onCoinholderVote,
|
||||
onSupport = onSupport,
|
||||
onAbout = onAbout
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
internal fun WrapProfile(
|
||||
internal fun WrapReceive(
|
||||
walletAddresses: WalletAddresses?,
|
||||
onBack: () -> Unit,
|
||||
onAddressDetails: () -> Unit,
|
||||
onAddressBook: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onCoinholderVote: () -> Unit,
|
||||
onSupport: () -> Unit,
|
||||
onAbout: () -> Unit
|
||||
) {
|
||||
if (null == walletAddresses) {
|
||||
// Display loading indicator
|
||||
} else {
|
||||
Profile(
|
||||
Receive(
|
||||
walletAddresses.unified,
|
||||
onBack = onBack,
|
||||
onAddressDetails = onAddressDetails,
|
||||
onAddressBook = onAddressBook,
|
||||
onSettings = onSettings,
|
||||
onCoinholderVote = onCoinholderVote,
|
||||
onSupport = onSupport,
|
||||
onAbout = onAbout
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.util
|
||||
package co.electriccoin.zcash.ui.screen.receive.util
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.util
|
||||
package co.electriccoin.zcash.ui.screen.receive.util
|
||||
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.util
|
||||
package co.electriccoin.zcash.ui.screen.receive.util
|
||||
|
||||
interface QrCodeGenerator {
|
||||
/**
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.util
|
||||
package co.electriccoin.zcash.ui.screen.receive.util
|
||||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.profile.view
|
||||
package co.electriccoin.zcash.ui.screen.receive.view
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -6,7 +6,6 @@ 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.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
@ -30,11 +29,9 @@ import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
|||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.profile.util.AndroidQrCodeImageGenerator
|
||||
import co.electriccoin.zcash.ui.screen.profile.util.JvmQrCodeGenerator
|
||||
import co.electriccoin.zcash.ui.screen.profile.util.ProfileConfiguration
|
||||
import co.electriccoin.zcash.ui.screen.receive.util.AndroidQrCodeImageGenerator
|
||||
import co.electriccoin.zcash.ui.screen.receive.util.JvmQrCodeGenerator
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -43,15 +40,10 @@ import kotlin.math.roundToInt
|
|||
fun ComposablePreview() {
|
||||
ZcashTheme(darkTheme = true) {
|
||||
GradientSurface {
|
||||
Profile(
|
||||
Receive(
|
||||
walletAddress = runBlocking { WalletAddressFixture.unified() },
|
||||
onBack = {},
|
||||
onAddressDetails = {},
|
||||
onAddressBook = {},
|
||||
onSettings = {},
|
||||
onCoinholderVote = {},
|
||||
onSupport = {},
|
||||
onAbout = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -59,44 +51,32 @@ fun ComposablePreview() {
|
|||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
fun Profile(
|
||||
fun Receive(
|
||||
walletAddress: WalletAddress,
|
||||
onBack: () -> Unit,
|
||||
onAddressDetails: () -> Unit,
|
||||
onAddressBook: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onCoinholderVote: () -> Unit,
|
||||
onSupport: () -> Unit,
|
||||
onAbout: () -> Unit
|
||||
) {
|
||||
Column {
|
||||
ProfileTopAppBar(onBack = onBack)
|
||||
ProfileContents(
|
||||
ReceiveTopAppBar(onBack = onBack)
|
||||
ReceiveContents(
|
||||
walletAddress = walletAddress,
|
||||
onAddressDetails = onAddressDetails,
|
||||
onAddressBook = onAddressBook,
|
||||
onSettings = onSettings,
|
||||
onCoinholderVote = onCoinholderVote,
|
||||
onSupport = onSupport,
|
||||
onAbout = onAbout,
|
||||
isAddressBookEnabled = ProfileConfiguration.IS_ADDRESS_BOOK_ENABLED,
|
||||
isCoinholderVoteEnabled = ProfileConfiguration.IS_COINHOLDER_VOTE_ENABLED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private fun ProfileTopAppBar(onBack: () -> Unit) {
|
||||
private fun ReceiveTopAppBar(onBack: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.profile_title)) },
|
||||
title = { Text(text = stringResource(id = R.string.receive_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.profile_back_content_description)
|
||||
contentDescription = stringResource(R.string.receive_back_content_description)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -107,16 +87,9 @@ private val DEFAULT_QR_CODE_SIZE = 320.dp
|
|||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
private fun ProfileContents(
|
||||
private fun ReceiveContents(
|
||||
walletAddress: WalletAddress,
|
||||
onAddressDetails: () -> Unit,
|
||||
onAddressBook: () -> Unit,
|
||||
onSettings: () -> Unit,
|
||||
onCoinholderVote: () -> Unit,
|
||||
onSupport: () -> Unit,
|
||||
onAbout: () -> Unit,
|
||||
isAddressBookEnabled: Boolean,
|
||||
isCoinholderVoteEnabled: Boolean
|
||||
) {
|
||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||
QrCode(data = walletAddress.address, DEFAULT_QR_CODE_SIZE, Modifier.align(Alignment.CenterHorizontally))
|
||||
|
@ -131,17 +104,7 @@ private fun ProfileContents(
|
|||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
)
|
||||
PrimaryButton(onClick = onAddressDetails, text = stringResource(id = R.string.profile_see_address_details))
|
||||
if (isAddressBookEnabled) {
|
||||
TertiaryButton(onClick = onAddressBook, text = stringResource(id = R.string.profile_address_book))
|
||||
}
|
||||
TertiaryButton(onClick = onSettings, text = stringResource(id = R.string.profile_settings))
|
||||
Divider()
|
||||
if (isCoinholderVoteEnabled) {
|
||||
TertiaryButton(onClick = onCoinholderVote, text = stringResource(id = R.string.profile_coinholder_vote))
|
||||
}
|
||||
TertiaryButton(onClick = onSupport, text = stringResource(id = R.string.profile_support))
|
||||
TertiaryButton(onClick = onAbout, text = stringResource(id = R.string.profile_about))
|
||||
PrimaryButton(onClick = onAddressDetails, text = stringResource(id = R.string.receive_see_address_details))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +125,7 @@ private fun QrCode(data: String, size: Dp, modifier: Modifier) {
|
|||
|
||||
Image(
|
||||
bitmap = qrCodeImage,
|
||||
contentDescription = stringResource(R.string.profile_qr_code_content_description),
|
||||
contentDescription = stringResource(R.string.receive_qr_code_content_description),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
|
@ -24,7 +24,6 @@ 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.Header
|
||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
|
||||
|
@ -94,7 +93,6 @@ private fun SeedMainContent(
|
|||
.verticalScroll(rememberScrollState())
|
||||
.padding(top = paddingValues.calculateTopPadding())
|
||||
) {
|
||||
Header(stringResource(R.string.seed_header))
|
||||
Body(stringResource(R.string.seed_body))
|
||||
|
||||
ChipGrid(persistableWallet.seedPhrase.split)
|
||||
|
|
|
@ -7,19 +7,19 @@ import androidx.activity.viewModels
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
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.settings.view.Settings
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapSettings(
|
||||
goBack: () -> Unit,
|
||||
goWalletBackup: () -> Unit
|
||||
goBack: () -> Unit
|
||||
) {
|
||||
WrapSettings(
|
||||
activity = this,
|
||||
goBack = goBack,
|
||||
goWalletBackup = goWalletBackup
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,6 @@ internal fun MainActivity.WrapSettings(
|
|||
private fun WrapSettings(
|
||||
activity: ComponentActivity,
|
||||
goBack: () -> Unit,
|
||||
goWalletBackup: () -> Unit
|
||||
) {
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||
|
@ -45,8 +44,8 @@ private fun WrapSettings(
|
|||
isBackgroundSyncEnabled = isBackgroundSyncEnabled,
|
||||
isKeepScreenOnDuringSyncEnabled = isKeepScreenOnWhileSyncing,
|
||||
isAnalyticsEnabled = isAnalyticsEnabled,
|
||||
isRescanEnabled = ConfigurationEntries.IS_RESCAN_ENABLED.getValue(RemoteConfig.current),
|
||||
onBack = goBack,
|
||||
onBackupWallet = goWalletBackup,
|
||||
onRescanWallet = {
|
||||
walletViewModel.rescanBlockchain()
|
||||
},
|
||||
|
|
|
@ -10,6 +10,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
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
|
||||
|
@ -18,7 +21,11 @@ import androidx.compose.material3.Switch
|
|||
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.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -29,8 +36,6 @@ import co.electriccoin.zcash.ui.R
|
|||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
|
||||
@Preview("Settings")
|
||||
|
@ -42,8 +47,8 @@ fun PreviewSettings() {
|
|||
isBackgroundSyncEnabled = true,
|
||||
isKeepScreenOnDuringSyncEnabled = true,
|
||||
isAnalyticsEnabled = true,
|
||||
isRescanEnabled = true,
|
||||
onBack = {},
|
||||
onBackupWallet = {},
|
||||
onRescanWallet = {},
|
||||
onBackgroundSyncSettingsChanged = {},
|
||||
onIsKeepScreenOnDuringSyncSettingsChanged = {},
|
||||
|
@ -60,23 +65,25 @@ fun Settings(
|
|||
isBackgroundSyncEnabled: Boolean,
|
||||
isKeepScreenOnDuringSyncEnabled: Boolean,
|
||||
isAnalyticsEnabled: Boolean,
|
||||
isRescanEnabled: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onBackupWallet: () -> Unit,
|
||||
onRescanWallet: () -> Unit,
|
||||
onBackgroundSyncSettingsChanged: (Boolean) -> Unit,
|
||||
onIsKeepScreenOnDuringSyncSettingsChanged: (Boolean) -> Unit,
|
||||
onAnalyticsSettingsChanged: (Boolean) -> Unit
|
||||
) {
|
||||
Scaffold(topBar = {
|
||||
SettingsTopAppBar(onBack = onBack)
|
||||
SettingsTopAppBar(
|
||||
isRescanEnabled = isRescanEnabled,
|
||||
onBack = onBack,
|
||||
onRescanWallet = onRescanWallet
|
||||
)
|
||||
}) { paddingValues ->
|
||||
SettingsMainContent(
|
||||
paddingValues,
|
||||
isBackgroundSyncEnabled = isBackgroundSyncEnabled,
|
||||
isKeepScreenOnDuringSyncEnabled = isKeepScreenOnDuringSyncEnabled,
|
||||
isAnalyticsEnabled = isAnalyticsEnabled,
|
||||
onBackupWallet = onBackupWallet,
|
||||
onRescanWallet = onRescanWallet,
|
||||
onBackgroundSyncSettingsChanged = onBackgroundSyncSettingsChanged,
|
||||
onIsKeepScreenOnDuringSyncSettingsChanged = onIsKeepScreenOnDuringSyncSettingsChanged,
|
||||
onAnalyticsSettingsChanged = onAnalyticsSettingsChanged
|
||||
|
@ -86,7 +93,11 @@ fun Settings(
|
|||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
private fun SettingsTopAppBar(onBack: () -> Unit) {
|
||||
private fun SettingsTopAppBar(
|
||||
isRescanEnabled: Boolean,
|
||||
onBack: () -> Unit,
|
||||
onRescanWallet: () -> Unit
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.settings_header)) },
|
||||
navigationIcon = {
|
||||
|
@ -98,10 +109,38 @@ private fun SettingsTopAppBar(onBack: () -> Unit) {
|
|||
contentDescription = stringResource(R.string.settings_back_content_description)
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (isRescanEnabled) {
|
||||
TroubleshootingMenu(onRescanWallet)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TroubleshootingMenu(
|
||||
onRescanWallet: () -> Unit
|
||||
) {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(Icons.Default.MoreVert, contentDescription = stringResource(id = R.string.settings_overflow_content_description))
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(id = R.string.settings_rescan)) },
|
||||
onClick = {
|
||||
onRescanWallet()
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongParameterList")
|
||||
private fun SettingsMainContent(
|
||||
|
@ -109,8 +148,6 @@ private fun SettingsMainContent(
|
|||
isBackgroundSyncEnabled: Boolean,
|
||||
isKeepScreenOnDuringSyncEnabled: Boolean,
|
||||
isAnalyticsEnabled: Boolean,
|
||||
onBackupWallet: () -> Unit,
|
||||
onRescanWallet: () -> Unit,
|
||||
onBackgroundSyncSettingsChanged: (Boolean) -> Unit,
|
||||
onIsKeepScreenOnDuringSyncSettingsChanged: (Boolean) -> Unit,
|
||||
onAnalyticsSettingsChanged: (Boolean) -> Unit
|
||||
|
@ -119,8 +156,6 @@ private fun SettingsMainContent(
|
|||
Modifier
|
||||
.padding(top = paddingValues.calculateTopPadding())
|
||||
) {
|
||||
PrimaryButton(onClick = onBackupWallet, text = stringResource(id = R.string.settings_backup))
|
||||
TertiaryButton(onClick = onRescanWallet, text = stringResource(id = R.string.settings_rescan))
|
||||
SwitchWithLabel(
|
||||
label = stringResource(id = R.string.settings_enable_background_sync),
|
||||
state = isBackgroundSyncEnabled,
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="home_scan_content_description">Scan</string>
|
||||
<string name="home_profile_content_description">Profile</string>
|
||||
<string name="home_menu_content_description">Open menu</string>
|
||||
<string name="home_button_receive">Receive</string>
|
||||
<string name="home_button_send">Send</string>
|
||||
<string name="home_button_request">Request ZEC</string>
|
||||
|
||||
<string name="home_menu_seed_phrase">My secret phrase</string>
|
||||
<string name="home_menu_settings">Settings</string>
|
||||
<string name="home_menu_about">About</string>
|
||||
<string name="home_menu_support">Contact support</string>
|
||||
|
||||
|
||||
<string name="home_status_syncing_format" formatted="true">Syncing - <xliff:g id="synced_percent" example="50">%1$d</xliff:g>%%</string> <!-- double %% for escaping -->
|
||||
<string name="home_status_syncing_catchup">Syncing</string>
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="profile_title">Profile</string>
|
||||
<string name="profile_back_content_description">Back</string>
|
||||
<string name="profile_qr_code_content_description">QR code for unified address</string>
|
||||
<string name="profile_caption">Your UA Address</string>
|
||||
<string name="profile_see_address_details">See Address Details</string>
|
||||
<string name="profile_address_book">Address Book</string>
|
||||
<string name="profile_settings">Settings</string>
|
||||
<string name="profile_coinholder_vote">Coinholder Vote</string>
|
||||
<string name="profile_support">Support</string>
|
||||
<string name="profile_about">About</string>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="receive_title">Receive</string>
|
||||
<string name="receive_back_content_description">Back</string>
|
||||
<string name="receive_qr_code_content_description">QR code for unified address</string>
|
||||
<string name="receive_caption">Your UA Address</string>
|
||||
<string name="receive_see_address_details">See Address Details</string>
|
||||
|
||||
</resources>
|
|
@ -1,5 +1,5 @@
|
|||
<resources>
|
||||
<string name="seed_title">Backup Wallet</string>
|
||||
<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>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="send_title">Send ZEC</string>
|
||||
<string name="send_back_content_description">Back</string>
|
||||
<string name="send_scan_content_description">Scan</string>
|
||||
<string name="send_to">Who would you like to send ZEC to?</string>
|
||||
<string name="send_amount">How much?</string>
|
||||
<string name="send_memo">Memo</string>
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
<string name="settings_header">Settings</string>
|
||||
<string name="settings_back_content_description">Back</string>
|
||||
|
||||
<string name="settings_backup">Backup Wallet</string>
|
||||
<string name="settings_wipe">Wipe Wallet Data</string>
|
||||
|
||||
<string name="settings_rescan">Rescan Blockchain</string>
|
||||
<string name="settings_overflow_content_description">Additional settings</string>
|
||||
<string name="settings_rescan">Rescan blockchain</string>
|
||||
<string name="settings_enable_background_sync">Background sync</string>
|
||||
<string name="settings_enable_keep_screen_on">Keep screen on during sync</string>
|
||||
<string name="settings_enable_analytics">Report crashes</string>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="support_header">Support</string>
|
||||
<string name="support_header">Contact support</string>
|
||||
<string name="support_back_content_description">Back</string>
|
||||
|
||||
<string name="support_hint">How can we help?</string>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="wallet_address_title">Wallet Addresses</string>
|
||||
<string name="wallet_address_title">My wallet address</string>
|
||||
<string name="wallet_address_back_content_description">Back</string>
|
||||
<string name="wallet_address_unified">Your Unified Address</string>
|
||||
<string name="wallet_address_header_includes">which includes</string>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package co.electroniccoin.zcash.ui.screenshot
|
||||
|
||||
import android.content.Context
|
||||
|
@ -230,106 +232,29 @@ class ScreenshotTest : UiTestPrerequisites() {
|
|||
backupScreenshots(resContext, tag, composeTestRule)
|
||||
homeScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Profile screen
|
||||
// navigateTo(MainActivity.NAV_PROFILE)
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.home_profile_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
profileScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Settings is a subscreen of profile
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_settings))).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
settingsScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Back to profile
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.settings_back_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
// Address Details is a subscreen of profile
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_see_address_details))).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
addressDetailsScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Back to profile
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.wallet_address_back_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
// Contact Support is a subscreen of profile
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_support))).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
supportScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Back to profile
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.support_back_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_title))).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
// About is a subscreen of profile
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_about))).also {
|
||||
it.performScrollTo()
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
aboutScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// Back to profile
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.about_back_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
// Back to home
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.settings_back_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.Ready }
|
||||
|
||||
if (ConfigurationEntries.IS_REQUEST_ZEC_ENABLED.getValue(emptyConfiguration)) {
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.home_button_request))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
||||
requestZecScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.HOME)
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.secretState.value is SecretState.Ready }
|
||||
}
|
||||
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.home_button_send))).also {
|
||||
it.assertExists()
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.synchronizer.value != null }
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.spendingKey.value != null }
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
||||
// These are the buttons on the home screen
|
||||
navigateTo(NavigationTargets.SEND)
|
||||
sendZecScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.HOME)
|
||||
navigateTo(NavigationTargets.RECEIVE)
|
||||
receiveZecScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
// These are the hamburger menu items
|
||||
// We could manually click on each one, which is a better integration test but a worse screenshot test
|
||||
navigateTo(NavigationTargets.SEED)
|
||||
seedScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.SETTINGS)
|
||||
settingsScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.SUPPORT)
|
||||
supportScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.ABOUT)
|
||||
aboutScreenshots(resContext, tag, composeTestRule)
|
||||
|
||||
navigateTo(NavigationTargets.WALLET_ADDRESS_DETAILS)
|
||||
addressDetailsScreenshots(resContext, tag, composeTestRule)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,17 +411,12 @@ private fun homeScreenshots(resContext: Context, tag: String, composeTestRule: A
|
|||
it.assertExists()
|
||||
ScreenshotTest.takeScreenshot(tag, "Home 1")
|
||||
}
|
||||
}
|
||||
|
||||
private fun profileScreenshots(resContext: Context, tag: String, composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>) {
|
||||
// Note: increased timeout limit to satisfy time needed for SDK initialization
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.addresses.value != null }
|
||||
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.profile_title))).also {
|
||||
composeTestRule.onNode(hasContentDescription(resContext.getString(R.string.home_menu_content_description))).also {
|
||||
it.assertExists()
|
||||
it.performClick()
|
||||
ScreenshotTest.takeScreenshot(tag, "Home 2 - Menu")
|
||||
}
|
||||
|
||||
ScreenshotTest.takeScreenshot(tag, "Profile 1")
|
||||
}
|
||||
|
||||
private fun settingsScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||
|
@ -515,6 +435,8 @@ private fun addressDetailsScreenshots(resContext: Context, tag: String, composeT
|
|||
ScreenshotTest.takeScreenshot(tag, "Addresses 1")
|
||||
}
|
||||
|
||||
// This screen is not currently navigable from the app
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private fun requestZecScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.request_title))).also {
|
||||
it.assertExists()
|
||||
|
@ -523,7 +445,29 @@ private fun requestZecScreenshots(resContext: Context, tag: String, composeTestR
|
|||
ScreenshotTest.takeScreenshot(tag, "Request 1")
|
||||
}
|
||||
|
||||
private fun sendZecScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||
private fun receiveZecScreenshots(resContext: Context, tag: String, composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>) {
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.addresses.value != null }
|
||||
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.receive_title))).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
ScreenshotTest.takeScreenshot(tag, "Receive 1")
|
||||
|
||||
composeTestRule.onNodeWithText(resContext.getString(R.string.receive_see_address_details)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
ScreenshotTest.takeScreenshot(tag, "Address details")
|
||||
}
|
||||
|
||||
private fun sendZecScreenshots(resContext: Context, tag: String, composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>) {
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.synchronizer.value != null }
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.spendingKey.value != null }
|
||||
composeTestRule.waitUntil(DEFAULT_TIMEOUT_MILLISECONDS) { composeTestRule.activity.walletViewModel.walletSnapshot.value != null }
|
||||
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.send_title))).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
@ -564,3 +508,11 @@ private fun aboutScreenshots(resContext: Context, tag: String, composeTestRule:
|
|||
|
||||
ScreenshotTest.takeScreenshot(tag, "About 1")
|
||||
}
|
||||
|
||||
private fun seedScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||
composeTestRule.onNode(hasText(resContext.getString(R.string.seed_title))).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
ScreenshotTest.takeScreenshot(tag, "Seed 1")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue