[#225] Refactor design to separate module

This moves the theme and common UI elements to a separate Gradle module.

This is a first step towards creating our own custom design system, as it would eventually allow hiding of Material Design from the rest of the app UI.

As part of this change, a new common utility module was created so that both the ui and ui-design modules can depend on it.
This commit is contained in:
Carter Jernigan 2022-02-21 09:50:09 -05:00 committed by Carter Jernigan
parent 6be2590ad8
commit f01c855ead
56 changed files with 708 additions and 422 deletions

View File

@ -0,0 +1,57 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spackle-lib:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
<module name="zcash-android-app.spackle-lib" />
<option name="TESTING_TYPE" value="0" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="EXTRA_OPTIONS" value="" />
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
<option name="RETENTION_ENABLED" value="No" />
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
<option name="DEBUGGER_TYPE" value="Auto" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Hybrid>
<Java />
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,57 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ui-design-lib:connectedCheck" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
<module name="zcash-android-app.ui-design-lib" />
<option name="TESTING_TYPE" value="0" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="EXTRA_OPTIONS" value="" />
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
<option name="RETENTION_ENABLED" value="No" />
<option name="RETENTION_MAX_SNAPSHOTS" value="2" />
<option name="RETENTION_COMPRESS_SNAPSHOTS" value="false" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="INSPECTION_WITHOUT_ACTIVITY_RESTART" value="false" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
<option name="DEBUGGER_TYPE" value="Auto" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Hybrid>
<Java />
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@ -25,11 +25,14 @@ The logical components of the app are implemented as a number of Gradle modules.
* `app` — Compiles all of the modules together into the final application. This module contains minimal actual code. Note that the Java package structure for this module is under `cash.z.ecc.app` while the Android package name is `cash.z.ecc`.
* `build-info-lib` — Collects information from the build environment (e.g. Git SHA, Git commit count) and compiles them into the application. Can also be used for injection of API keys or other secrets.
* `ui-lib` — User interface that the user interacts with. This contains 99% of the UI code, along with localizations, icons, and other assets.
* ui
* `ui-design` — Contains UI theme elements only. Besides offering modularization, this allows for hiding of some Material Design components behind our own custom components.
* `ui-lib` — User interface that the user interacts with. This contains 99% of the UI code, along with localizations, icons, and other assets.
* preference
* `preference-api-lib` — Multiplatform interfaces for key-value storage of preferences.
* `preference-impl-android-lib` — Android-specific implementation for preference storage.
* `sdk-ext-lib` — Contains extensions on top of the to the Zcash SDK. Some of these extensions might be migrated into the SDK eventually, while others might represent Android-centric idioms. Depending on how this module evolves, it could adopt another name such as `wallet-lib` or be split into two.
* `spackle-lib` — Random utilities, to fill in the cracks in the Kotlin and Android frameworks.
## Shared Resources
There are some app-wide resources that share a common namespace, and these should be documented here to make it easy to ensure there are no collisions.

View File

@ -161,17 +161,21 @@ dependencyResolutionManagement {
// Bundles
bundle(
"androidx-compose",
"androidx-compose-core",
listOf(
"androidx-activity-compose",
"androidx-compose-compiler",
"androidx-compose-foundation",
"androidx-compose-material",
"androidx-compose-material3",
"androidx-compose-material-icons-core",
"androidx-compose-material-icons-extended",
"androidx-compose-tooling",
"androidx-compose-ui",
)
)
bundle(
"androidx-compose-extended",
listOf(
"androidx-activity-compose",
"androidx-compose-material-icons-core",
"androidx-compose-material-icons-extended",
"androidx-navigation-compose",
"androidx-viewmodel-compose"
)
@ -199,6 +203,8 @@ include("build-info-lib")
include("preference-api-lib")
include("preference-impl-android-lib")
include("sdk-ext-lib")
include("spackle-lib")
include("ui-design-lib")
include("ui-lib")
if (extra["IS_SDK_INCLUDED_BUILD"].toString().toBoolean()) {

View File

@ -0,0 +1,41 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlin-parcelize")
id("zcash.android-build-conventions")
}
android {
// TODO [#6]: Figure out how to move this into the build-conventions
kotlinOptions {
jvmTarget = libs.versions.java.get()
allWarningsAsErrors = project.property("ZCASH_IS_TREAT_WARNINGS_AS_ERRORS").toString().toBoolean()
freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn"
}
// Force orchestrator to be used for this module, because we need the preference files
// to be purged between tests
defaultConfig {
testInstrumentationRunnerArguments["clearPackageData"] = "true"
}
testOptions {
execution = "ANDROIDX_TEST_ORCHESTRATOR"
}
}
dependencies {
implementation(libs.androidx.annotation)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.kotlinx.coroutines.test)
androidTestUtil(libs.androidx.test.orchestrator) {
artifact {
type = "apk"
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="co.electriccoin.zcash.preference">
<application android:label="zcash-preference-test" />
</manifest>

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.onboarding.model
package co.electriccoin.zcash.spackle.model
import androidx.test.filters.SmallTest
import org.junit.Test

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.onboarding.model
package co.electriccoin.zcash.spackle.model
import androidx.test.filters.SmallTest
import org.junit.Test

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="co.electriccoin.zcash.spackle">
<application/>
</manifest>

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.util
package co.electriccoin.zcash.spackle
import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
@ -15,12 +15,12 @@ object AndroidApiVersion {
return Build.VERSION.SDK_INT >= sdk
}
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
val isAtLeastP = isAtLeast(Build.VERSION_CODES.P)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
val isAtLeastO = isAtLeast(Build.VERSION_CODES.O)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
val isAtLeastP = isAtLeast(Build.VERSION_CODES.P)
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q)
val isAtLeastQ = isAtLeast(Build.VERSION_CODES.Q)

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.util
package co.electriccoin.zcash.spackle
/**
* Implements a lazy singleton pattern with an input argument.

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.util
package co.electriccoin.zcash.spackle
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.onboarding.model
package co.electriccoin.zcash.spackle.model
/**
* Useful for accessing arrays or lists by index.

View File

@ -1,6 +1,4 @@
package cash.z.ecc.ui.screen.onboarding.model
import cash.z.ecc.sdk.model.PercentDecimal
package co.electriccoin.zcash.spackle.model
data class Progress(val current: Index, val last: Index) {
init {
@ -8,8 +6,6 @@ data class Progress(val current: Index, val last: Index) {
require(last.value >= current.value) { "last ($last) must be >= current ($current)" }
}
fun percent() = PercentDecimal.newLenient((current.value + 1.toFloat()) / (last.value + 1).toFloat())
companion object {
val EMPTY = Progress(Index(0), Index(1))
}

View File

@ -0,0 +1,62 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlin-parcelize")
id("zcash.android-build-conventions")
}
android {
buildFeatures {
viewBinding = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.androidx.compose.compiler.get().versionConstraint.displayName
}
// TODO [#6]: Figure out how to move this into the build-conventions
kotlinOptions {
jvmTarget = libs.versions.java.get()
allWarningsAsErrors = project.property("ZCASH_IS_TREAT_WARNINGS_AS_ERRORS").toString().toBoolean()
freeCompilerArgs = freeCompilerArgs.plus("-Xopt-in=kotlin.RequiresOptIn")
}
sourceSets {
getByName("main").apply {
res.setSrcDirs(
setOf(
"src/main/res/ui/common",
)
)
}
}
}
dependencies {
implementation(libs.androidx.annotation)
implementation(libs.androidx.core)
implementation(libs.androidx.splash)
implementation(libs.bundles.androidx.compose.core)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
implementation(projects.spackleLib)
// Material2 is only used for the EditText
implementation(libs.androidx.compose.material)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.androidx.compose.test.junit)
androidTestImplementation(libs.androidx.compose.test.manifest)
androidTestImplementation(libs.kotlin.reflect)
androidTestImplementation(libs.kotlin.test)
if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
androidTestUtil(libs.androidx.test.orchestrator) {
artifact {
type = "apk"
}
}
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="cash.z.ecc.ui.design">
<application
android:label="zcash-ui-design-test"/>
</manifest>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cash.z.ecc.ui.design">
<application />
</manifest>

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.theme
package cash.z.ecc.ui.design
/**
* A tiny weight, useful for spacers to fill an empty space.

View File

@ -0,0 +1,32 @@
package cash.z.ecc.ui.design.compat
import android.content.Context
import androidx.annotation.FontRes
import androidx.core.content.res.ResourcesCompat
import cash.z.ecc.ui.design.R
import co.electriccoin.zcash.spackle.AndroidApiVersion
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object FontCompat {
fun isFontPrefetchNeeded() = !AndroidApiVersion.isAtLeastO
suspend fun prefetchFontsLegacy(context: Context) {
prefetchFontLegacy(context, R.font.rubik_medium)
prefetchFontLegacy(context, R.font.rubik_regular)
}
/**
* Pre-fetches fonts on Android N (API 25) and below.
*/
/*
* ResourcesCompat is used implicitly by Compose on older Android versions.
* The backwards compatibility library performs disk IO and then
* caches the results. This moves that IO off the main thread, to prevent ANRs and
* jank during app startup.
*/
private suspend fun prefetchFontLegacy(context: Context, @FontRes fontRes: Int) =
withContext(Dispatchers.IO) {
ResourcesCompat.getFont(context, fontRes)
}
}

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -12,8 +12,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Index
@Preview
@Composable

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import cash.z.ecc.ui.screen.onboarding.model.Index
import co.electriccoin.zcash.spackle.model.Index
// Note: Row size should probably change for landscape layouts
const val CHIP_GRID_ROW_SIZE = 3

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
object CommonTag {
const val CHIP_LAYOUT = "chip_layout"

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.foundation.background
import androidx.compose.material3.Surface

View File

@ -1,17 +1,19 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import cash.z.ecc.ui.screen.onboarding.model.Progress
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Progress
// Eventually rename to GradientLinearProgressIndicator
@Composable
fun PinkProgress(progress: Progress, modifier: Modifier = Modifier) {
// Needs custom implementation to apply gradient
LinearProgressIndicator(
progress = progress.percent().decimal, modifier,
progress = progress.percent(), modifier,
ZcashTheme.colors.progressStart, ZcashTheme.colors.progressBackground
)
}
private fun Progress.percent() = (current.value + 1.toFloat()) / (last.value + 1).toFloat()

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text

View File

@ -1,10 +1,9 @@
package cash.z.ecc.ui.screen.common
package cash.z.ecc.ui.design.component
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.TextFieldColors
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -32,10 +31,8 @@ fun TextField(
keyboardActions: KeyboardActions = KeyboardActions(),
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
androidx.compose.material.TextField(
value,
onValueChange,
@ -54,6 +51,6 @@ fun TextField(
singleLine,
maxLines,
interactionSource,
colors = colors
colors = TextFieldDefaults.textFieldColors()
)
}

View File

@ -0,0 +1,39 @@
package cash.z.ecc.ui.design.theme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import cash.z.ecc.ui.theme.ZcashTheme
@Immutable
data class ExtendedColors(
val surfaceEnd: Color,
val onBackgroundHeader: Color,
val tertiary: Color,
val onTertiary: Color,
val callout: Color,
val onCallout: Color,
val progressStart: Color,
val progressEnd: Color,
val progressBackground: Color,
val chipIndex: Color,
val overlay: Color,
val highlight: Color,
val addressHighlightBorder: Color,
val addressHighlightUnified: Color,
val addressHighlightSapling: Color,
val addressHighlightTransparent: Color,
val addressHighlightViewing: Color,
val dangerous: Color,
val onDangerous: Color
) {
@Composable
fun surfaceGradient() = Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.surface,
ZcashTheme.colors.surfaceEnd
)
)
}

View File

@ -0,0 +1,52 @@
package cash.z.ecc.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import cash.z.ecc.ui.design.theme.ExtendedColors
import cash.z.ecc.ui.design.theme.internal.DarkColorPalette
import cash.z.ecc.ui.design.theme.internal.DarkExtendedColorPalette
import cash.z.ecc.ui.design.theme.internal.ExtendedTypography
import cash.z.ecc.ui.design.theme.internal.LightColorPalette
import cash.z.ecc.ui.design.theme.internal.LightExtendedColorPalette
import cash.z.ecc.ui.design.theme.internal.LocalExtendedColors
import cash.z.ecc.ui.design.theme.internal.LocalExtendedTypography
import cash.z.ecc.ui.design.theme.internal.Typography
@Composable
fun ZcashTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val baseColors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
val extendedColors = if (darkTheme) {
DarkExtendedColorPalette
} else {
LightExtendedColorPalette
}
CompositionLocalProvider(LocalExtendedColors provides extendedColors) {
MaterialTheme(
colorScheme = baseColors,
typography = Typography,
content = content
)
}
}
// Use with eg. ZcashTheme.colors.tertiary
object ZcashTheme {
val colors: ExtendedColors
@Composable
get() = LocalExtendedColors.current
val typography: ExtendedTypography
@Composable
get() = LocalExtendedTypography.current
}

View File

@ -0,0 +1,196 @@
@file:Suppress("MagicNumber")
package cash.z.ecc.ui.design.theme.internal
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import cash.z.ecc.ui.design.theme.ExtendedColors
internal object Dark {
val backgroundStart = Color(0xff243155)
val backgroundEnd = Color(0xff29365A)
val textHeaderOnBackground = Color(0xffCBDCF2)
val textBodyOnBackground = Color(0xFF93A4BE)
val textPrimaryButton = Color(0xFF0F2341)
val textSecondaryButton = Color(0xFF0F2341)
val textTertiaryButton = Color.White
val textNavigationButton = Color.Black
val textCaption = Color(0xFF68728B)
val textChipIndex = Color(0xFFFFB900)
val primaryButton = Color(0xFFFFB900)
val primaryButtonPressed = Color(0xFFFFD800)
val primaryButtonDisabled = Color(0x33F4B728)
val secondaryButton = Color(0xFFA7C0D9)
val secondaryButtonPressed = Color(0xFFC8DCEF)
val secondaryButtonDisabled = Color(0x33C8DCEF)
val tertiaryButton = Color.Transparent
val tertiaryButtonPressed = Color(0xB0C3D2BA)
// TODO how does the invisible button show a disabled state?
val navigationButton = Color(0xFFA7C0D9)
val navigationButtonPressed = Color(0xFFC8DCEF)
val progressStart = Color(0xFFF364CE)
val progressEnd = Color(0xFFF8964F)
val progressBackground = Color(0xFF929bb3)
val callout = Color(0xFFa7bed8)
val onCallout = Color(0xFF3d698f)
val overlay = Color(0x22000000)
val highlight = Color(0xFFFFD800)
val addressHighlightBorder = Color(0xFF525252)
val addressHighlightUnified = Color(0xFFFFD800)
val addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
val dangerous = Color(0xFFEC0008)
val onDangerous = Color(0xFFFFFFFF)
}
internal object Light {
val backgroundStart = Color(0xFFE3EFF9)
val backgroundEnd = Color(0xFFD2E4F3)
val textHeaderOnBackground = Color(0xff2D3747)
val textBodyOnBackground = Color(0xFF7B8897)
val textNavigationButton = Color(0xFF7B8897)
val textPrimaryButton = Color(0xFFF2F7FC)
val textSecondaryButton = Color(0xFF2E476E)
val textTertiaryButton = Color(0xFF283559)
val textCaption = Color(0xFF2D3747)
val textChipIndex = Color(0xFFEE8592)
// TODO The button colors are wrong for light
val primaryButton = Color(0xFF263357)
val primaryButtonPressed = Color(0xFFFFD800)
val primaryButtonDisabled = Color(0x33F4B728)
val secondaryButton = Color(0xFFE8F3FA)
val secondaryButtonPressed = Color(0xFFFAFBFD)
val secondaryButtonDisabled = Color(0xFFE6EFF8)
val tertiaryButton = Color.Transparent
val tertiaryButtonPressed = Color(0xFFFFFFFF)
val navigationButton = Color(0xFFE3EDF7)
val navigationButtonPressed = Color(0xFFE3EDF7)
val progressStart = Color(0xFFF364CE)
val progressEnd = Color(0xFFF8964F)
val progressBackground = Color(0xFFbeccdf)
val callout = Color(0xFFe6f0f9)
val onCallout = Color(0xFFa1b8d0)
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 addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
val dangerous = Color(0xFFEC0008)
val onDangerous = Color(0xFFFFFFFF)
}
internal val DarkColorPalette = darkColorScheme(
primary = Dark.primaryButton,
secondary = Dark.secondaryButton,
onPrimary = Dark.textPrimaryButton,
onSecondary = Dark.textSecondaryButton,
surface = Dark.backgroundStart,
onSurface = Dark.textBodyOnBackground,
background = Dark.backgroundStart,
onBackground = Dark.textBodyOnBackground,
)
internal val LightColorPalette = lightColorScheme(
primary = Light.primaryButton,
secondary = Light.secondaryButton,
onPrimary = Light.textPrimaryButton,
onSecondary = Light.textSecondaryButton,
surface = Light.backgroundStart,
onSurface = Light.textBodyOnBackground,
background = Light.backgroundStart,
onBackground = Light.textBodyOnBackground,
)
internal val DarkExtendedColorPalette = ExtendedColors(
surfaceEnd = Dark.backgroundEnd,
onBackgroundHeader = Dark.textHeaderOnBackground,
tertiary = Dark.tertiaryButton,
onTertiary = Dark.textTertiaryButton,
callout = Dark.callout,
onCallout = Dark.onCallout,
progressStart = Dark.progressStart,
progressEnd = Dark.progressEnd,
progressBackground = Dark.progressBackground,
chipIndex = Dark.textChipIndex,
overlay = Dark.overlay,
highlight = Dark.highlight,
addressHighlightBorder = Dark.addressHighlightBorder,
addressHighlightUnified = Dark.addressHighlightUnified,
addressHighlightSapling = Dark.addressHighlightSapling,
addressHighlightTransparent = Dark.addressHighlightTransparent,
addressHighlightViewing = Dark.addressHighlightViewing,
dangerous = Dark.dangerous,
onDangerous = Dark.onDangerous
)
internal val LightExtendedColorPalette = ExtendedColors(
surfaceEnd = Light.backgroundEnd,
onBackgroundHeader = Light.textHeaderOnBackground,
tertiary = Light.tertiaryButton,
onTertiary = Light.textTertiaryButton,
callout = Light.callout,
onCallout = Light.onCallout,
progressStart = Light.progressStart,
progressEnd = Light.progressEnd,
progressBackground = Light.progressBackground,
chipIndex = Light.textChipIndex,
overlay = Light.overlay,
highlight = Light.highlight,
addressHighlightBorder = Light.addressHighlightBorder,
addressHighlightUnified = Light.addressHighlightUnified,
addressHighlightSapling = Light.addressHighlightSapling,
addressHighlightTransparent = Light.addressHighlightTransparent,
addressHighlightViewing = Light.addressHighlightViewing,
dangerous = Light.dangerous,
onDangerous = Light.onDangerous
)
internal val LocalExtendedColors = staticCompositionLocalOf {
ExtendedColors(
surfaceEnd = Color.Unspecified,
onBackgroundHeader = Color.Unspecified,
tertiary = Color.Unspecified,
onTertiary = Color.Unspecified,
callout = Color.Unspecified,
onCallout = Color.Unspecified,
progressStart = Color.Unspecified,
progressEnd = Color.Unspecified,
progressBackground = Color.Unspecified,
chipIndex = Color.Unspecified,
overlay = Color.Unspecified,
highlight = Color.Unspecified,
addressHighlightBorder = Color.Unspecified,
addressHighlightUnified = Color.Unspecified,
addressHighlightSapling = Color.Unspecified,
addressHighlightTransparent = Color.Unspecified,
addressHighlightViewing = Color.Unspecified,
dangerous = Color.Unspecified,
onDangerous = Color.Unspecified
)
}

View File

@ -1,4 +1,4 @@
package cash.z.ecc.ui.theme
package cash.z.ecc.ui.design.theme.internal
import androidx.compose.material3.Typography
import androidx.compose.runtime.Immutable
@ -10,7 +10,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.unit.ExperimentalUnitApi
import androidx.compose.ui.unit.sp
import cash.z.ecc.ui.R
import cash.z.ecc.ui.design.R
private val Rubik = FontFamily(
Font(R.font.rubik_regular, FontWeight.W400),
@ -18,7 +18,7 @@ private val Rubik = FontFamily(
)
@OptIn(ExperimentalUnitApi::class)
val Typography = Typography(
internal val Typography = Typography(
headlineLarge = TextStyle(
fontFamily = Rubik,
fontWeight = FontWeight.W600,

View File

@ -50,7 +50,8 @@ dependencies {
implementation(libs.androidx.lifecycle.livedata)
implementation(libs.androidx.splash)
implementation(libs.androidx.workmanager)
implementation(libs.bundles.androidx.compose)
implementation(libs.bundles.androidx.compose.core)
implementation(libs.bundles.androidx.compose.extended)
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
@ -61,6 +62,8 @@ dependencies {
implementation(projects.preferenceApiLib)
implementation(projects.preferenceImplAndroidLib)
implementation(projects.sdkExtLib)
implementation(projects.spackleLib)
implementation(projects.uiDesignLib)
androidTestImplementation(libs.bundles.androidx.test)
androidTestImplementation(libs.androidx.compose.test.junit)

View File

@ -20,7 +20,7 @@ import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
import cash.z.ecc.sdk.model.SeedPhrase
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.CommonTag
import cash.z.ecc.ui.design.component.CommonTag
import cash.z.ecc.ui.screen.restore.RestoreTag
import cash.z.ecc.ui.screen.restore.state.WordList
import cash.z.ecc.ui.test.getStringResource

View File

@ -8,7 +8,7 @@ import cash.z.ecc.sdk.SynchronizerCompanion
import cash.z.ecc.sdk.type.fromResources
import cash.z.ecc.ui.preference.EncryptedPreferenceKeys
import cash.z.ecc.ui.preference.EncryptedPreferenceSingleton
import cash.z.ecc.ui.util.LazyWithArgument
import co.electriccoin.zcash.spackle.LazyWithArgument
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers

View File

@ -9,14 +9,12 @@ import android.os.SystemClock
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.FontRes
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.core.content.res.ResourcesCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
@ -29,9 +27,10 @@ import cash.z.ecc.sdk.model.PersistableWallet
import cash.z.ecc.sdk.model.SeedPhrase
import cash.z.ecc.sdk.model.ZecRequest
import cash.z.ecc.sdk.type.fromResources
import cash.z.ecc.ui.design.compat.FontCompat
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.screen.backup.view.BackupWallet
import cash.z.ecc.ui.screen.backup.viewmodel.BackupViewModel
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.home.view.Home
import cash.z.ecc.ui.screen.home.viewmodel.SecretState
import cash.z.ecc.ui.screen.home.viewmodel.WalletViewModel
@ -46,11 +45,7 @@ import cash.z.ecc.ui.screen.seed.view.Seed
import cash.z.ecc.ui.screen.settings.view.Settings
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
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@ -69,15 +64,14 @@ class MainActivity : ComponentActivity() {
setupSplashScreen()
if (AndroidApiVersion.isAtLeastO) {
setupUiContent()
} else {
if (FontCompat.isFontPrefetchNeeded()) {
lifecycleScope.launch {
prefetchFontLegacy(applicationContext, R.font.rubik_medium)
prefetchFontLegacy(applicationContext, R.font.rubik_regular)
FontCompat.prefetchFontsLegacy(applicationContext)
setupUiContent()
}
} else {
setupUiContent()
}
}
@ -126,7 +120,8 @@ class MainActivity : ComponentActivity() {
// the user is going through the backup flow. Don't use eager collection in the view model,
// so that the collection is still tied to UI lifecycle.
lifecycleScope.launch {
walletViewModel.synchronizer.collect()
walletViewModel.synchronizer.collect {
}
}
}
@ -434,20 +429,6 @@ private fun copyToClipboard(context: Context, persistableWallet: PersistableWall
clipboardManager.setPrimaryClip(data)
}
/**
* Pre-fetches fonts on Android N (API 25) and below.
*/
/*
* ResourcesCompat is used implicitly by Compose on older Android versions.
* The backwards compatibility library performs disk IO and then
* caches the results. This moves that IO off the main thread, to prevent ANRs and
* jank during app startup.
*/
private suspend fun prefetchFontLegacy(context: Context, @FontRes fontRes: Int) =
withContext(Dispatchers.IO) {
ResourcesCompat.getFont(context, fontRes)
}
private fun ZecRequest.newShareIntent(context: Context) = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, context.getString(R.string.request_template_format, toUri()))

View File

@ -1,9 +1,9 @@
package cash.z.ecc.ui.preference
import android.content.Context
import cash.z.ecc.ui.util.SuspendingLazy
import co.electriccoin.zcash.preference.AndroidPreferenceProvider
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.spackle.SuspendingLazy
object EncryptedPreferenceSingleton {

View File

@ -1,9 +1,9 @@
package cash.z.ecc.ui.preference
import android.content.Context
import cash.z.ecc.ui.util.SuspendingLazy
import co.electriccoin.zcash.preference.AndroidPreferenceProvider
import co.electriccoin.zcash.preference.api.PreferenceProvider
import co.electriccoin.zcash.spackle.SuspendingLazy
object StandardPreferenceSingleton {

View File

@ -1,7 +1,7 @@
package cash.z.ecc.ui.screen.backup.model
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.screen.onboarding.model.Progress
import co.electriccoin.zcash.spackle.model.Index
import co.electriccoin.zcash.spackle.model.Progress
enum class BackupStage {
// Note: the ordinal order is used to manage progression through each stage

View File

@ -1,6 +1,6 @@
package cash.z.ecc.ui.screen.backup.state
import cash.z.ecc.ui.screen.onboarding.model.Index
import co.electriccoin.zcash.spackle.model.Index
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

View File

@ -18,22 +18,22 @@ import androidx.compose.ui.tooling.preview.Preview
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import cash.z.ecc.sdk.model.PersistableWallet
import cash.z.ecc.ui.R
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.CHIP_GRID_ROW_SIZE
import cash.z.ecc.ui.design.component.Chip
import cash.z.ecc.ui.design.component.ChipGrid
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.NavigationButton
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.screen.backup.BackupTag
import cash.z.ecc.ui.screen.backup.model.BackupStage
import cash.z.ecc.ui.screen.backup.state.BackupState
import cash.z.ecc.ui.screen.backup.state.TestChoices
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.CHIP_GRID_ROW_SIZE
import cash.z.ecc.ui.screen.common.Chip
import cash.z.ecc.ui.screen.common.ChipGrid
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.Header
import cash.z.ecc.ui.screen.common.NavigationButton
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Index
@Preview(device = Devices.PIXEL_4)
@Composable

View File

@ -24,10 +24,10 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.screen.backup.BackupTag
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Index
/**
* @param chipIndex The index of the chip, which is displayed to the user.

View File

@ -8,7 +8,7 @@ import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.ui.screen.backup.model.BackupStage
import cash.z.ecc.ui.screen.backup.state.BackupState
import cash.z.ecc.ui.screen.backup.state.TestChoices
import cash.z.ecc.ui.screen.onboarding.model.Index
import co.electriccoin.zcash.spackle.model.Index
class BackupViewModel(application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) {
val backupState: BackupState = run {

View File

@ -9,19 +9,19 @@ import androidx.compose.material.icons.filled.Shield
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.Chip
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.Header
import cash.z.ecc.ui.screen.common.NavigationButton
import cash.z.ecc.ui.screen.common.PinkProgress
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.SecondaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.screen.onboarding.model.Progress
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.Chip
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.NavigationButton
import cash.z.ecc.ui.design.component.PinkProgress
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.SecondaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.screen.onboarding.view.Callout
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Index
import co.electriccoin.zcash.spackle.model.Progress
@Preview
@Composable

View File

@ -23,15 +23,15 @@ import cash.z.ecc.android.sdk.db.entity.Transaction
import cash.z.ecc.sdk.model.toZecString
import cash.z.ecc.sdk.model.total
import cash.z.ecc.ui.R
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.fixture.WalletSnapshotFixture
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.Header
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.screen.home.model.WalletSnapshot
import cash.z.ecc.ui.screen.home.model.totalBalance
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
@Preview

View File

@ -1,5 +1,8 @@
package cash.z.ecc.ui.screen.onboarding.model
import co.electriccoin.zcash.spackle.model.Index
import co.electriccoin.zcash.spackle.model.Progress
enum class OnboardingStage {
// Note: the ordinal order is used to manage progression through each stage
// so be careful if reordering these

View File

@ -27,19 +27,19 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
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.Header
import cash.z.ecc.ui.screen.common.NavigationButton
import cash.z.ecc.ui.screen.common.PinkProgress
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.SecondaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.NavigationButton
import cash.z.ecc.ui.design.component.PinkProgress
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.SecondaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.screen.onboarding.model.OnboardingStage
import cash.z.ecc.ui.screen.onboarding.model.Progress
import cash.z.ecc.ui.screen.onboarding.state.OnboardingState
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Progress
@Preview
@Composable

View File

@ -24,10 +24,10 @@ import androidx.compose.ui.unit.dp
import cash.z.ecc.sdk.fixture.WalletAddressFixture
import cash.z.ecc.sdk.model.WalletAddress
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.PrimaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.screen.profile.util.AndroidQrCodeImageGenerator
import cash.z.ecc.ui.screen.profile.util.JvmQrCodeGenerator
import cash.z.ecc.ui.theme.ZcashTheme

View File

@ -31,10 +31,10 @@ import cash.z.ecc.sdk.model.ZecRequestMessage
import cash.z.ecc.sdk.model.ZecString
import cash.z.ecc.sdk.model.fromZecString
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.TextField
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TextField
import cash.z.ecc.ui.theme.ZcashTheme
import kotlinx.coroutines.runBlocking

View File

@ -47,22 +47,22 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import cash.z.ecc.sdk.model.SeedPhraseValidation
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.CHIP_GRID_ROW_SIZE
import cash.z.ecc.ui.screen.common.Chip
import cash.z.ecc.ui.screen.common.CommonTag
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.Header
import cash.z.ecc.ui.screen.common.NavigationButton
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.TextField
import cash.z.ecc.ui.screen.onboarding.model.Index
import cash.z.ecc.ui.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.CHIP_GRID_ROW_SIZE
import cash.z.ecc.ui.design.component.Chip
import cash.z.ecc.ui.design.component.CommonTag
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.NavigationButton
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TextField
import cash.z.ecc.ui.screen.restore.RestoreTag
import cash.z.ecc.ui.screen.restore.model.ParseResult
import cash.z.ecc.ui.screen.restore.state.WordList
import cash.z.ecc.ui.screen.restore.state.wordValidation
import cash.z.ecc.ui.theme.MINIMAL_WEIGHT
import cash.z.ecc.ui.theme.ZcashTheme
import co.electriccoin.zcash.spackle.model.Index
@Preview("Restore Wallet")
@Composable

View File

@ -18,11 +18,11 @@ import androidx.compose.ui.tooling.preview.Preview
import cash.z.ecc.sdk.fixture.PersistableWalletFixture
import cash.z.ecc.sdk.model.PersistableWallet
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.Body
import cash.z.ecc.ui.screen.common.ChipGrid
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.Header
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.ChipGrid
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.Header
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.theme.ZcashTheme
@Preview("Seed")

View File

@ -13,10 +13,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import cash.z.ecc.ui.R
import cash.z.ecc.ui.screen.common.DangerousButton
import cash.z.ecc.ui.screen.common.GradientSurface
import cash.z.ecc.ui.screen.common.PrimaryButton
import cash.z.ecc.ui.screen.common.TertiaryButton
import cash.z.ecc.ui.design.component.DangerousButton
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.PrimaryButton
import cash.z.ecc.ui.design.component.TertiaryButton
import cash.z.ecc.ui.theme.ZcashTheme
@Preview("Settings")

View File

@ -40,11 +40,11 @@ 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.design.MINIMAL_WEIGHT
import cash.z.ecc.ui.design.component.Body
import cash.z.ecc.ui.design.component.GradientSurface
import cash.z.ecc.ui.design.component.ListHeader
import cash.z.ecc.ui.design.component.ListItem
import cash.z.ecc.ui.theme.ZcashTheme
import kotlinx.coroutines.runBlocking

View File

@ -1,102 +0,0 @@
@file:Suppress("MagicNumber")
package cash.z.ecc.ui.theme
import androidx.compose.ui.graphics.Color
object Dark {
val backgroundStart = Color(0xff243155)
val backgroundEnd = Color(0xff29365A)
val textHeaderOnBackground = Color(0xffCBDCF2)
val textBodyOnBackground = Color(0xFF93A4BE)
val textPrimaryButton = Color(0xFF0F2341)
val textSecondaryButton = Color(0xFF0F2341)
val textTertiaryButton = Color.White
val textNavigationButton = Color.Black
val textCaption = Color(0xFF68728B)
val textChipIndex = Color(0xFFFFB900)
val primaryButton = Color(0xFFFFB900)
val primaryButtonPressed = Color(0xFFFFD800)
val primaryButtonDisabled = Color(0x33F4B728)
val secondaryButton = Color(0xFFA7C0D9)
val secondaryButtonPressed = Color(0xFFC8DCEF)
val secondaryButtonDisabled = Color(0x33C8DCEF)
val tertiaryButton = Color.Transparent
val tertiaryButtonPressed = Color(0xB0C3D2BA)
// TODO how does the invisible button show a disabled state?
val navigationButton = Color(0xFFA7C0D9)
val navigationButtonPressed = Color(0xFFC8DCEF)
val progressStart = Color(0xFFF364CE)
val progressEnd = Color(0xFFF8964F)
val progressBackground = Color(0xFF929bb3)
val callout = Color(0xFFa7bed8)
val onCallout = Color(0xFF3d698f)
val overlay = Color(0x22000000)
val highlight = Color(0xFFFFD800)
val addressHighlightBorder = Color(0xFF525252)
val addressHighlightUnified = Color(0xFFFFD800)
val addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
val dangerous = Color(0xFFEC0008)
val onDangerous = Color(0xFFFFFFFF)
}
object Light {
val backgroundStart = Color(0xFFE3EFF9)
val backgroundEnd = Color(0xFFD2E4F3)
val textHeaderOnBackground = Color(0xff2D3747)
val textBodyOnBackground = Color(0xFF7B8897)
val textNavigationButton = Color(0xFF7B8897)
val textPrimaryButton = Color(0xFFF2F7FC)
val textSecondaryButton = Color(0xFF2E476E)
val textTertiaryButton = Color(0xFF283559)
val textCaption = Color(0xFF2D3747)
val textChipIndex = Color(0xFFEE8592)
// TODO The button colors are wrong for light
val primaryButton = Color(0xFF263357)
val primaryButtonPressed = Color(0xFFFFD800)
val primaryButtonDisabled = Color(0x33F4B728)
val secondaryButton = Color(0xFFE8F3FA)
val secondaryButtonPressed = Color(0xFFFAFBFD)
val secondaryButtonDisabled = Color(0xFFE6EFF8)
val tertiaryButton = Color.Transparent
val tertiaryButtonPressed = Color(0xFFFFFFFF)
val navigationButton = Color(0xFFE3EDF7)
val navigationButtonPressed = Color(0xFFE3EDF7)
val progressStart = Color(0xFFF364CE)
val progressEnd = Color(0xFFF8964F)
val progressBackground = Color(0xFFbeccdf)
val callout = Color(0xFFe6f0f9)
val onCallout = Color(0xFFa1b8d0)
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 addressHighlightSapling = Color(0xFF1BBFF6)
val addressHighlightTransparent = Color(0xFF97999A)
val addressHighlightViewing = Color(0xFF504062)
val dangerous = Color(0xFFEC0008)
val onDangerous = Color(0xFFFFFFFF)
}

View File

@ -1,170 +0,0 @@
package cash.z.ecc.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
private val DarkColorPalette = darkColorScheme(
primary = Dark.primaryButton,
secondary = Dark.secondaryButton,
onPrimary = Dark.textPrimaryButton,
onSecondary = Dark.textSecondaryButton,
surface = Dark.backgroundStart,
onSurface = Dark.textBodyOnBackground,
background = Dark.backgroundStart,
onBackground = Dark.textBodyOnBackground,
)
private val LightColorPalette = lightColorScheme(
primary = Light.primaryButton,
secondary = Light.secondaryButton,
onPrimary = Light.textPrimaryButton,
onSecondary = Light.textSecondaryButton,
surface = Light.backgroundStart,
onSurface = Light.textBodyOnBackground,
background = Light.backgroundStart,
onBackground = Light.textBodyOnBackground,
)
@Immutable
data class ExtendedColors(
val surfaceEnd: Color,
val onBackgroundHeader: Color,
val tertiary: Color,
val onTertiary: Color,
val callout: Color,
val onCallout: Color,
val progressStart: Color,
val progressEnd: Color,
val progressBackground: Color,
val chipIndex: Color,
val overlay: Color,
val highlight: Color,
val addressHighlightBorder: Color,
val addressHighlightUnified: Color,
val addressHighlightSapling: Color,
val addressHighlightTransparent: Color,
val addressHighlightViewing: Color,
val dangerous: Color,
val onDangerous: Color
) {
@Composable
fun surfaceGradient() = Brush.verticalGradient(
colors = listOf(
MaterialTheme.colorScheme.surface,
ZcashTheme.colors.surfaceEnd
)
)
}
val DarkExtendedColorPalette = ExtendedColors(
surfaceEnd = Dark.backgroundEnd,
onBackgroundHeader = Dark.textHeaderOnBackground,
tertiary = Dark.tertiaryButton,
onTertiary = Dark.textTertiaryButton,
callout = Dark.callout,
onCallout = Dark.onCallout,
progressStart = Dark.progressStart,
progressEnd = Dark.progressEnd,
progressBackground = Dark.progressBackground,
chipIndex = Dark.textChipIndex,
overlay = Dark.overlay,
highlight = Dark.highlight,
addressHighlightBorder = Dark.addressHighlightBorder,
addressHighlightUnified = Dark.addressHighlightUnified,
addressHighlightSapling = Dark.addressHighlightSapling,
addressHighlightTransparent = Dark.addressHighlightTransparent,
addressHighlightViewing = Dark.addressHighlightViewing,
dangerous = Dark.dangerous,
onDangerous = Dark.onDangerous
)
val LightExtendedColorPalette = ExtendedColors(
surfaceEnd = Light.backgroundEnd,
onBackgroundHeader = Light.textHeaderOnBackground,
tertiary = Light.tertiaryButton,
onTertiary = Light.textTertiaryButton,
callout = Light.callout,
onCallout = Light.onCallout,
progressStart = Light.progressStart,
progressEnd = Light.progressEnd,
progressBackground = Light.progressBackground,
chipIndex = Light.textChipIndex,
overlay = Light.overlay,
highlight = Light.highlight,
addressHighlightBorder = Light.addressHighlightBorder,
addressHighlightUnified = Light.addressHighlightUnified,
addressHighlightSapling = Light.addressHighlightSapling,
addressHighlightTransparent = Light.addressHighlightTransparent,
addressHighlightViewing = Light.addressHighlightViewing,
dangerous = Light.dangerous,
onDangerous = Light.onDangerous
)
val LocalExtendedColors = staticCompositionLocalOf {
ExtendedColors(
surfaceEnd = Color.Unspecified,
onBackgroundHeader = Color.Unspecified,
tertiary = Color.Unspecified,
onTertiary = Color.Unspecified,
callout = Color.Unspecified,
onCallout = Color.Unspecified,
progressStart = Color.Unspecified,
progressEnd = Color.Unspecified,
progressBackground = Color.Unspecified,
chipIndex = Color.Unspecified,
overlay = Color.Unspecified,
highlight = Color.Unspecified,
addressHighlightBorder = Color.Unspecified,
addressHighlightUnified = Color.Unspecified,
addressHighlightSapling = Color.Unspecified,
addressHighlightTransparent = Color.Unspecified,
addressHighlightViewing = Color.Unspecified,
dangerous = Color.Unspecified,
onDangerous = Color.Unspecified
)
}
@Composable
fun ZcashTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val baseColors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
val extendedColors = if (darkTheme) {
DarkExtendedColorPalette
} else {
LightExtendedColorPalette
}
CompositionLocalProvider(LocalExtendedColors provides extendedColors) {
MaterialTheme(
colorScheme = baseColors,
typography = Typography,
content = content
)
}
}
// Use with eg. ZcashTheme.colors.tertiary
object ZcashTheme {
val colors: ExtendedColors
@Composable
get() = LocalExtendedColors.current
val typography: ExtendedTypography
@Composable
get() = LocalExtendedTypography.current
}