[#62] Wallet details scaffold

Implements a details screen showing various wallet addresses

Some known issues requiring followup in future pull requests:
 - #159 Colors for light theme have not been defined yet
 - #160 Color blocks have 2x borders along the top/bottom of the middle item
 - #161 Some address types display placeholder values
This commit is contained in:
Carter Jernigan 2021-12-31 08:28:16 -05:00 committed by GitHub
parent 205344c201
commit ee96e915ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 721 additions and 30 deletions

View File

@ -1,16 +1,12 @@
# Speed up builds. Keep these flags here for quick debugging of issues.
org.gradle.vfs.watch=true
# https://github.com/gradle/gradle/issues/13382
org.gradle.vfs.watch=false
org.gradle.configureondemand=false
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx3g
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m
kotlin.mpp.stability.nowarn=true
kapt.include.compile.classpath=false
kapt.incremental.apt=true
kapt.use.worker.api=true
android.useAndroidX=true
android.nonTransitiveRClass=true
android.builder.sdkDownload=true

View File

@ -21,6 +21,7 @@ dependencies {
implementation(libs.zcash.bip39)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.kotlinx.coroutines.test)
if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
androidTestUtil(libs.androidx.test.orchestrator) {

View File

@ -0,0 +1,33 @@
package cash.z.ecc.sdk.model
import androidx.test.filters.SmallTest
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import cash.z.ecc.sdk.fixture.WalletAddressesFixture
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Test
class WalletAddressesTest {
@Test
@SmallTest
fun security() {
val walletAddresses = WalletAddressesFixture.new()
val actual = WalletAddressesFixture.new().toString()
assertFalse(actual.contains(walletAddresses.shieldedOrchard))
assertFalse(actual.contains(walletAddresses.shieldedSapling))
assertFalse(actual.contains(walletAddresses.transparent))
assertFalse(actual.contains(walletAddresses.unified))
assertFalse(actual.contains(walletAddresses.viewingKey))
}
@OptIn(ExperimentalCoroutinesApi::class)
@Test
@SmallTest
fun new() = runTest {
val expected = WalletAddressesFixture.new()
val actual = WalletAddresses.new(PersistableWalletFixture.new())
assertEquals(expected, actual)
}
}

View File

@ -0,0 +1,22 @@
package cash.z.ecc.sdk.fixture
import cash.z.ecc.sdk.model.WalletAddresses
object WalletAddressesFixture {
// These fixture values are derived from the secret defined in PersistableWalletFixture
const val UNIFIED = "Unified GitHub Issue #161"
const val SHIELDED_ORCHARD = "Shielded Orchard GitHub Issue #161"
@Suppress("MaxLineLength")
const val SHIELDED_SAPLING = "ztestsapling1475xtm56czrzmleqzzlu4cxvjjfsy2p6rv78q07232cpsx5ee52k0mn5jyndq09mampkgvrxnwg"
const val TRANSPARENT = "tmXuTnE11JojToagTqxXUn6KvdxDE3iLKbp"
const val VIEWING_KEY = "03feaa290589a20f795f302ba03847b0a6c9c2b571d75d80bc4ebb02382d0549da"
fun new(
unified: String = UNIFIED,
shieldedOrchard: String = SHIELDED_ORCHARD,
shieldedSapling: String = SHIELDED_SAPLING,
transparent: String = TRANSPARENT,
viewingKey: String = VIEWING_KEY
) = WalletAddresses(unified, shieldedOrchard, shieldedSapling, transparent, viewingKey)
}

View File

@ -0,0 +1,51 @@
package cash.z.ecc.sdk.model
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.tool.DerivationTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
data class WalletAddresses(
val unified: String,
val shieldedOrchard: String,
val shieldedSapling: String,
val transparent: String,
val viewingKey: String
) {
// Override to prevent leaking details in logs
override fun toString() = "WalletAddresses"
companion object {
suspend fun new(persistableWallet: PersistableWallet): WalletAddresses {
// Dispatcher needed because SecureRandom is loaded, which is slow and performs IO
// https://github.com/zcash/kotlin-bip39/issues/13
val bip39Seed = withContext(Dispatchers.IO) {
Mnemonics.MnemonicCode(persistableWallet.seedPhrase.joinToString()).toSeed()
}
// Dispatchers needed until an SDK is published with the implementation of
// https://github.com/zcash/zcash-android-wallet-sdk/issues/269
val viewingKey = withContext(Dispatchers.IO) {
DerivationTool.deriveUnifiedViewingKeys(bip39Seed, persistableWallet.network)[0]
}.extpub
val shieldedSaplingAddress = withContext(Dispatchers.IO) {
DerivationTool.deriveShieldedAddress(bip39Seed, persistableWallet.network)
}
val transparentAddress = withContext(Dispatchers.IO) {
DerivationTool.deriveTransparentAddress(bip39Seed, persistableWallet.network)
}
// TODO [#161]: Pending SDK support, fix providing correct values for the unified
return WalletAddresses(
unified = "Unified GitHub Issue #161",
shieldedOrchard = "Shielded Orchard GitHub Issue #161",
shieldedSapling = shieldedSaplingAddress,
transparent = transparentAddress,
viewingKey = viewingKey
)
}
}
}

View File

@ -31,7 +31,8 @@ android {
"src/main/res/ui/common",
"src/main/res/ui/home",
"src/main/res/ui/onboarding",
"src/main/res/ui/restore"
"src/main/res/ui/restore",
"src/main/res/ui/wallet_address"
)
)
}

