diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9b2cce9f..029a0ac4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ tools:targetApi="29" /> diff --git a/app/src/zcashmainnetDebug/AndroidManifest.xml b/app/src/zcashmainnetDebug/AndroidManifest.xml index e14f7718..c4408c40 100644 --- a/app/src/zcashmainnetDebug/AndroidManifest.xml +++ b/app/src/zcashmainnetDebug/AndroidManifest.xml @@ -10,7 +10,7 @@ android:label="@string/app_name"> ? = withContext(Dispatchers.IO) { listFiles() } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt index ab41d2a0..5620483d 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt @@ -5,25 +5,47 @@ package co.electriccoin.zcash.ui.design.component import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AccountBox +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview import co.electriccoin.zcash.ui.design.R import co.electriccoin.zcash.ui.design.theme.ZcashTheme +@Preview +@Composable +private fun TextComposablePreview() { + ZcashTheme(forceDarkMode = false) { + GradientSurface { + Column { + Reference(text = "Test reference text", onClick = {}) + Reference(text = "User account", imageVector = Icons.Outlined.AccountBox, onClick = {}) + // Preview the rest of the composable + } + } + } +} + @Composable fun Header( text: String, @@ -36,7 +58,23 @@ fun Header( color = color, textAlign = textAlign, modifier = modifier, - style = MaterialTheme.typography.headlineLarge, + style = ZcashTheme.typography.secondary.headlineLarge, + ) +} + +@Composable +fun SubHeader( + text: String, + modifier: Modifier = Modifier, + textAlign: TextAlign = TextAlign.Start, + color: Color = ZcashTheme.colors.onBackgroundHeader, +) { + Text( + text = text, + color = color, + textAlign = textAlign, + modifier = modifier, + style = ZcashTheme.typography.secondary.headlineSmall, ) } @@ -171,33 +209,46 @@ fun ListHeader( ) } +@Suppress("LongParameterList") @Composable fun Reference( text: String, + onClick: () -> Unit, modifier: Modifier = Modifier, - textAlign: TextAlign = TextAlign.Start, - onClick: () -> Unit + textAlign: TextAlign = TextAlign.Center, + imageVector: ImageVector? = null, + imageContentDescription: String? = null ) { - Box( + Row( modifier = Modifier .wrapContentSize() .clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner)) .clickable { onClick() } - .then(modifier) + .padding(all = ZcashTheme.dimens.spacingDefault) + .then(modifier), + verticalAlignment = Alignment.CenterVertically ) { + imageVector?.let { + Icon( + imageVector = imageVector, + contentDescription = imageContentDescription + ) + } + Spacer(modifier = Modifier.padding(ZcashTheme.dimens.spacingTiny)) Text( text = text, + textAlign = TextAlign.Center, style = - MaterialTheme.typography.bodyLarge + ZcashTheme.typography.primary.bodyLarge .merge( TextStyle( color = ZcashTheme.colors.reference, textAlign = textAlign, - textDecoration = TextDecoration.Underline + textDecoration = TextDecoration.Underline, + fontWeight = FontWeight.SemiBold ) - ), - modifier = Modifier.padding(all = ZcashTheme.dimens.spacingDefault) + ) ) } } @@ -220,7 +271,10 @@ fun HeaderWithZecIcon( style = ZcashTheme.extendedTypography.zecBalance, color = MaterialTheme.colorScheme.onBackground, maxLines = 1, - modifier = Modifier.basicMarquee().then(modifier) + modifier = + Modifier + .basicMarquee() + .then(modifier) ) } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt index 5f3276c6..97f18bbe 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt @@ -19,13 +19,10 @@ data class ExtendedColors( val progressBackground: Color, val chipIndex: Color, val textFieldHint: Color, + val textDescription: Color, val layoutStroke: Color, val overlay: Color, val highlight: Color, - val addressHighlightBorder: Color, - val addressHighlightUnified: Color, - val addressHighlightSapling: Color, - val addressHighlightTransparent: Color, val dangerous: Color, val onDangerous: Color, val reference: Color, diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt index 1ba00f42..97f71d5f 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt @@ -27,6 +27,8 @@ internal object Dark { val textCaption = Color(0xFFFFFFFF) val textChipIndex = Color(0xFFFFB900) val textFieldHint = Color(0xFFB7B7B7) + val textDescription = Color(0xFF777777) + val layoutStroke = Color(0xFFFFFFFF) val primaryButton = Color(0xFFFFFFFF) @@ -90,10 +92,10 @@ internal object Light { val textCaption = Color(0xFF000000) val textChipIndex = Color(0xFFEE8592) val textFieldHint = Color(0xFFB7B7B7) + val textDescription = Color(0xFF777777) + val layoutStroke = Color(0xFF000000) - // TODO [#159]: The button colors are wrong for light - // TODO [#159]: https://github.com/Electric-Coin-Company/zashi-android/issues/159 val primaryButton = Color(0xFF000000) val primaryButtonPressed = Color(0xFF000000) val primaryButtonDisabled = Color(0xFF000000) @@ -118,13 +120,6 @@ internal object Light { val overlay = Color(0x22000000) val highlight = Color(0xFFFFD800) - // TODO [#159]: The colors are wrong for light theme - // TODO [#159]: https://github.com/Electric-Coin-Company/zashi-android/issues/159 - val addressHighlightBorder = Color(0xFF525252) - val addressHighlightUnified = Color(0xFFFFD800) - val addressHighlightSapling = Color(0xFF1BBFF6) - val addressHighlightTransparent = Color(0xFF97999A) - val dangerous = Color(0xFFEC0008) val onDangerous = Color(0xFFFFFFFF) @@ -179,13 +174,10 @@ internal val DarkExtendedColorPalette = progressBackground = Dark.progressBackground, chipIndex = Dark.textChipIndex, textFieldHint = Dark.textFieldHint, + textDescription = Dark.textDescription, layoutStroke = Dark.layoutStroke, overlay = Dark.overlay, highlight = Dark.highlight, - addressHighlightBorder = Dark.addressHighlightBorder, - addressHighlightUnified = Dark.addressHighlightUnified, - addressHighlightSapling = Dark.addressHighlightSapling, - addressHighlightTransparent = Dark.addressHighlightTransparent, dangerous = Dark.dangerous, onDangerous = Dark.onDangerous, disabledButtonTextColor = Dark.disabledButtonTextColor, @@ -213,13 +205,10 @@ internal val LightExtendedColorPalette = progressBackground = Light.progressBackground, chipIndex = Light.textChipIndex, textFieldHint = Light.textFieldHint, + textDescription = Light.textDescription, layoutStroke = Light.layoutStroke, overlay = Light.overlay, highlight = Light.highlight, - addressHighlightBorder = Light.addressHighlightBorder, - addressHighlightUnified = Light.addressHighlightUnified, - addressHighlightSapling = Light.addressHighlightSapling, - addressHighlightTransparent = Light.addressHighlightTransparent, dangerous = Light.dangerous, onDangerous = Light.onDangerous, disabledButtonTextColor = Light.disabledButtonTextColor, @@ -249,13 +238,10 @@ internal val LocalExtendedColors = progressBackground = Color.Unspecified, chipIndex = Color.Unspecified, textFieldHint = Color.Unspecified, + textDescription = Color.Unspecified, layoutStroke = Color.Unspecified, overlay = Color.Unspecified, highlight = Color.Unspecified, - addressHighlightBorder = Color.Unspecified, - addressHighlightUnified = Color.Unspecified, - addressHighlightSapling = Color.Unspecified, - addressHighlightTransparent = Color.Unspecified, dangerous = Color.Unspecified, onDangerous = Color.Unspecified, disabledButtonTextColor = Color.Unspecified, diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt index 9ce431bc..dd164934 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt @@ -107,7 +107,7 @@ internal val SecondaryTypography = headlineSmall = TextStyle( fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.Bold, + fontWeight = FontWeight.SemiBold, fontSize = 20.sp, textAlign = TextAlign.Center ), diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt deleted file mode 100644 index 3880a57f..00000000 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt +++ /dev/null @@ -1,185 +0,0 @@ -package co.electriccoin.zcash.ui.screen.address.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.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import androidx.test.filters.MediumTest -import cash.z.ecc.android.sdk.fixture.WalletAddressesFixture -import cash.z.ecc.android.sdk.model.WalletAddresses -import co.electriccoin.zcash.test.UiTestPrerequisites -import co.electriccoin.zcash.ui.R -import co.electriccoin.zcash.ui.design.theme.ZcashTheme -import co.electriccoin.zcash.ui.screen.address.WalletAddressesTag -import co.electriccoin.zcash.ui.test.getStringResource -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Rule -import org.junit.Test -import java.util.concurrent.atomic.AtomicInteger - -class WalletAddressViewTest : UiTestPrerequisites() { - @get:Rule - val composeTestRule = createComposeRule() - - @Test - @MediumTest - fun initial_screen_setup() = - runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) - - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { - it.assertExists() - } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { - it.assertExists() - } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { - it.assertExists() - } - - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertExists() - } - - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertDoesNotExist() - } - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertDoesNotExist() - } - } - - @Test - @MediumTest - fun unified_collapses() = - runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) - - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertExists() - } - - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { - it.assertExists() - it.performClick() - } - - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertDoesNotExist() - } - } - - @Test - @MediumTest - fun sapling_expands() = - runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) - - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertDoesNotExist() - } - - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { - it.assertExists() - it.performClick() - } - - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertExists() - } - } - - @Test - @MediumTest - fun transparent_expands() = - runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) - - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertDoesNotExist() - } - - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { - it.assertExists() - it.performClick() - } - - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertExists() - } - } - - @Test - @MediumTest - fun back_clicked() = - runTest { - val testSetup = newTestSetup(WalletAddressesFixture.new()) - - assertEquals(0, testSetup.getOnBackCount()) - - composeTestRule.onNodeWithContentDescription( - getStringResource(R.string.wallet_address_back_content_description) - ).also { - it.performClick() - } - - assertEquals(1, testSetup.getOnBackCount()) - } - - @Test - @MediumTest - fun copy_to_clipboard_clicked() = - runTest { - val testSetup = newTestSetup(WalletAddressesFixture.new()) - - assertEquals(0, testSetup.getOnCopyToClipboardCount()) - - composeTestRule.onNodeWithTag( - WalletAddressesTag.WALLET_ADDRESS - ).also { - it.performClick() - } - - assertEquals(1, testSetup.getOnCopyToClipboardCount()) - } - - private fun newTestSetup(initialState: WalletAddresses) = TestSetup(composeTestRule, initialState) - - private class TestSetup(private val composeTestRule: ComposeContentTestRule, initialState: WalletAddresses) { - private val onBackCount = AtomicInteger(0) - private val onCopyToClipboardCount = AtomicInteger(0) - - fun getOnBackCount(): Int { - composeTestRule.waitForIdle() - return onBackCount.get() - } - - fun getOnCopyToClipboardCount(): Int { - composeTestRule.waitForIdle() - return onCopyToClipboardCount.get() - } - - init { - composeTestRule.setContent { - ZcashTheme { - WalletAddresses( - walletAddresses = initialState, - onCopyToClipboard = { - onCopyToClipboardCount.incrementAndGet() - }, - onBack = { - onBackCount.incrementAndGet() - } - ) - } - } - } - } -} diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt index 1decdd84..0c4c5858 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt @@ -4,6 +4,7 @@ import android.content.Intent import androidx.test.filters.SmallTest import co.electriccoin.zcash.ui.fixture.VersionInfoFixture import co.electriccoin.zcash.ui.test.getAppContext +import co.electriccoin.zcash.ui.util.FileShareUtil import org.junit.Assert.assertEquals import org.junit.Test import kotlin.io.path.pathString @@ -26,6 +27,7 @@ class FileShareUtilTest { FileShareUtil.newShareContentIntent( context = getAppContext(), dataFilePath = tempFilePath.pathString, + fileType = FileShareUtil.ZASHI_INTERNAL_DATA_MIME_TYPE, versionInfo = VersionInfoFixture.new() ) assertEquals(intent.action, Intent.ACTION_VIEW) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt index 848beafd..44643ae6 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt @@ -2,9 +2,11 @@ package co.electriccoin.zcash.ui.screen.receive.view import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.filters.MediumTest -import cash.z.ecc.android.sdk.fixture.WalletAddressFixture -import cash.z.ecc.android.sdk.model.WalletAddress +import cash.z.ecc.android.sdk.fixture.WalletAddressesFixture +import cash.z.ecc.android.sdk.model.WalletAddresses import co.electriccoin.zcash.test.UiTestPrerequisites +import co.electriccoin.zcash.ui.common.model.VersionInfo +import co.electriccoin.zcash.ui.fixture.VersionInfoFixture import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -18,7 +20,12 @@ class ReceiveViewScreenBrightnessTest : UiTestPrerequisites() { @MediumTest fun testBrightnessDefaultState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + // Using isDebuggable flag to have brightness toggle in the UI + val testSetup = + newTestSetup( + WalletAddressesFixture.new(), + VersionInfoFixture.new(isDebuggable = true) + ) assertEquals(0, testSetup.getScreenBrightnessCount()) } @@ -27,7 +34,12 @@ class ReceiveViewScreenBrightnessTest : UiTestPrerequisites() { @MediumTest fun testBrightnessOnState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + // Using isDebuggable flag to have brightness toggle in the UI + val testSetup = + newTestSetup( + WalletAddressesFixture.new(), + VersionInfoFixture.new(isDebuggable = true) + ) assertEquals(false, testSetup.getOnAdjustBrightness()) assertEquals(0, testSetup.getScreenBrightnessCount()) @@ -38,5 +50,8 @@ class ReceiveViewScreenBrightnessTest : UiTestPrerequisites() { assertEquals(1, testSetup.getScreenBrightnessCount()) } - private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) + private fun newTestSetup( + walletAddresses: WalletAddresses, + versionInfo: VersionInfo + ) = ReceiveViewTestSetup(composeTestRule, walletAddresses, versionInfo) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt index 3444f482..fd4929d7 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt @@ -2,9 +2,11 @@ package co.electriccoin.zcash.ui.screen.receive.view import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.filters.MediumTest -import cash.z.ecc.android.sdk.fixture.WalletAddressFixture -import cash.z.ecc.android.sdk.model.WalletAddress +import cash.z.ecc.android.sdk.fixture.WalletAddressesFixture +import cash.z.ecc.android.sdk.model.WalletAddresses import co.electriccoin.zcash.test.UiTestPrerequisites +import co.electriccoin.zcash.ui.common.model.VersionInfo +import co.electriccoin.zcash.ui.fixture.VersionInfoFixture import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -18,7 +20,11 @@ class ReceiveViewScreenTimeoutTest : UiTestPrerequisites() { @MediumTest fun testTimeoutDefaultState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + val testSetup = + newTestSetup( + WalletAddressesFixture.new(), + VersionInfoFixture.new() + ) assertEquals(0, testSetup.getScreenTimeoutCount()) } @@ -27,7 +33,12 @@ class ReceiveViewScreenTimeoutTest : UiTestPrerequisites() { @MediumTest fun testTimeoutOnState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + // Using isDebuggable flag to have brightness toggle in the UI + val testSetup = + newTestSetup( + WalletAddressesFixture.new(), + VersionInfoFixture.new(isDebuggable = true) + ) assertEquals(false, testSetup.getOnAdjustBrightness()) assertEquals(0, testSetup.getScreenTimeoutCount()) @@ -38,5 +49,8 @@ class ReceiveViewScreenTimeoutTest : UiTestPrerequisites() { assertEquals(1, testSetup.getScreenTimeoutCount()) } - private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) + private fun newTestSetup( + walletAddresses: WalletAddresses, + versionInfo: VersionInfo + ) = ReceiveViewTestSetup(composeTestRule, walletAddresses, versionInfo) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt index e2156f1a..fc7495c5 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt @@ -5,8 +5,8 @@ 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 cash.z.ecc.android.sdk.fixture.WalletAddressesFixture +import cash.z.ecc.android.sdk.model.WalletAddresses import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.test.getStringResource import kotlinx.coroutines.test.runTest @@ -14,6 +14,9 @@ import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test +// TODO [#1184]: Improve ReceiveScreen UI tests +// TODO [#1184]: https://github.com/Electric-Coin-Company/zashi-android/issues/1184 + /* * 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 @@ -27,11 +30,11 @@ class ReceiveViewTest { @MediumTest fun setup() = runTest { - val walletAddress = WalletAddressFixture.unified() - newTestSetup(walletAddress) + val walletAddresses = WalletAddressesFixture.new() + newTestSetup(walletAddresses) // Enable substring for ellipsizing - composeTestRule.onNodeWithText(walletAddress.address, substring = true).also { + composeTestRule.onNodeWithText(walletAddresses.unified.address, substring = true).also { it.assertExists() } } @@ -40,7 +43,7 @@ class ReceiveViewTest { @MediumTest fun click_settings_test() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + val testSetup = newTestSetup(WalletAddressesFixture.new()) assertEquals(0, testSetup.getOnSettingsCount()) @@ -53,23 +56,5 @@ class ReceiveViewTest { assertEquals(1, testSetup.getOnSettingsCount()) } - @Test - @MediumTest - fun address_details() = - runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) - - assertEquals(0, testSetup.getOnAddressDetailsCount()) - - composeTestRule.onNodeWithText( - text = getStringResource(R.string.receive_see_address_details), - ignoreCase = true - ).also { - it.performClick() - } - - assertEquals(1, testSetup.getOnAddressDetailsCount()) - } - - private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) + private fun newTestSetup(walletAddresses: WalletAddresses) = ReceiveViewTestSetup(composeTestRule, walletAddresses) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTestSetup.kt index 9458dadd..e1aacfc2 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTestSetup.kt @@ -1,23 +1,27 @@ package co.electriccoin.zcash.ui.screen.receive.view +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick -import cash.z.ecc.android.sdk.model.WalletAddress +import cash.z.ecc.android.sdk.model.WalletAddresses import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.common.LocalScreenBrightness import co.electriccoin.zcash.ui.common.LocalScreenTimeout import co.electriccoin.zcash.ui.common.ScreenBrightness import co.electriccoin.zcash.ui.common.ScreenTimeout +import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.design.theme.ZcashTheme +import co.electriccoin.zcash.ui.fixture.VersionInfoFixture import co.electriccoin.zcash.ui.test.getStringResource import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger class ReceiveViewTestSetup( private val composeTestRule: ComposeContentTestRule, - walletAddress: WalletAddress + walletAddresses: WalletAddresses, + versionInfo: VersionInfo = VersionInfoFixture.new() ) { private val onSettingsCount = AtomicInteger(0) private val onAddressDetailsCount = AtomicInteger(0) @@ -53,16 +57,17 @@ class ReceiveViewTestSetup( ZcashTheme { ZcashTheme { Receive( - walletAddress, + walletAddress = walletAddresses, + snackbarHostState = SnackbarHostState(), onSettings = { onSettingsCount.getAndIncrement() }, - onAddressDetails = { - onAddressDetailsCount.getAndIncrement() - }, onAdjustBrightness = { onAdjustBrightness.getAndSet(it) }, + onAddrCopyToClipboard = {}, + onQrImageShare = {}, + versionInfo = versionInfo ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/ShareFileProvider.kt b/ui-lib/src/main/java/co/electriccoin/zcash/global/ShareFileProvider.kt similarity index 80% rename from ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/ShareFileProvider.kt rename to ui-lib/src/main/java/co/electriccoin/zcash/global/ShareFileProvider.kt index 7eb13eb2..cddfd7f4 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/ShareFileProvider.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/global/ShareFileProvider.kt @@ -1,4 +1,4 @@ -package co.electriccoin.zcash.ui.screen.exportdata.util +package co.electriccoin.zcash.global import androidx.core.content.FileProvider import co.electriccoin.zcash.ui.R diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt index 86e01358..5cfaae4c 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt @@ -20,11 +20,9 @@ import co.electriccoin.zcash.ui.NavigationTargets.SCAN import co.electriccoin.zcash.ui.NavigationTargets.SEED_RECOVERY import co.electriccoin.zcash.ui.NavigationTargets.SETTINGS import co.electriccoin.zcash.ui.NavigationTargets.SUPPORT -import co.electriccoin.zcash.ui.NavigationTargets.WALLET_ADDRESS_DETAILS import co.electriccoin.zcash.ui.configuration.ConfigurationEntries 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.exportdata.WrapExportPrivateData import co.electriccoin.zcash.ui.screen.history.WrapHistory import co.electriccoin.zcash.ui.screen.home.WrapHome @@ -51,7 +49,6 @@ internal fun MainActivity.Navigation() { onPageChange = { homeViewModel.screenIndex.value = it }, - goAddressDetails = { navController.navigateJustOnce(WALLET_ADDRESS_DETAILS) }, goBack = { finish() }, goHistory = { navController.navigateJustOnce(HISTORY) }, goSettings = { navController.navigateJustOnce(SETTINGS) }, @@ -72,13 +69,6 @@ internal fun MainActivity.Navigation() { WrapCheckForUpdate() } } - composable(WALLET_ADDRESS_DETAILS) { - WrapWalletAddresses( - goBack = { - navController.popBackStackJustOnce(WALLET_ADDRESS_DETAILS) - } - ) - } composable(SETTINGS) { WrapSettings( goAbout = { @@ -201,5 +191,4 @@ object NavigationTargets { const val SEND = "send" const val SETTINGS = "settings" const val SUPPORT = "support" - const val WALLET_ADDRESS_DETAILS = "wallet_address_details" } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt deleted file mode 100644 index 190673be..00000000 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt +++ /dev/null @@ -1,48 +0,0 @@ -@file:Suppress("ktlint:standard:filename") - -package co.electriccoin.zcash.ui.screen.address - -import androidx.activity.ComponentActivity -import androidx.activity.viewModels -import androidx.compose.runtime.Composable -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import co.electriccoin.zcash.spackle.ClipboardManagerUtil -import co.electriccoin.zcash.ui.MainActivity -import co.electriccoin.zcash.ui.R -import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel -import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator -import co.electriccoin.zcash.ui.screen.address.view.WalletAddresses - -@Composable -internal fun MainActivity.WrapWalletAddresses(goBack: () -> Unit) { - WrapWalletAddresses(this, goBack) -} - -@Composable -private fun WrapWalletAddresses( - activity: ComponentActivity, - goBack: () -> Unit -) { - val walletViewModel by activity.viewModels() - - val walletAddresses = walletViewModel.addresses.collectAsStateWithLifecycle().value - - if (null == walletAddresses) { - // TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer - // TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available - // TODO [#1146]: https://github.com/Electric-Coin-Company/zashi-android/issues/1146 - CircularScreenProgressIndicator() - } else { - WalletAddresses( - walletAddresses, - goBack, - onCopyToClipboard = { address -> - ClipboardManagerUtil.copyToClipboard( - activity.applicationContext, - activity.getString(R.string.wallet_address_clipboard_tag), - address - ) - }, - ) - } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/WalletAddressesTag.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/WalletAddressesTag.kt deleted file mode 100644 index 78c16957..00000000 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/WalletAddressesTag.kt +++ /dev/null @@ -1,8 +0,0 @@ -package co.electriccoin.zcash.ui.screen.address - -/** - * These are only used for automated testing. - */ -object WalletAddressesTag { - const val WALLET_ADDRESS = "wallet_address_tag" -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt deleted file mode 100644 index 38218a97..00000000 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt +++ /dev/null @@ -1,295 +0,0 @@ -@file:Suppress("TooManyFunctions") - -package co.electriccoin.zcash.ui.screen.address.view - -import androidx.compose.foundation.Image -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.ArrowDropDownCircle -import androidx.compose.material3.Divider -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -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.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.ColorPainter -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import cash.z.ecc.android.sdk.fixture.WalletAddressesFixture -import cash.z.ecc.android.sdk.model.WalletAddresses -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.ListHeader -import co.electriccoin.zcash.ui.design.component.ListItem -import co.electriccoin.zcash.ui.design.theme.ZcashTheme -import co.electriccoin.zcash.ui.screen.address.WalletAddressesTag -import kotlinx.coroutines.runBlocking - -@Preview("WalletAddresses") -@Composable -private fun ComposablePreview() { - ZcashTheme(forceDarkMode = false) { - GradientSurface { - WalletAddresses( - runBlocking { WalletAddressesFixture.new() }, - onBack = {}, - onCopyToClipboard = {} - ) - } - } -} - -@Composable -fun WalletAddresses( - walletAddresses: WalletAddresses, - onBack: () -> Unit, - onCopyToClipboard: (String) -> Unit -) { - Column { - WalletDetailTopAppBar(onBack) - WalletDetailAddresses( - walletAddresses = walletAddresses, - onCopyToClipboard = onCopyToClipboard, - modifier = - Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) - } -} - -@Composable -@OptIn(ExperimentalMaterial3Api::class) -private fun WalletDetailTopAppBar(onBack: () -> Unit) { - TopAppBar( - title = { - Text( - text = stringResource(id = R.string.wallet_address_title) - ) - }, - navigationIcon = { - IconButton( - onClick = onBack - ) { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = stringResource(R.string.wallet_address_back_content_description) - ) - } - } - ) -} - -private val BIG_INDICATOR_WIDTH = 24.dp -private val SMALL_INDICATOR_WIDTH = 16.dp - -@Composable -private fun WalletDetailAddresses( - walletAddresses: WalletAddresses, - onCopyToClipboard: (String) -> Unit, - modifier: Modifier = Modifier -) { - Column(modifier) { - Row( - Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) - ) { - Image( - painter = ColorPainter(ZcashTheme.colors.highlight), - contentDescription = "", - modifier = - Modifier - .fillMaxHeight() - .width(BIG_INDICATOR_WIDTH) - ) - - Column(Modifier.fillMaxWidth()) { - ExpandableRow( - title = stringResource(R.string.wallet_address_unified), - content = walletAddresses.unified.address, - isInitiallyExpanded = true, - onCopyToClipboard = onCopyToClipboard - ) - - Box(Modifier.height(IntrinsicSize.Min)) { - Divider(modifier = Modifier.fillMaxHeight()) - ListHeader( - text = stringResource(R.string.wallet_address_header_includes), - modifier = Modifier.padding(all = ZcashTheme.dimens.spacingSmall) - ) - } - - SaplingAddress( - saplingAddress = walletAddresses.sapling.address, - onCopyToClipboard = onCopyToClipboard, - modifier = - Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) - ) - - TransparentAddress( - transparentAddress = walletAddresses.transparent.address, - onCopyToClipboard = onCopyToClipboard, - modifier = - Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) - ) - } - } - } -} - -// Note: The addresses code below has opportunities to be made more DRY. -// Refactoring that is being held off until issue #160 is fixed, since knowledge -// of row position will be needed. - -@Composable -private fun SaplingAddress( - saplingAddress: String, - onCopyToClipboard: (String) -> Unit, - modifier: Modifier = Modifier -) { - Row(modifier) { - SmallIndicator(ZcashTheme.colors.addressHighlightSapling) - - ExpandableRow( - title = stringResource(R.string.wallet_address_sapling), - content = saplingAddress, - isInitiallyExpanded = false, - onCopyToClipboard = onCopyToClipboard - ) - } -} - -@Composable -private fun TransparentAddress( - transparentAddress: String, - onCopyToClipboard: (String) -> Unit, - modifier: Modifier = Modifier -) { - Row(modifier) { - SmallIndicator(ZcashTheme.colors.addressHighlightTransparent) - ExpandableRow( - title = stringResource(R.string.wallet_address_transparent), - content = transparentAddress, - isInitiallyExpanded = false, - onCopyToClipboard = onCopyToClipboard - ) - } -} - -@Composable -private fun ExpandableRow( - title: String, - content: String, - isInitiallyExpanded: Boolean, - onCopyToClipboard: (String) -> Unit -) { - var expandedState by rememberSaveable { mutableStateOf(isInitiallyExpanded) } - - Column { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = - Modifier - .defaultMinSize(minHeight = 48.dp) - .clickable { expandedState = !expandedState } - .padding( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingTiny - ) - ) { - ListItem(text = title) - Spacer( - modifier = - Modifier - .fillMaxWidth() - .weight(MINIMAL_WEIGHT) - ) - ExpandableArrow(expandedState) - } - if (expandedState) { - Body( - content, - modifier = - Modifier - .clickable { onCopyToClipboard(content) } - .padding( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingTiny - ) - .testTag(WalletAddressesTag.WALLET_ADDRESS) - ) - } - } -} - -@Composable -private fun SmallIndicator(color: Color) { - // TODO [#160]: Border is not the right implementation here, as it causes double thickness for the middle item - // TODO [#160]: https://github.com/Electric-Coin-Company/zashi-android/issues/160 - Image( - modifier = - Modifier - .fillMaxHeight() - .width(SMALL_INDICATOR_WIDTH) - .border(1.dp, ZcashTheme.colors.addressHighlightBorder), - painter = ColorPainter(color), - contentDescription = "" - ) -} - -private const val NINETY_DEGREES = 90f - -@Composable -private fun ExpandableArrow(isExpanded: Boolean) { - Icon( - imageVector = Icons.Filled.ArrowDropDownCircle, - contentDescription = - if (isExpanded) { - stringResource(id = R.string.wallet_address_hide) - } else { - stringResource(id = R.string.wallet_address_show) - }, - modifier = - if (isExpanded) { - Modifier - } else { - Modifier.rotate(NINETY_DEGREES) - }, - tint = MaterialTheme.colorScheme.onBackground - ) -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt index 78f8b99f..78955ecf 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt @@ -17,8 +17,8 @@ import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator -import co.electriccoin.zcash.ui.screen.exportdata.util.FileShareUtil import co.electriccoin.zcash.ui.screen.exportdata.view.ExportPrivateData +import co.electriccoin.zcash.ui.util.FileShareUtil import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow @@ -93,6 +93,7 @@ fun shareData( context = context, network = ZcashNetwork.fromResources(context) ), + fileType = FileShareUtil.ZASHI_INTERNAL_DATA_MIME_TYPE, versionInfo = VersionInfo.new(context.applicationContext) ) runCatching { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt index e558a096..0adc63d7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt @@ -26,7 +26,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow @Composable internal fun MainActivity.WrapHome( onPageChange: (HomeScreenIndex) -> Unit, - goAddressDetails: () -> Unit, goBack: () -> Unit, goHistory: () -> Unit, goSettings: () -> Unit, @@ -36,7 +35,6 @@ internal fun MainActivity.WrapHome( WrapHome( this, onPageChange = onPageChange, - goAddressDetails = goAddressDetails, goBack = goBack, goHistory = goHistory, goScan = goScan, @@ -49,7 +47,6 @@ internal fun MainActivity.WrapHome( @Composable internal fun WrapHome( activity: ComponentActivity, - goAddressDetails: () -> Unit, goBack: () -> Unit, goHistory: () -> Unit, goSettings: () -> Unit, @@ -117,7 +114,6 @@ internal fun WrapHome( WrapReceive( activity = activity, onSettings = goSettings, - onAddressDetails = goAddressDetails, ) } ), diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt index 015cc989..7ad512d7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt @@ -2,48 +2,134 @@ package co.electriccoin.zcash.ui.screen.receive +import android.content.Context +import android.graphics.Bitmap import androidx.activity.ComponentActivity import androidx.activity.viewModels +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.graphics.asAndroidBitmap import androidx.lifecycle.compose.collectAsStateWithLifecycle -import cash.z.ecc.android.sdk.model.WalletAddresses +import co.electriccoin.zcash.spackle.ClipboardManagerUtil +import co.electriccoin.zcash.spackle.Twig +import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend +import co.electriccoin.zcash.ui.R +import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel -import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator import co.electriccoin.zcash.ui.screen.receive.view.Receive +import co.electriccoin.zcash.ui.util.FileShareUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File @Composable internal fun WrapReceive( activity: ComponentActivity, onSettings: () -> Unit, - onAddressDetails: () -> Unit, ) { val viewModel by activity.viewModels() val walletAddresses = viewModel.addresses.collectAsStateWithLifecycle().value - WrapReceive( - walletAddresses, + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + val versionInfo = VersionInfo.new(activity.applicationContext) + + Receive( + walletAddress = walletAddresses, + snackbarHostState = snackbarHostState, + onAdjustBrightness = { /* Just for testing purposes */ }, + onAddrCopyToClipboard = { address -> + ClipboardManagerUtil.copyToClipboard( + activity.applicationContext, + activity.getString(R.string.receive_clipboard_tag), + address + ) + }, + onQrImageShare = { imageBitmap -> + scope.launch { + shareData( + context = activity.applicationContext, + snackbarHostState = snackbarHostState, + qrImageBitmap = imageBitmap.asAndroidBitmap(), + versionInfo = versionInfo + ).collect { shareResult -> + Twig.info { + if (shareResult) { + "Sharing the address QR code was successful" + } else { + "Sharing the address QR code failed" + } + } + // No other action for now + } + } + }, onSettings = onSettings, - onAddressDetails = onAddressDetails, + versionInfo = versionInfo ) } -@Composable -internal fun WrapReceive( - walletAddresses: WalletAddresses?, - onSettings: () -> Unit, - onAddressDetails: () -> Unit, -) { - if (null == walletAddresses) { - // TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer - // TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available - // TODO [#1146]: https://github.com/Electric-Coin-Company/zashi-android/issues/1146 - CircularScreenProgressIndicator() - } else { - Receive( - walletAddresses.unified, - onSettings = onSettings, - onAddressDetails = onAddressDetails, - onAdjustBrightness = { /* Just for testing */ } - ) +private const val CACHE_SUBDIR = "zcash_address_qr_images" // NON-NLS +private const val TEMP_FILE_NAME_PREFIX = "zcash_address_qr_" // NON-NLS +private const val TEMP_FILE_NAME_SUFFIX = ".png" // NON-NLS + +fun shareData( + context: Context, + snackbarHostState: SnackbarHostState, + qrImageBitmap: Bitmap, + versionInfo: VersionInfo +): Flow = + callbackFlow { + // Initialize cache directory + val cacheDir = context.getInternalCacheDirSuspend(CACHE_SUBDIR) + + // Save the bitmap to a temporary file in the cache directory + val bitmapFile = + withContext(Dispatchers.IO) { + File.createTempFile( + TEMP_FILE_NAME_PREFIX, + TEMP_FILE_NAME_SUFFIX, + cacheDir, + ).also { + it.storeBitmap(qrImageBitmap) + } + } + + // Example of the expected temporary file path: + // /data/user/0/co.electriccoin.zcash.debug/cache/zcash_address_qr_images/ + // zcash_address_qr_6455164324646067652.png + + val shareIntent = + FileShareUtil.newShareContentIntent( + context = context, + dataFilePath = bitmapFile.absolutePath, + versionInfo = versionInfo, + fileType = FileShareUtil.ZASHI_QR_CODE_MIME_TYPE + ) + runCatching { + context.startActivity(shareIntent) + trySend(true) + }.onFailure { + snackbarHostState.showSnackbar(message = context.getString(R.string.receive_data_unable_to_share)) + trySend(false) + } + awaitClose { + // No resources to release + } + } + +suspend fun File.storeBitmap(bitmap: Bitmap) = + withContext(Dispatchers.IO) { + outputStream().use { fOut -> + @Suppress("MagicNumber") + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut) + fOut.flush() + } } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt index 4efc3d50..b487099a 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt @@ -8,10 +8,7 @@ object JvmQrCodeGenerator : QrCodeGenerator { data: String, sizePixels: Int ): BooleanArray { - val bitMatrix = - QRCodeWriter().let { - it.encode(data, BarcodeFormat.QR_CODE, sizePixels, sizePixels) - } + val bitMatrix = QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, sizePixels, sizePixels) return BooleanArray(sizePixels * sizePixels).apply { var booleanArrayPosition = 0 diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt index 5d66263a..2caf7eb7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt @@ -1,11 +1,16 @@ package co.electriccoin.zcash.ui.screen.receive.view import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -13,34 +18,41 @@ import androidx.compose.material.icons.filled.BrightnessHigh import androidx.compose.material.icons.filled.BrightnessLow import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import cash.z.ecc.android.sdk.fixture.WalletAddressFixture +import cash.z.ecc.android.sdk.fixture.WalletAddressesFixture import cash.z.ecc.android.sdk.model.WalletAddress +import cash.z.ecc.android.sdk.model.WalletAddresses import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.common.BrightenScreen import co.electriccoin.zcash.ui.common.DisableScreenTimeout +import co.electriccoin.zcash.ui.common.model.VersionInfo import co.electriccoin.zcash.ui.common.test.CommonTag -import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT -import co.electriccoin.zcash.ui.design.component.Body +import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator import co.electriccoin.zcash.ui.design.component.GradientSurface -import co.electriccoin.zcash.ui.design.component.PrimaryButton +import co.electriccoin.zcash.ui.design.component.Reference import co.electriccoin.zcash.ui.design.component.SmallTopAppBar +import co.electriccoin.zcash.ui.design.component.SubHeader import co.electriccoin.zcash.ui.design.theme.ZcashTheme +import co.electriccoin.zcash.ui.fixture.VersionInfoFixture import co.electriccoin.zcash.ui.screen.receive.util.AndroidQrCodeImageGenerator import co.electriccoin.zcash.ui.screen.receive.util.JvmQrCodeGenerator import kotlinx.coroutines.runBlocking @@ -52,46 +64,63 @@ private fun ComposablePreview() { ZcashTheme(forceDarkMode = false) { GradientSurface { Receive( - walletAddress = runBlocking { WalletAddressFixture.unified() }, + walletAddress = runBlocking { WalletAddressesFixture.new() }, + snackbarHostState = SnackbarHostState(), onSettings = {}, - onAddressDetails = {}, onAdjustBrightness = {}, + onAddrCopyToClipboard = {}, + onQrImageShare = {}, + versionInfo = VersionInfoFixture.new() ) } } } +@Suppress("LongParameterList") @Composable fun Receive( - walletAddress: WalletAddress, + walletAddress: WalletAddresses?, + snackbarHostState: SnackbarHostState, onSettings: () -> Unit, - onAddressDetails: () -> Unit, onAdjustBrightness: (Boolean) -> Unit, + onAddrCopyToClipboard: (String) -> Unit, + onQrImageShare: (ImageBitmap) -> Unit, + versionInfo: VersionInfo, ) { val (brightness, setBrightness) = rememberSaveable { mutableStateOf(false) } - Scaffold(topBar = { - ReceiveTopAppBar( - adjustBrightness = brightness, - onSettings = onSettings, - onBrightness = { - onAdjustBrightness(!brightness) - setBrightness(!brightness) - } - ) - }) { paddingValues -> - ReceiveContents( - walletAddress = walletAddress, - onAddressDetails = onAddressDetails, - adjustBrightness = brightness, - modifier = - Modifier.padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacingRegular, - end = ZcashTheme.dimens.screenHorizontalSpacingRegular - ) - ) + Scaffold( + topBar = { + ReceiveTopAppBar( + adjustBrightness = brightness, + onSettings = onSettings, + onBrightness = { + onAdjustBrightness(!brightness) + setBrightness(!brightness) + }, + versionInfo = versionInfo, + ) + }, + snackbarHost = { SnackbarHost(snackbarHostState) }, + ) { paddingValues -> + if (null == walletAddress) { + CircularScreenProgressIndicator() + } else { + ReceiveContents( + walletAddress = walletAddress, + onAddressCopyToClipboard = onAddrCopyToClipboard, + onQrImageShare = onQrImageShare, + adjustBrightness = brightness, + versionInfo = versionInfo, + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault, + start = ZcashTheme.dimens.screenHorizontalSpacingRegular, + end = ZcashTheme.dimens.screenHorizontalSpacingRegular + ) + ) + } } } @@ -99,23 +128,26 @@ fun Receive( private fun ReceiveTopAppBar( adjustBrightness: Boolean, onSettings: () -> Unit, - onBrightness: () -> Unit + onBrightness: () -> Unit, + versionInfo: VersionInfo ) { SmallTopAppBar( titleText = stringResource(id = R.string.receive_title), regularActions = { - IconButton( - onClick = onBrightness - ) { - Icon( - imageVector = - if (adjustBrightness) { - Icons.Default.BrightnessLow - } else { - Icons.Default.BrightnessHigh - }, - contentDescription = stringResource(R.string.receive_brightness_content_description) - ) + if (versionInfo.isDebuggable) { + IconButton( + onClick = onBrightness + ) { + Icon( + imageVector = + if (adjustBrightness) { + Icons.Default.BrightnessLow + } else { + Icons.Default.BrightnessHigh + }, + contentDescription = stringResource(R.string.receive_brightness_content_description) + ) + } } }, hamburgerMenuActions = { @@ -132,13 +164,14 @@ private fun ReceiveTopAppBar( ) } -private val DEFAULT_QR_CODE_SIZE = 320.dp - +@Suppress("LongParameterList") @Composable private fun ReceiveContents( - walletAddress: WalletAddress, - onAddressDetails: () -> Unit, + walletAddress: WalletAddresses, + onAddressCopyToClipboard: (String) -> Unit, + onQrImageShare: (ImageBitmap) -> Unit, adjustBrightness: Boolean, + versionInfo: VersionInfo, modifier: Modifier = Modifier, ) { Column( @@ -149,76 +182,160 @@ private fun ReceiveContents( .then(modifier), horizontalAlignment = Alignment.CenterHorizontally ) { - QrCode( - data = walletAddress.address, - size = DEFAULT_QR_CODE_SIZE, - adjustBrightness = adjustBrightness, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) - - Body( - text = stringResource(id = R.string.wallet_address_unified), - Modifier.align(Alignment.CenterHorizontally) - ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) - - // TODO [#163]: Ellipsize center of the string - // TODO [#163]: https://github.com/Electric-Coin-Company/zashi-android/issues/163 - Text( - text = walletAddress.address, - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.align(Alignment.CenterHorizontally), - overflow = TextOverflow.Ellipsis, - maxLines = 1 - ) - - Spacer( - modifier = - Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) - ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) - - PrimaryButton( - onClick = onAddressDetails, - text = stringResource(id = R.string.receive_see_address_details) - ) - } -} - -@Composable -private fun QrCode( - data: String, - size: Dp, - modifier: Modifier = Modifier, - adjustBrightness: Boolean = false, -) { - Column(modifier = modifier) { if (adjustBrightness) { BrightenScreen() DisableScreenTimeout() } - val sizePixels = with(LocalDensity.current) { size.toPx() }.roundToInt() + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) - // In the future, use actual/expect to switch QR code generator implementations for multiplatform + Address( + walletAddress = walletAddress.unified, + onAddressCopyToClipboard = onAddressCopyToClipboard, + onQrImageShare = onQrImageShare, + ) - // Note that our implementation has an extra array copy to BooleanArray, which is a cross-platform - // representation. This should have minimal performance impact since the QR code is relatively - // small and we only generate QR codes infrequently. + if (versionInfo.isTestnet) { + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingHuge)) - val qrCodePixelArray = JvmQrCodeGenerator.generate(data, sizePixels) - val qrCodeImage = AndroidQrCodeImageGenerator.generate(qrCodePixelArray, sizePixels) + Address( + walletAddress = walletAddress.sapling, + onAddressCopyToClipboard = onAddressCopyToClipboard, + onQrImageShare = onQrImageShare, + ) + } - Image( - bitmap = qrCodeImage, - contentDescription = stringResource(R.string.receive_qr_code_content_description) + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingHuge)) + + Address( + walletAddress = walletAddress.transparent, + onAddressCopyToClipboard = onAddressCopyToClipboard, + onQrImageShare = onQrImageShare, ) } } + +private val DEFAULT_QR_CODE_SIZE = 320.dp + +@Suppress("LongMethod") +@Composable +private fun Address( + walletAddress: WalletAddress, + onAddressCopyToClipboard: (String) -> Unit, + onQrImageShare: (ImageBitmap) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier) { + SubHeader( + text = + stringResource( + id = + when (walletAddress) { + is WalletAddress.Unified -> R.string.receive_wallet_address_unified + is WalletAddress.Sapling -> R.string.receive_wallet_address_sapling + is WalletAddress.Transparent -> R.string.receive_wallet_address_transparent + } + ), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingTiny)) + + val sizePixels = with(LocalDensity.current) { DEFAULT_QR_CODE_SIZE.toPx() }.roundToInt() + val qrCodeImage = + remember { + qrCodeForAddress( + address = walletAddress.address, + size = sizePixels + ) + } + + QrCode( + qrCodeImage = qrCodeImage, + onQrImageBitmapShare = onQrImageShare, + contentDescription = + stringResource( + id = + when (walletAddress) { + is WalletAddress.Unified -> R.string.receive_unified_content_description + is WalletAddress.Sapling -> R.string.receive_sapling_content_description + is WalletAddress.Transparent -> R.string.receive_transparent_content_description + } + ), + modifier = Modifier.align(Alignment.CenterHorizontally), + ) + + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingTiny)) + + // TODO [#163]: Ellipsize center of the string + // TODO [#163]: https://github.com/Electric-Coin-Company/zashi-android/issues/163 + Text( + text = walletAddress.address, + style = ZcashTheme.typography.primary.bodyLarge, + color = ZcashTheme.colors.textDescription, + textAlign = TextAlign.Center, + modifier = + Modifier + .align(Alignment.CenterHorizontally) + .clickable { onAddressCopyToClipboard(walletAddress.address) } + .padding(horizontal = ZcashTheme.dimens.spacingLarge) + .fillMaxWidth(), + ) + + Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + Reference( + text = stringResource(id = R.string.receive_copy), + onClick = { onAddressCopyToClipboard(walletAddress.address) }, + textAlign = TextAlign.Center, + imageVector = ImageVector.vectorResource(R.drawable.copy), + imageContentDescription = null, + modifier = Modifier.wrapContentSize(), + ) + Reference( + text = stringResource(id = R.string.receive_share), + onClick = { onQrImageShare(qrCodeImage) }, + textAlign = TextAlign.Center, + imageVector = ImageVector.vectorResource(R.drawable.share), + imageContentDescription = null, + modifier = Modifier.wrapContentSize(), + ) + } + } +} + +private fun qrCodeForAddress( + address: String, + size: Int, +): ImageBitmap { + // In the future, use actual/expect to switch QR code generator implementations for multiplatform + + // Note that our implementation has an extra array copy to BooleanArray, which is a cross-platform + // representation. This should have minimal performance impact since the QR code is relatively + // small and we only generate QR codes infrequently. + + val qrCodePixelArray = JvmQrCodeGenerator.generate(address, size) + + return AndroidQrCodeImageGenerator.generate(qrCodePixelArray, size) +} + +@Composable +private fun QrCode( + contentDescription: String, + qrCodeImage: ImageBitmap, + onQrImageBitmapShare: (ImageBitmap) -> Unit, + modifier: Modifier = Modifier, +) { + Image( + bitmap = qrCodeImage, + contentDescription = contentDescription, + modifier = + Modifier + .clickable { onQrImageBitmapShare(qrCodeImage) } + .then(modifier) + ) +} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt index f97f90c7..9e1f9517 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt @@ -37,12 +37,13 @@ private const val MAX_EXCEPTIONS_TO_REPORT = 5 suspend fun CrashInfo.Companion.all(context: Context): List { val exceptionDirectory = ExceptionPath.getExceptionDirectory(context) ?: return emptyList() - val filesList: List = exceptionDirectory.listFilesSuspend().toList() - return filesList - .mapNotNull { + val filesList: List? = exceptionDirectory.listFilesSuspend()?.toList() + return filesList?.run { + mapNotNull { ReportedException.new(it) }.sortedBy { it.time } - .reversed() - .take(MAX_EXCEPTIONS_TO_REPORT) - .map { CrashInfo(it.exceptionClassName, it.isUncaught, it.time) } + .reversed() + .take(MAX_EXCEPTIONS_TO_REPORT) + .map { CrashInfo(it.exceptionClassName, it.isUncaught, it.time) } + } ?: emptyList() } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/util/FileShareUtil.kt similarity index 92% rename from ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt rename to ui-lib/src/main/java/co/electriccoin/zcash/ui/util/FileShareUtil.kt index 30333071..c7e74769 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/util/FileShareUtil.kt @@ -1,4 +1,4 @@ -package co.electriccoin.zcash.ui.screen.exportdata.util +package co.electriccoin.zcash.ui.util import android.content.Context import android.content.Intent @@ -13,6 +13,7 @@ object FileShareUtil { const val SHARE_CONTENT_PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION const val ZASHI_INTERNAL_DATA_MIME_TYPE = "application/octet-stream" // NON-NLS + const val ZASHI_QR_CODE_MIME_TYPE = "image/png" // NON-NLS const val ZASHI_INTERNAL_DATA_AUTHORITY = "co.electriccoin.zcash.provider" // NON-NLS const val ZASHI_INTERNAL_DATA_AUTHORITY_DEBUG = "co.electriccoin.zcash.debug.provider" // NON-NLS @@ -30,7 +31,8 @@ object FileShareUtil { internal fun newShareContentIntent( context: Context, dataFilePath: String, - versionInfo: VersionInfo + fileType: String, + versionInfo: VersionInfo, ): Intent { val fileUri = FileProvider.getUriForFile( @@ -43,7 +45,7 @@ object FileShareUtil { Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_STREAM, fileUri) - type = ZASHI_INTERNAL_DATA_MIME_TYPE + type = fileType } val shareDataIntent = diff --git a/ui-lib/src/main/res/ui/export_data/xml/share_file_provider_paths.xml b/ui-lib/src/main/res/ui/export_data/xml/share_file_provider_paths.xml index dabb6865..3dba2941 100644 --- a/ui-lib/src/main/res/ui/export_data/xml/share_file_provider_paths.xml +++ b/ui-lib/src/main/res/ui/export_data/xml/share_file_provider_paths.xml @@ -1,21 +1,24 @@ + + + - + \ No newline at end of file diff --git a/ui-lib/src/main/res/ui/receive/drawable/copy.xml b/ui-lib/src/main/res/ui/receive/drawable/copy.xml new file mode 100644 index 00000000..fade5864 --- /dev/null +++ b/ui-lib/src/main/res/ui/receive/drawable/copy.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/ui-lib/src/main/res/ui/receive/drawable/share.xml b/ui-lib/src/main/res/ui/receive/drawable/share.xml new file mode 100644 index 00000000..88b75cb5 --- /dev/null +++ b/ui-lib/src/main/res/ui/receive/drawable/share.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/receive/values/strings.xml b/ui-lib/src/main/res/ui/receive/values/strings.xml index b5e81670..479608d5 100644 --- a/ui-lib/src/main/res/ui/receive/values/strings.xml +++ b/ui-lib/src/main/res/ui/receive/values/strings.xml @@ -2,8 +2,14 @@ Receive Adjust brightness - QR code for address - Your Address - See Address Details - + Unified Address QR code + Sapling Address QR code + Transparent Address QR code + Unified Address + Sapling Address + Transparent Address + Copy + Share + Zcash Wallet Address + Unable to find an application to share the QR code with. diff --git a/ui-lib/src/main/res/ui/wallet_address/values/strings.xml b/ui-lib/src/main/res/ui/wallet_address/values/strings.xml deleted file mode 100644 index fbf6e774..00000000 --- a/ui-lib/src/main/res/ui/wallet_address/values/strings.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - My wallet addresses - Back - - Your Unified Address: - which includes - Shielded Sapling (NU1) - Transparent - Show address - Hide address - - Zcash Wallet Address - diff --git a/ui-lib/src/main/res/ui/warning/values/strings.xml b/ui-lib/src/main/res/ui/warning/values/strings.xml index 2f1487cf..c41b09d9 100644 --- a/ui-lib/src/main/res/ui/warning/values/strings.xml +++ b/ui-lib/src/main/res/ui/warning/values/strings.xml @@ -3,9 +3,9 @@ Not enough space! You need approximately - %1$d gig of space while synchronizing the Zcash blockchain, but only 300 megs once done. Syncing + %1$d Gbyte of space while synchronizing the Zcash blockchain, but only 300 Mbyte once done. Syncing will stay paused until more space is available. - ~%1$d megs + ~%1$d Mbyte required to continue Unknown diff --git a/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt b/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt index eaa52fa4..c69a7834 100644 --- a/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt +++ b/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt @@ -290,9 +290,6 @@ class ScreenshotTest : UiTestPrerequisites() { composeTestRule.navigateInHomeTab(HomeTag.TAB_BALANCES) balancesScreenshots(resContext, tag, composeTestRule) - navigateTo(NavigationTargets.WALLET_ADDRESS_DETAILS) - addressDetailsScreenshots(resContext, tag, composeTestRule) - navigateTo(NavigationTargets.HISTORY) transactionHistoryScreenshots(resContext, tag, composeTestRule) @@ -419,18 +416,6 @@ private fun settingsScreenshots( ScreenshotTest.takeScreenshot(tag, "Settings 1") } -private fun addressDetailsScreenshots( - resContext: Context, - tag: String, - composeTestRule: ComposeTestRule -) { - composeTestRule.onNode(hasText(resContext.getString(R.string.wallet_address_title))).also { - it.assertExists() - } - - ScreenshotTest.takeScreenshot(tag, "Addresses 1") -} - private fun transactionHistoryScreenshots( resContext: Context, tag: String, @@ -468,7 +453,7 @@ private fun receiveZecScreenshots( composeTestRule.onNode( hasContentDescription( - value = resContext.getString(R.string.receive_qr_code_content_description), + value = resContext.getString(R.string.receive_unified_content_description), ignoreCase = true ) ).also {