View File

@ -0,0 +1,200 @@
package cash.z.ecc.ui.screen.wallet_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.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.filters.MediumTest
import cash.z.ecc.sdk.fixture.WalletAddressesFixture
import cash.z.ecc.sdk.model.WalletAddresses
import cash.z.ecc.ui.R
import cash.z.ecc.ui.test.getStringResource
import cash.z.ecc.ui.theme.ZcashTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
class WalletAddressViewTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
@MediumTest
fun initial_screen_setup() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also {
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_shielded_orchard)).also {
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_shielded_sapling)).also {
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also {
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_viewing_key)).also {
it.assertExists()
}
composeTestRule.onNodeWithText(walletAddresses.unified).also {
it.assertExists()
}
composeTestRule.onNodeWithText(walletAddresses.shieldedOrchard).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(walletAddresses.shieldedSapling).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(walletAddresses.transparent).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(walletAddresses.viewingKey).also {
it.assertDoesNotExist()
}
}
@Test
@MediumTest
fun unified_collapses() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(walletAddresses.unified).also {
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also {
it.assertExists()
it.performClick()
}
composeTestRule.onNodeWithText(walletAddresses.unified).also {
it.assertDoesNotExist()
}
}
@Test
@MediumTest
fun shielded_orchard_expands() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(walletAddresses.shieldedOrchard).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_shielded_orchard)).also {
it.assertExists()
it.performClick()
}
composeTestRule.onNodeWithText(walletAddresses.shieldedOrchard).also {
it.assertExists()
}
}
@Test
@MediumTest
fun shielded_sapling_expands() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(walletAddresses.shieldedSapling).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_shielded_sapling)).also {
it.assertExists()
it.performClick()
}
composeTestRule.onNodeWithText(walletAddresses.shieldedSapling).also {
it.assertExists()
}
}
@Test
@MediumTest
fun transparent_expands() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(walletAddresses.transparent).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also {
it.assertExists()
it.performClick()
}
composeTestRule.onNodeWithText(walletAddresses.transparent).also {
it.assertExists()
}
}
@Test
@MediumTest
fun viewing_expands() {
val walletAddresses = WalletAddressesFixture.new()
newTestSetup(walletAddresses)
composeTestRule.onNodeWithText(walletAddresses.viewingKey).also {
it.assertDoesNotExist()
}
composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_viewing_key)).also {
it.assertExists()
it.performClick()
}
composeTestRule.onNodeWithText(walletAddresses.viewingKey).also {
it.assertExists()
}
}
@Test
@MediumTest
fun back() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnBackCount())
composeTestRule.onNodeWithContentDescription(getStringResource(R.string.wallet_address_back_content_description)).also {
it.performClick()
}
assertEquals(1, testSetup.getOnBackCount())
}
private fun newTestSetup(initialState: WalletAddresses = WalletAddressesFixture.new()) = TestSetup(composeTestRule, initialState)
private class TestSetup(private val composeTestRule: ComposeContentTestRule, initialState: WalletAddresses) {
private var onBackCount = 0
fun getOnBackCount(): Int {
composeTestRule.waitForIdle()
return onBackCount
}
init {
composeTestRule.setContent {
ZcashTheme {
WalletAddresses(
initialState,
onBack = {
onBackCount++
}
)
}
}
}
}
}

View File

@ -37,6 +37,7 @@ import cash.z.ecc.ui.screen.onboarding.viewmodel.OnboardingViewModel
import cash.z.ecc.ui.screen.restore.view.RestoreWallet
import cash.z.ecc.ui.screen.restore.viewmodel.CompleteWordSetState
import cash.z.ecc.ui.screen.restore.viewmodel.RestoreViewModel
import cash.z.ecc.ui.screen.wallet_address.view.WalletAddresses
import cash.z.ecc.ui.theme.ZcashTheme
import cash.z.ecc.ui.util.AndroidApiVersion
import kotlinx.coroutines.Dispatchers
@ -199,7 +200,21 @@ class MainActivity : ComponentActivity() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { WrapHome({}, {}, {}, {}) }
composable("home") {
WrapHome(
goScan = {},
goProfile = { navController.navigate("wallet_address_details") },
goSend = {},
goRequest = {}
)
}
composable("wallet_address_details") {
WrapWalletAddresses(
goBack = {
navController.popBackStack()
}
)
}
}
}
@ -225,6 +240,21 @@ class MainActivity : ComponentActivity() {
}
}
@Composable
private fun WrapWalletAddresses(
goBack: () -> Unit,
) {
val walletAddresses = walletViewModel.addresses.collectAsState().value
if (null == walletAddresses) {
// Display loading indicator
} else {
WalletAddresses(
walletAddresses,
goBack
)
}
}
companion object {
@VisibleForTesting
internal val SPLASH_SCREEN_DELAY = 0.seconds

View File

@ -31,3 +31,29 @@ fun Body(
modifier = modifier
)
}
@Composable
fun ListItem(
text: String,
modifier: Modifier = Modifier
) {
Text(
text = text,
style = ZcashTheme.typography.listItem,
color = MaterialTheme.colors.onBackground,
modifier = modifier
)
}
@Composable
fun ListHeader(
text: String,
modifier: Modifier = Modifier
) {
Text(
text = text,
style = ZcashTheme.typography.listItem,
color = MaterialTheme.colors.onBackground,
modifier = modifier
)
}

View File

@ -12,6 +12,7 @@ import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
import cash.z.ecc.android.sdk.type.WalletBalance
import cash.z.ecc.sdk.SynchronizerCompanion
import cash.z.ecc.sdk.model.PersistableWallet
import cash.z.ecc.sdk.model.WalletAddresses
import cash.z.ecc.ui.common.ANDROID_STATE_FLOW_TIMEOUT_MILLIS
import cash.z.ecc.ui.preference.EncryptedPreferenceKeys
import cash.z.ecc.ui.preference.EncryptedPreferenceSingleton
@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
@ -120,6 +122,15 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
emptyList()
)
@OptIn(FlowPreview::class)
val addresses: StateFlow<WalletAddresses?> = secretState
.filterIsInstance<SecretState.Ready>()
.map { WalletAddresses.new(it.persistableWallet) }
.stateIn(
viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = ANDROID_STATE_FLOW_TIMEOUT_MILLIS),
null
)
/**
* Creates a wallet asynchronously and then persists it. Clients observe
* [secretState] to see the side effects. This would be used for a user creating a new wallet.

View File

@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
@ -123,13 +122,10 @@ fun RestoreWallet(
@Composable
private fun RestoreTopAppBar(onBack: () -> Unit, onClear: () -> Unit) {
TopAppBar {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
TopAppBar(
title = { Text(text = stringResource(id = R.string.restore_header)) },
navigationIcon = {
IconButton(
modifier = Modifier.padding(16.dp),
onClick = onBack
) {
Icon(
@ -137,18 +133,11 @@ private fun RestoreTopAppBar(onBack: () -> Unit, onClear: () -> Unit) {
contentDescription = stringResource(R.string.restore_back_content_description)
)
}
Text(text = stringResource(id = R.string.restore_header))
Spacer(
Modifier
.fillMaxWidth()
.weight(MINIMAL_WEIGHT)
)
},
actions = {
NavigationButton(onClick = onClear, stringResource(R.string.restore_button_clear))
}
}
)
}
@Suppress("UNUSED_PARAMETER")

View File

@ -0,0 +1,274 @@
@file:Suppress("TooManyFunctions", "PackageNaming")
package cash.z.ecc.ui.screen.wallet_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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDownCircle
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cash.z.ecc.sdk.fixture.WalletAddressesFixture
import cash.z.ecc.sdk.model.WalletAddresses
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.ListHeader
import cash.z.ecc.ui.screen.common.ListItem
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
@Preview
@Composable
fun ComposablePreview() {
ZcashTheme(darkTheme = true) {
GradientSurface {
WalletAddresses(
WalletAddressesFixture.new(),
onBack = {}
)
}
}
}
@Composable
fun WalletAddresses(walletAddresses: WalletAddresses, onBack: () -> Unit) {
Column {
WalletDetailTopAppBar(onBack)
WalletDetailAddresses(walletAddresses)
}
}
@Composable
private fun WalletDetailTopAppBar(onBack: () -> Unit) {
TopAppBar(
title = {
Text(
text = stringResource(id = R.string.wallet_address_title)
)
},
navigationIcon = {
IconButton(
modifier = Modifier.padding(16.dp),
onClick = onBack
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(R.string.wallet_address_back_content_description),
tint = MaterialTheme.colors.secondary
)
}
}
)
}
private val BIG_INDICATOR_WIDTH = 24.dp
private val SMALL_INDICATOR_WIDTH = 16.dp
@Composable
private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
Column(Modifier.fillMaxWidth()) {
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,
isInitiallyExpanded = true
)
Box(Modifier.height(IntrinsicSize.Min)) {
Divider(modifier = Modifier.fillMaxHeight())
ListHeader(text = stringResource(R.string.wallet_address_header_includes))
}
OrchardAddress(walletAddresses.shieldedOrchard)
SaplingAddress(walletAddresses.shieldedSapling)
TransparentAddress(walletAddresses.transparent)
}
}
}
Divider(thickness = 8.dp)
ViewingKey(walletAddresses.viewingKey)
}
// 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 OrchardAddress(orchardAddress: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
SmallIndicator(ZcashTheme.colors.addressHighlightOrchard)
ExpandableRow(
title = stringResource(R.string.wallet_address_shielded_orchard),
content = orchardAddress,
isInitiallyExpanded = false
)
}
}
@Composable
private fun SaplingAddress(saplingAddress: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
SmallIndicator(ZcashTheme.colors.addressHighlightSapling)
ExpandableRow(
title = stringResource(R.string.wallet_address_shielded_sapling),
content = saplingAddress,
isInitiallyExpanded = false
)
}
}
@Composable
private fun TransparentAddress(transparentAddress: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
SmallIndicator(ZcashTheme.colors.addressHighlightTransparent)
ExpandableRow(
title = stringResource(R.string.wallet_address_transparent),
content = transparentAddress,
isInitiallyExpanded = false
)
}
}
@Composable
private fun ViewingKey(viewingKey: String) {
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
) {
Image(
painter = ColorPainter(ZcashTheme.colors.addressHighlightViewing),
contentDescription = "",
modifier = Modifier
.width(SMALL_INDICATOR_WIDTH)
)
ExpandableRow(
title = stringResource(R.string.wallet_address_viewing_key),
content = viewingKey,
isInitiallyExpanded = false
)
}
}
@Composable
private fun ExpandableRow(
title: String,
content: String,
isInitiallyExpanded: Boolean,
modifier: Modifier = Modifier
) {
var expandedState by rememberSaveable { mutableStateOf(isInitiallyExpanded) }
Column(
modifier
.fillMaxWidth()
.clickable {
expandedState = !expandedState
}
) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.defaultMinSize(minHeight = 48.dp)) {
ListItem(text = title)
Spacer(
modifier = Modifier
.fillMaxWidth()
.weight(MINIMAL_WEIGHT)
)
ExpandableArrow(expandedState)
}
if (expandedState) {
Body(content)
}
}
}
@Composable
private fun SmallIndicator(color: Color) {
// TODO [#160]: Border is not the right implementation here, as it causes double thickness for the middle item
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.colors.onBackground
)
}

View File

@ -41,6 +41,13 @@ object Dark {
val overlay = Color(0x22000000)
val highlight = Color(0xFFFFD800)
val addressHighlightBorder = Color(0xFF525252)
val addressHighlightUnified = Color(0xFFFFD800)
val addressHighlightOrchard = Color(0xFFFFD800)
val addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
}
object Light {
@ -80,4 +87,12 @@ object Light {
val overlay = Color(0x22000000)
val highlight = Color(0xFFFFD800)
// [TODO #159]: The colors are wrong for light theme
val addressHighlightBorder = Color(0xFF525252)
val addressHighlightUnified = Color(0xFFFFD800)
val addressHighlightOrchard = Color(0xFFFFD800)
val addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
}

View File

@ -46,7 +46,13 @@ data class ExtendedColors(
val progressBackground: Color,
val chipIndex: Color,
val overlay: Color,
val highlight: Color
val highlight: Color,
val addressHighlightBorder: Color,
val addressHighlightUnified: Color,
val addressHighlightOrchard: Color,
val addressHighlightSapling: Color,
val addressHighlightTransparent: Color,
val addressHighlightViewing: Color
) {
@Composable
fun surfaceGradient() = Brush.verticalGradient(
@ -69,7 +75,13 @@ val DarkExtendedColorPalette = ExtendedColors(
progressBackground = Dark.progressBackground,
chipIndex = Dark.textChipIndex,
overlay = Dark.overlay,
highlight = Dark.highlight
highlight = Dark.highlight,
addressHighlightBorder = Dark.addressHighlightBorder,
addressHighlightUnified = Dark.addressHighlightUnified,
addressHighlightOrchard = Dark.addressHighlightOrchard,
addressHighlightSapling = Dark.addressHighlightSapling,
addressHighlightTransparent = Dark.addressHighlightTransparent,
addressHighlightViewing = Dark.addressHighlightViewing
)
val LightExtendedColorPalette = ExtendedColors(
@ -84,7 +96,13 @@ val LightExtendedColorPalette = ExtendedColors(
progressBackground = Light.progressBackground,
chipIndex = Light.textChipIndex,
overlay = Light.overlay,
highlight = Light.highlight
highlight = Light.highlight,
addressHighlightBorder = Light.addressHighlightBorder,
addressHighlightUnified = Light.addressHighlightUnified,
addressHighlightOrchard = Light.addressHighlightOrchard,
addressHighlightSapling = Light.addressHighlightSapling,
addressHighlightTransparent = Light.addressHighlightTransparent,
addressHighlightViewing = Light.addressHighlightViewing
)
val LocalExtendedColors = staticCompositionLocalOf {
@ -100,7 +118,13 @@ val LocalExtendedColors = staticCompositionLocalOf {
progressBackground = Color.Unspecified,
chipIndex = Color.Unspecified,
overlay = Color.Unspecified,
highlight = Color.Unspecified
highlight = Color.Unspecified,
addressHighlightBorder = Color.Unspecified,
addressHighlightUnified = Color.Unspecified,
addressHighlightOrchard = Color.Unspecified,
addressHighlightSapling = Color.Unspecified,
addressHighlightTransparent = Color.Unspecified,
addressHighlightViewing = Color.Unspecified
)
}

View File

@ -44,6 +44,7 @@ val Typography = Typography(
@Immutable
data class ExtendedTypography(
val chipIndex: TextStyle,
val listItem: TextStyle,
)
val LocalExtendedTypography = staticCompositionLocalOf {
@ -52,6 +53,9 @@ val LocalExtendedTypography = staticCompositionLocalOf {
fontSize = 10.sp,
baselineShift = BaselineShift.Superscript,
fontWeight = FontWeight.Bold
),
listItem = Typography.body1.copy(
fontSize = 24.sp
)
)
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="wallet_address_title">Wallet Addresses</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>
<string name="wallet_address_shielded_orchard">Shielded Orchard (NU5)</string>
<string name="wallet_address_shielded_sapling">Shielded Sapling (NU1)</string>
<string name="wallet_address_transparent">Transparent</string>
<string name="wallet_address_viewing_key">Viewing Key Only (Sapling)</string>
<string name="wallet_address_show">Show address</string>
<string name="wallet_address_hide">Hide address</string>
</resources>