diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index d6afcd19..7ef4bf7d 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -6,7 +6,7 @@ This code review checklist is intended to serve as a starting point for the auth
- [ ] Automated tests: Did you add appropriate automated tests for any code changes?
- [ ] Manual tests: Did you update the [manual tests](../blob/main/docs/testing/manual_testing) as appropriate? _While we aim for automated testing of the application, some aspects require manual testing. If you had to manually test something during development of this pull request, write those steps down._
- [ ] Code coverage: Did you check the code coverage report for the automated tests? _While we are not looking for perfect coverage, the tool can point out potential cases that have been missed._
-- [ ] Documentation: Did you update documentation as appropiate? (e.g [README.md](../blob/main/README.md), etc.)
+- [ ] Documentation: Did you update documentation as appropriate? (e.g [README.md](../blob/main/README.md), etc.)
- [ ] Run the app: Did you run the app and try the changes?
- [ ] Screenshots: Did you provide before and after UI screenshots in the description of this pull request? _This is only applicable for changes that modify the UI._
- [ ] Rebase and squash: Did you pull in the latest changes from the main branch and squash your commits before assigning a reviewer? _Having your code up to date and squashed will make it easier for others to review. Use best judgement when squashing commits, as some changes (such as refactoring) might be easier to review as a separate commit._
diff --git a/.gitignore b/.gitignore
index bf49da05..ffc7bf5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ bin/
build/
gen/
local.properties
+/.idea/deploymentTargetDropDown.xml
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 00000000..f2c71e49
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,4 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+misc.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 00000000..52f1cea3
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+zcash-android-app
\ No newline at end of file
diff --git a/.idea/runConfigurations/clean.xml b/.idea/runConfigurations/clean.xml
new file mode 100644
index 00000000..269467e9
--- /dev/null
+++ b/.idea/runConfigurations/clean.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/dependencyUpdates.xml b/.idea/runConfigurations/dependencyUpdates.xml
new file mode 100644
index 00000000..98b02f01
--- /dev/null
+++ b/.idea/runConfigurations/dependencyUpdates.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/detektAll.xml b/.idea/runConfigurations/detektAll.xml
new file mode 100644
index 00000000..a845280d
--- /dev/null
+++ b/.idea/runConfigurations/detektAll.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/lint.xml b/.idea/runConfigurations/lint.xml
new file mode 100644
index 00000000..8d4552ba
--- /dev/null
+++ b/.idea/runConfigurations/lint.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index a10e13b9..52115758 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,7 @@ While we aim to continue improving this sample, it is not an official product.
If you'd like to compile this application from source, please see our [Setup Documentation](docs/Setup.md) to get started.
# Reporting an issue
-If you wish to report a security issue, please follow our [Responsible Disclosure guidelines](https://github.com/zcash/ZcashLightClientKit/blob/master/responsible_disclosure.md). See the [Wallet App Threat Model](https://zcash.readthedocs.io/en/latest/rtd_pages/wallet_threat_model.html) for more information about the security and privacy limitations of the wallet. There are some known security and privacy limitations:
-- Traffic analysis, like in other cryptocurrency wallets, can leak some privacy of the user.
-- The wallet requires a trust in the server to display accurate transaction information.
+If you wish to report a security issue, please follow our [Responsible Disclosure guidelines](https://github.com/zcash/ZcashLightClientKit/blob/master/responsible_disclosure.md). See the [Wallet App Threat Model](https://zcash.readthedocs.io/en/latest/rtd_pages/wallet_threat_model.html) for more information about the security and privacy limitations of the wallet.
If you'd like to report a technical issue or feature request for the Android Wallet, please file a [GitHub issue](https://github.com/zcash/secant-android-wallet/issues/new/choose).
@@ -21,3 +19,16 @@ General Zcash questions and/or support requests and are best directed to either:
# Contributing
Contributions are very much welcomed! Please read our [Contributing Guidelines](docs/CONTRIBUTING.md) to learn about our process.
+
+# Forking
+If you plan to fork the project to create a new app of your own, please make the following changes. (If you're making a GitHub fork to contribute back to the project, these steps are not necessary.)
+
+1. Change the app name under app/
+1. Remove any copyrighted ZCash or Electric Coin Company icons, logos, or assets
+1. Change the package name
+ 1. Under [app/build.gradle.kts](app/build.gradle.kts), change the package name of the application
+ 1. Under [app/proguard-project.txt](app/proguard-project.txt), change the `-repackageclasses` directive to your own package name
+
+# Known Issues
+
+1. During builds, a warning will be printed that says "Unable to detect AGP versions for included builds. All projects in the build should use the same AGP version." This can be safely ignored. The version under build-conventions is the same as the version used elsewhere in the application.
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 00000000..ca253012
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,103 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("kotlin-parcelize")
+ id("androidx.navigation.safeargs")
+ id("zcash.android-build-conventions")
+}
+
+val packageName = "cash.z.ecc.android"
+
+android {
+ defaultConfig {
+ applicationId = packageName
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ buildFeatures {
+ viewBinding = true
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = libs.versions.compose.get()
+ }
+
+ flavorDimensions.add("network")
+
+ productFlavors {
+ // would rather name them "testnet" and "mainnet" but product flavor names cannot start with the word "test"
+ create("zcashtestnet") {
+ dimension = "network"
+ applicationId = "$packageName.testnet" // allow to be installed alongside mainnet
+ matchingFallbacks.addAll(listOf("zcashtestnet", "debug"))
+ }
+
+ create("zcashmainnet") {
+ dimension = "network"
+ applicationId = packageName
+ matchingFallbacks.addAll(listOf("zcashmainnet", "release"))
+ }
+ }
+
+ buildTypes {
+ getByName("release").apply {
+ isMinifyEnabled = project.property("IS_MINIFY_ENABLED").toString().toBoolean()
+ proguardFiles.addAll(
+ listOf(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ File("proguard-project.txt")
+ )
+ )
+ }
+ }
+
+ signingConfigs {
+ val releaseKeystorePath = project.property("ZCASH_RELEASE_KEYSTORE_PATH").toString()
+ val releaseKeystorePassword = project.property("ZCASH_RELEASE_KEYSTORE_PASSWORD").toString()
+ val releaseKeyAlias = project.property("ZCASH_RELEASE_KEY_ALIAS").toString()
+ val releaseKeyAliasPassword =
+ project.property("ZCASH_RELEASE_KEY_ALIAS_PASSWORD").toString()
+ val isReleaseSigningConfigured = listOf(
+ releaseKeystorePath,
+ releaseKeystorePassword,
+ releaseKeyAlias,
+ releaseKeyAliasPassword
+ ).all { !it.isNullOrBlank() }
+
+ if (isReleaseSigningConfigured) {
+ // If this block doesn't execute, the output will be unsigned
+ create("release").apply {
+ storeFile = File(releaseKeystorePath)
+ storePassword = releaseKeystorePassword
+ keyAlias = releaseKeyAlias
+ keyPassword = releaseKeyAliasPassword
+ }
+ }
+ }
+
+ // TODO [#5]: Figure out how to move this into the build-conventions
+ testCoverage {
+ jacocoVersion = libs.versions.jacoco.get()
+ }
+
+ // TODO [#6]: Figure out how to move this into the build-conventions
+ kotlinOptions {
+ jvmTarget = libs.versions.java.get()
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.activity)
+ implementation(libs.androidx.annotation)
+ implementation(libs.androidx.core)
+ implementation(libs.bundles.androidx.compose)
+ implementation(libs.google.material)
+ implementation(libs.kotlin)
+ implementation(libs.kotlinx.coroutines.android)
+ implementation(libs.kotlinx.coroutines.core)
+ implementation(libs.zcash)
+
+ androidTestImplementation(libs.bundles.androidx.test)
+}
diff --git a/app/proguard-project.txt b/app/proguard-project.txt
new file mode 100644
index 00000000..3fd87cff
--- /dev/null
+++ b/app/proguard-project.txt
@@ -0,0 +1,9 @@
+# This improves obfuscation.
+-repackageclasses 'cash.z.ecc.android'
+
+# Ensure that stacktraces are reversible.
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable
+
+# Generate the combined proguard configuration for debugging.
+-printconfiguration build/outputs/proguard-config.txt
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..d59e98a7
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/cash/z/ecc/android/app/App.kt b/app/src/main/java/cash/z/ecc/android/app/App.kt
new file mode 100644
index 00000000..9b28b058
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/App.kt
@@ -0,0 +1,19 @@
+package cash.z.ecc.android.app
+
+import android.app.Application
+import cash.z.ecc.android.sdk.demoapp.BuildConfig
+import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
+import cash.z.ecc.android.sdk.ext.Twig
+
+class App : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+
+ if (BuildConfig.DEBUG) {
+ StrictModeHelper.enableStrictMode()
+ }
+
+ Twig.plant(TroubleshootingTwig())
+ }
+}
diff --git a/app/src/main/java/cash/z/ecc/android/app/MainActivity.kt b/app/src/main/java/cash/z/ecc/android/app/MainActivity.kt
new file mode 100644
index 00000000..605bf5ba
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/MainActivity.kt
@@ -0,0 +1,38 @@
+package cash.z.ecc.android.app
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import cash.z.ecc.android.app.ui.theme.MyApplicationTheme
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MyApplicationTheme {
+ // A surface container using the 'background' color from the theme
+ Surface(color = MaterialTheme.colors.background) {
+ Greeting("Android")
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun Greeting(name: String) {
+ Text(text = "Hello $name!")
+}
+
+@Preview(showBackground = true)
+@Composable
+fun DefaultPreview() {
+ MyApplicationTheme {
+ Greeting("Android")
+ }
+}
diff --git a/app/src/main/java/cash/z/ecc/android/app/StrictModeHelper.kt b/app/src/main/java/cash/z/ecc/android/app/StrictModeHelper.kt
new file mode 100644
index 00000000..fc43cad5
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/StrictModeHelper.kt
@@ -0,0 +1,61 @@
+package cash.z.ecc.android.app
+
+import android.annotation.SuppressLint
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import android.os.StrictMode
+
+
+object StrictModeHelper {
+
+ fun enableStrictMode() {
+ configureStrictMode()
+
+ // Workaround for Android bug
+ // https://issuetracker.google.com/issues/36951662
+ // Not needed if target O_MR1 and running on O_MR1
+ // Don't really need to check target, because of Google Play enforcement on targetSdkVersion for app updates
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
+ Handler(Looper.getMainLooper()).postAtFrontOfQueue { configureStrictMode() }
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private fun configureStrictMode() {
+ StrictMode.enableDefaults()
+
+ StrictMode.setThreadPolicy(
+ StrictMode.ThreadPolicy.Builder().apply {
+ detectAll()
+ penaltyLog()
+ }.build()
+ )
+
+ // Don't enable missing network tags, because those are noisy.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ StrictMode.setVmPolicy(
+ StrictMode.VmPolicy.Builder().apply {
+ detectActivityLeaks()
+ detectCleartextNetwork()
+ detectContentUriWithoutPermission()
+ detectFileUriExposure()
+ detectLeakedClosableObjects()
+ detectLeakedRegistrationObjects()
+ detectLeakedSqlLiteObjects()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ // Disable because this is mostly flagging Android X and Play Services
+ // builder.detectNonSdkApiUsage();
+ }
+ }.build()
+ )
+ } else {
+ StrictMode.setVmPolicy(
+ StrictMode.VmPolicy.Builder().apply {
+ detectAll()
+ penaltyLog()
+ }.build()
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/cash/z/ecc/android/app/ui/theme/Color.kt b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Color.kt
new file mode 100644
index 00000000..3d0af38c
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Color.kt
@@ -0,0 +1,15 @@
+package cash.z.ecc.android.app.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+@Suppress("MagicNumber")
+val Purple200 = Color(0xFFBB86FC)
+
+@Suppress("MagicNumber")
+val Purple500 = Color(0xFF6200EE)
+
+@Suppress("MagicNumber")
+val Purple700 = Color(0xFF3700B3)
+
+@Suppress("MagicNumber")
+val Teal200 = Color(0xFF03DAC5)
diff --git a/app/src/main/java/cash/z/ecc/android/app/ui/theme/Shape.kt b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Shape.kt
new file mode 100644
index 00000000..a529f4ec
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package cash.z.ecc.android.app.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
diff --git a/app/src/main/java/cash/z/ecc/android/app/ui/theme/Theme.kt b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Theme.kt
new file mode 100644
index 00000000..2ed41724
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Theme.kt
@@ -0,0 +1,47 @@
+package cash.z.ecc.android.app.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun MyApplicationTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable() () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
diff --git a/app/src/main/java/cash/z/ecc/android/app/ui/theme/Type.kt b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Type.kt
new file mode 100644
index 00000000..2ff203ed
--- /dev/null
+++ b/app/src/main/java/cash/z/ecc/android/app/ui/theme/Type.kt
@@ -0,0 +1,28 @@
+package cash.z.ecc.android.app.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..95bbf453
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..74852f7d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..184de9f5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..184de9f5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..a571e600
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..61da551c
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c41dd285
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..db5080a7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..6dba46da
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..da31a871
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..15ac6817
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..b216f2d3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..f25a4197
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e96783cc
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..203e2191
--- /dev/null
+++ b/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..b17f0a12
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..7335191b
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Demo App
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..65789b57
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/zcashmainnet/res/values/strings.xml b/app/src/zcashmainnet/res/values/strings.xml
new file mode 100644
index 00000000..b05223d2
--- /dev/null
+++ b/app/src/zcashmainnet/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Mainnet Demo
+ Mainnet
+
diff --git a/app/src/zcashtestnet/res/values/strings.xml b/app/src/zcashtestnet/res/values/strings.xml
new file mode 100644
index 00000000..fe2c3908
--- /dev/null
+++ b/app/src/zcashtestnet/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Testnet Demo
+ Testnet
+
diff --git a/build-conventions/build.gradle.kts b/build-conventions/build.gradle.kts
new file mode 100644
index 00000000..5790f6d3
--- /dev/null
+++ b/build-conventions/build.gradle.kts
@@ -0,0 +1,21 @@
+import org.jetbrains.kotlin.konan.properties.loadProperties
+
+plugins {
+ `kotlin-dsl`
+}
+
+
+dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
+ implementation("com.android.tools.build:gradle:${getAndroidGradlePluginVersion()}")
+}
+
+fun getAndroidGradlePluginVersion(): String {
+ // A slightly gross way to use the root gradle.properties as the single source of truth for version numbers
+ val properties = run {
+ val rootPropertiesFile = File(project.projectDir.parentFile, "gradle.properties")
+ loadProperties(rootPropertiesFile.path)
+ }
+
+ return properties.getProperty("ANDROID_GRADLE_PLUGIN_VERSION")
+}
diff --git a/build-conventions/settings.gradle.kts b/build-conventions/settings.gradle.kts
new file mode 100644
index 00000000..c17d3d73
--- /dev/null
+++ b/build-conventions/settings.gradle.kts
@@ -0,0 +1,15 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ }
+}
+
+@Suppress("UnstableApiUsage")
+dependencyResolutionManagement {
+ repositories {
+ mavenCentral()
+ google()
+ }
+}
+
+rootProject.name = "build-conventions"
diff --git a/build-conventions/src/main/kotlin/zcash.android-build-conventions.gradle.kts b/build-conventions/src/main/kotlin/zcash.android-build-conventions.gradle.kts
new file mode 100644
index 00000000..0623515e
--- /dev/null
+++ b/build-conventions/src/main/kotlin/zcash.android-build-conventions.gradle.kts
@@ -0,0 +1,85 @@
+pluginManager.withPlugin("com.android.application") {
+ project.the().apply {
+ configureBaseExtension()
+
+ defaultConfig {
+ minSdk = (project.property("ANDROID_MIN_SDK_VERSION").toString().toInt())
+ targetSdk = (project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt())
+
+ // en_XA and ar_XB are pseudolocales for debugging.
+ // The rest of the locales provides an explicit list of the languages to keep in the
+ // final app. Doing this will strip out additional locales from libraries like
+ // Google Play Services and Firebase, which add unnecessary bloat.
+ resourceConfigurations.addAll(listOf("en", "en-rUS", "en-rGB", "en-rAU", "en_XA", "ar_XB"))
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+ if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
+ testInstrumentationRunnerArguments["clearPackageData"] = "true"
+ }
+ }
+ }
+}
+
+pluginManager.withPlugin("com.android.library") {
+ project.the().apply {
+ configureBaseExtension()
+
+ defaultConfig {
+ minSdk = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt()
+ targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt()
+
+ // The last two are for support of pseudolocales in debug builds.
+ // If we add other localizations, they should be included in this list.
+ // By explicitly setting supported locales, we strip out unused localizations from third party
+ // libraries (e.g. play services)
+ resourceConfigurations.addAll(listOf("en", "en-rUS", "en-rGB", "en-rAU", "en_XA", "ar_XB"))
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("proguard-consumer.txt")
+
+ if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
+ testInstrumentationRunnerArguments["clearPackageData"] = "true"
+ }
+ }
+ testCoverage {
+ jacocoVersion = project.property("JACOCO_VERSION").toString()
+ }
+ }
+}
+
+fun com.android.build.gradle.BaseExtension.configureBaseExtension() {
+ compileSdkVersion(project.property("ANDROID_COMPILE_SDK_VERSION").toString().toInt())
+ ndkVersion = project.property("ANDROID_NDK_VERSION").toString()
+
+ compileOptions {
+ val javaVersion = JavaVersion.toVersion(project.property("ANDROID_JVM_TARGET").toString())
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+ }
+
+ buildTypes {
+ getByName("debug").apply {
+ isTestCoverageEnabled = project.property("IS_COVERAGE_ENABLED").toString().toBoolean()
+ }
+ }
+
+ signingConfigs {
+ val debugKeystorePath = project.property("ZCASH_DEBUG_KEYSTORE_PATH").toString()
+ val isExplicitDebugSigningEnabled = !debugKeystorePath.isNullOrBlank()
+ if (isExplicitDebugSigningEnabled) {
+ // If this block doesn't execute, the output will still be signed with the default keystore
+ getByName("debug").apply {
+ storeFile = File(debugKeystorePath)
+ }
+ }
+ }
+
+ testOptions {
+ animationsDisabled = true
+
+ if (project.property("IS_USE_TEST_ORCHESTRATOR").toString().toBoolean()) {
+ execution = "ANDROIDX_TEST_ORCHESTRATOR"
+ }
+ }
+}
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..8384a456
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,58 @@
+import org.gradle.internal.impldep.org.junit.experimental.categories.Categories.CategoryFilter.exclude
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://jitpack.io")
+ gradlePluginPortal()
+ }
+ dependencies {
+ classpath("com.android.tools.build:gradle:${properties["ANDROID_GRADLE_PLUGIN_VERSION"]}")
+ classpath("androidx.navigation:navigation-safe-args-gradle-plugin:${properties["ANDROIDX_NAVIGATION_VERSION"]}")
+ }
+}
+
+plugins {
+ kotlin("jvm")
+ id("com.github.ben-manes.versions")
+ id("io.gitlab.arturbosch.detekt")
+}
+
+tasks {
+ register("detektAll", io.gitlab.arturbosch.detekt.Detekt::class) {
+ parallel = true
+ setSource(files(projectDir))
+ include("**/*.kt")
+ include("**/*.kts")
+ exclude("**/resources/**")
+ exclude("**/build/**")
+ exclude("**/commonTest/**")
+ exclude("**/jvmTest/**")
+ exclude("**/androidTest/**")
+ config.setFrom(files("${rootProject.projectDir}/tools/detekt.yml"))
+ buildUponDefaultConfig = true
+ }
+
+ withType {
+ gradleReleaseChannel = "current"
+
+ resolutionStrategy {
+ componentSelection {
+ all {
+ if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
+ reject("Unstable")
+ }
+ }
+ }
+ }
+ }
+}
+
+val unstableKeywords = listOf("alpha", "beta", "rc", "m", "ea", "build")
+
+fun isNonStable(version: String): Boolean {
+ val versionLowerCase = version.toLowerCase()
+
+ return unstableKeywords.any { versionLowerCase.contains(it) }
+}
diff --git a/docs/Architecture.md b/docs/Architecture.md
index 06692239..d5658d47 100644
--- a/docs/Architecture.md
+++ b/docs/Architecture.md
@@ -1,2 +1,7 @@
# Architecture
-TODO This is a placeholder for describing the app architecture.
\ No newline at end of file
+TODO This is a placeholder for describing the app architecture.
+## Gradle
+ * Versions are declared in `gradle.properties`. There's still enough inconsistency in how versions are handled in Gradle, that this is as close as we can get to a universal system. A version catalog is used for dependencies and is configured in `settings.gradle.kts`, but other versions like Gradle Plug-ins, the NDK version, Java version, and Android SDK versions don't fit into the version catalog model and are read directly from the properties
+ * Much of the Gradle configuration lives in `build-conventions` to prevent repetitive configuration as additional modules are added to the project
+ * Build scripts are written in Kotlin, so that a single language is used across build and the app code bases
+ * Only Gradle, Google, and JetBrains plug-ins are included in the critical path. Third party plug-ins can be used, but they're outside the critical path. For example, the Gradle Versions Plugin could be removed and wouldn't negative impact building, testing, or deploying the app
\ No newline at end of file
diff --git a/docs/CODE_REVIEW_GUIDELINES.md b/docs/CODE_REVIEW_GUIDELINES.md
index a606f5bc..e4dfba51 100644
--- a/docs/CODE_REVIEW_GUIDELINES.md
+++ b/docs/CODE_REVIEW_GUIDELINES.md
@@ -1,8 +1,8 @@
# Overall:
-- Does the contribution referece an existing Issue?
+- Does the contribution reference an existing GitHub issue?
- Are the requirements well defined?
-
+- Does static analysis pass? (e.g. `./gradlew detektAll` and `./gradlew lint`)
# Specification:
diff --git a/docs/Setup.md b/docs/Setup.md
index d6b0d7a0..7c2b8e3f 100644
--- a/docs/Setup.md
+++ b/docs/Setup.md
@@ -1,24 +1,18 @@
# Setup Guide
-## Gradle
-The app is built with Gradle and can be compiled on macOS, Windows, and Linux.
+The app is built with Gradle and can be compiled on macOS, Windows, and Linux. Development is typically done with the latest stable release of Android Studio.
Tip: On macOS and Linux, Gradle is invoked with `./gradlew`. On Windows, Gradle is invoked with `gradlew`. Throughout the documentation, the macOS and Linux syntax is used by default.
Tip: You do NOT need to install Gradle yourself. Running it from the command line or building the application within Android Studio will download the necessary dependencies automatically.
-## Android Studio
-Development is typically done with the latest stable release of Android Studio.
-
## Step by Step
To get set up for development, there are several steps that you need to go through. Going through these steps in order is important, as each step in the progression builds on the prior steps.
Start by making sure the command line with Gradle works first, because **all the Android Studio run configurations use Gradle internally.** The run configurations are not magic—they map directly to command line invocations with different arguments.
-When troubleshooting, fall back to trying the command line. Android Studio is a large and complex application, although under the hood it uses Gradle. Sometimes Android Studio will have a bug or failure that is bypassed by using the Gradle command line directly.
-
1. Install Java
1. Java 16 is currently recommended. Java 11 is the minimum requirement for Android Studio.
- 1. To simplify installation, use [Oracle's JDK](https://www.oracle.com/java/technologies/javase-jdk15-downloads.html) installer that will place the Java installation in the right place
+ 1. To simplify installation, use [Oracle's JDK](https://www.oracle.com/java/technologies/javase-jdk16-downloads.html) installer that will place the Java installation in the right place
1. Install Android Studio and the Android SDK
1. Download the [stable release of Android Studio](https://developer.android.com/studio#downloads)
1. TODO: Fill in step-by-step instructions for setting up a new environment and installing the Android SDK from within Android Studio.
@@ -31,14 +25,33 @@ When troubleshooting, fall back to trying the command line. Android Studio is a
1. From within Android Studio, choose to open an existing project and navigate to the checked out repo
1. After Android Studio finishes syncing with Gradle, look for the green "play" run button in the toolbar. To the left of it, choose the "App" run configuration under the dropdown menu. Then hit the run button
-# Troubleshooting
-Sometimes during upgrades to Gradle or Kotlin, corruption or weird issues can happen with a build. Try troubleshooting with the following steps:
-
+## Troubleshooting
1. Try running from the command line instead of Android Studio, to rule out Android Studio issues. If it works from the command line, try this step to reset Android Studio
- 1. Quit Android Studio
- 2. Deleting the invisible `.idea` in the root directory of the project
- 3. Relaunch Android Studio
+ 1. Quit Android Studio
+ 2. Deleting the invisible `.idea` in the root directory of the project
+ 3. Relaunch Android Studio
2. Clean the individual Gradle project by running `./gradlew clean` which will purge local build outputs.
3. Run Gradle with the flag `--rerun-tasks` which will effectively disable the build cache by re-running tasks and repopulating the cache. E.g. `./gradlew assemble --rerun-tasks`
4. Reboot your computer, which will ensure that Gradle and Kotlin daemons are completely killed and relaunched
-5. Delete the global Gradle cache under `~/.gradle/caches`
\ No newline at end of file
+5. Delete the global Gradle cache under `~/.gradle/caches`
+
+## Gradle Tasks
+A variety of Gradle tasks are set up within the project, and these tasks are also accessible in Android Studio as run configurations.
+`assemble` - Compiles the application
+`detektAll` - Performs static analysis with Detekt
+`lint` - Performs static analysis with Android lint
+`dependencyUpdates` - Checks for available dependency updates
+
+## Gradle Properties
+A variety of Gradle properties can be used to configure the build.
+### Debug Signing
+By default, the application is signed by the developers automatically generated debug signing key. In a team of developers, it may be advantageous to share a debug key so that debug builds can access key-restricted services such as Firebase or Google Maps. For such a setup, the path to a shared debug signing key can be set with the property `ZCASH_DEBUG_KEYSTORE_PATH`.
+
+### Release Signing
+To enable release signing, a release keystore needs to be provided during the build. This can be injected securely by setting the following Gradle properties.
+* ZCASH_RELEASE_KEYSTORE_PATH
+* ZCASH_RELEASE_KEYSTORE_PASSWORD
+* ZCASH_RELEASE_KEY_ALIAS
+* ZCASH_RELEASE_KEY_ALIAS_PASSWORD
+
+On a developer machine, these might be set under the user's global properties (e.g. `~/.gradle/gradle.properties` on macOS and Linux). On a continuous integration machine, these can also be set using environment variables with the prefix `ORG_GRADLE_PROJECT_` (e.g. `ORG_GRADLE_PROJECT_ZCASH_RELEASE_KEYSTORE_PATH`). DO NOT set these in the gradle.properties inside the Git repository, as this will leak your keystore password.
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..8136bd56
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,63 @@
+# Speed up builds. Keep these flags here for quick debugging of issues.
+org.gradle.vfs.watch=true
+org.gradle.configureondemand=false
+org.gradle.caching=true
+org.gradle.parallel=true
+
+kapt.include.compile.classpath=false
+kapt.incremental.apt=true
+kapt.use.worker.api=true
+
+android.useAndroidX=true
+android.builder.sdkDownload=true
+
+# Optionally configure code coverage, as historically Jacoco has at times been buggy with respect to new Kotlin versions
+IS_COVERAGE_ENABLED=true
+
+# Optionally configure test orchestrator.
+# It is disabled by default, because it causes tests to take about 2x longer to run.
+IS_USE_TEST_ORCHESTRATOR=false
+
+# Optionally disable minification
+IS_MINIFY_ENABLED=true
+
+# Set keystore details to enable build signing. Typically these
+# are overridden via ~/.gradle/gradle.properties to allow secure injection.
+# Debug keystore is useful if using Google Maps or Firebase, which require API keys to be linked
+# to a signing key. Without a debug keystore, the default Android debug keystore will be used.
+# Without a release signing configuration, the release output will not be signed.
+ZCASH_DEBUG_KEYSTORE_PATH=
+ZCASH_RELEASE_KEYSTORE_PATH=
+ZCASH_RELEASE_KEYSTORE_PASSWORD=
+ZCASH_RELEASE_KEY_ALIAS=
+ZCASH_RELEASE_KEY_ALIAS_PASSWORD=
+
+# Versions
+ANDROID_MIN_SDK_VERSION=24
+ANDROID_TARGET_SDK_VERSION=31
+ANDROID_COMPILE_SDK_VERSION=31
+
+ANDROID_NDK_VERSION=23.0.7599858
+
+ANDROID_GRADLE_PLUGIN_VERSION=7.0.2
+DETEKT_VERSION=1.18.1
+GRADLE_VERSIONS_PLUGIN_VERSION=0.38.0
+
+ANDROIDX_ACTIVITY_VERSION=1.3.1
+ANDROIDX_ANNOTATION_VERSION=1.2.0
+ANDROIDX_APPCOMPAT_VERSION=1.3.1
+ANDROIDX_COMPOSE_VERSION=1.0.2
+ANDROIDX_CORE_VERSION=1.6.0
+ANDROIDX_ESPRESSO_VERSION=3.4.0
+ANDROIDX_NAVIGATION_VERSION=2.3.5
+ANDROIDX_TEST_JUNIT_VERSION=1.1.3
+ANDROIDX_TEST_ORCHESTRATOR_VERSION=1.1.0-alpha1
+ANDROIDX_UI_AUTOMATOR_VERSION=2.2.0-alpha1
+GOOGLE_MATERIAL_VERSION=1.4.0
+JACOCO_VERSION=0.8.7
+KOTLINX_COROUTINES_VERSION=1.5.1
+KOTLIN_VERSION=1.5.21
+ZCASH_SDK_VERSION=1.3.0-beta18
+
+# This shouldn't be changed, as Android doesn't support targets beyond Java 8
+ANDROID_JVM_TARGET=1.8
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..7454180f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..ffed3a25
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..f6bf0ab3
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,110 @@
+enableFeaturePreview("VERSION_CATALOGS")
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ google()
+ }
+
+ plugins {
+ val detektVersion = extra["DETEKT_VERSION"].toString()
+ val gradleVersionsPluginVersion = extra["GRADLE_VERSIONS_PLUGIN_VERSION"].toString()
+ val kotlinVersion = extra["KOTLIN_VERSION"].toString()
+
+ kotlin("jvm") version (kotlinVersion)
+ id("com.github.ben-manes.versions") version (gradleVersionsPluginVersion) apply (false)
+ id("io.gitlab.arturbosch.detekt") version (detektVersion) apply (false)
+ }
+}
+
+dependencyResolutionManagement {
+ @Suppress("UnstableApiUsage")
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://jitpack.io")
+ }
+
+ @Suppress("UnstableApiUsage", "MaxLineLength")
+ versionCatalogs {
+ create("libs") {
+ val androidxActivityVersion = extra["ANDROIDX_ACTIVITY_VERSION"].toString()
+ val androidxAnnotationVersion = extra["ANDROIDX_ANNOTATION_VERSION"].toString()
+ val androidxAppcompatVersion = extra["ANDROIDX_APPCOMPAT_VERSION"].toString()
+ val androidxComposeVersion = extra["ANDROIDX_COMPOSE_VERSION"].toString()
+ val androidxCoreVersion = extra["ANDROIDX_CORE_VERSION"].toString()
+ val androidxEspressoVersion = extra["ANDROIDX_ESPRESSO_VERSION"].toString()
+ val androidxTestJunitVersion = extra["ANDROIDX_TEST_JUNIT_VERSION"].toString()
+ val androidxTestOrchestratorVersion = extra["ANDROIDX_ESPRESSO_VERSION"].toString()
+ val androidxUiAutomatorVersion = extra["ANDROIDX_UI_AUTOMATOR_VERSION"].toString()
+ val googleMaterialVersion = extra["GOOGLE_MATERIAL_VERSION"].toString()
+ val jacocoVersion = extra["JACOCO_VERSION"].toString()
+ val javaVersion = extra["ANDROID_JVM_TARGET"].toString()
+ val kotlinVersion = extra["KOTLIN_VERSION"].toString()
+ val kotlinxCoroutinesVersion = extra["KOTLINX_COROUTINES_VERSION"].toString()
+ val zcashSdkVersion = extra["ZCASH_SDK_VERSION"].toString()
+
+ // Standalone versions
+ version("compose", androidxComposeVersion)
+ version("jacoco", jacocoVersion)
+ version("java", javaVersion)
+
+ // Aliases
+ alias("androidx-activity").to("androidx.activity:activity-ktx:${androidxActivityVersion}")
+ alias("androidx-activity-compose").to("androidx.activity:activity-compose:$androidxActivityVersion")
+ alias("androidx-appcompat").to("androidx.appcompat:appcompat:$androidxAppcompatVersion")
+ alias("androidx-annotation").to("androidx.annotation:annotation:${androidxAnnotationVersion}")
+ alias("androidx-compose-foundation").to("androidx.compose.foundation:foundation:$androidxComposeVersion")
+ alias("androidx-compose-material").to("androidx.compose.material:material:$androidxComposeVersion")
+ alias("androidx-compose-material-icons-core").to("androidx.compose.material:material-icons-core:$androidxComposeVersion")
+ alias("androidx-compose-tooling").to("androidx.compose.ui:ui-tooling-preview:$androidxComposeVersion")
+ alias("androidx-compose-ui").to("androidx.compose.ui:ui:$androidxComposeVersion")
+ alias("androidx-core").to("androidx.core:core-ktx:${androidxCoreVersion}")
+ alias("androidx-viewmodel-compose").to("androidx.activity:activity-compose:$androidxActivityVersion")
+ alias("google-material").to("com.google.android.material:material:${googleMaterialVersion}")
+ alias("kotlin").to("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
+ alias("kotlinx-coroutines-android").to("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion")
+ alias("kotlinx-coroutines-core").to("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion")
+ alias("zcash").to("cash.z.ecc.android:zcash-android-sdk:${zcashSdkVersion}")
+
+ // Test libraries
+ alias("androidx-espresso-contrib").to("androidx.test.espresso:espresso-contrib:${androidxEspressoVersion}")
+ alias("androidx-espresso-core").to("androidx.test.espresso:espresso-core:${androidxEspressoVersion}")
+ alias("androidx-espresso-intents").to("androidx.test.espresso:espresso-intents:${androidxEspressoVersion}")
+ alias("androidx-junit").to("androidx.test.ext:junit:${androidxTestJunitVersion}")
+ alias("androidx-testOrchestrator").to("androidx.test:orchestrator:${androidxTestOrchestratorVersion}")
+ alias("androidx-uiAutomator").to("androidx.test.uiautomator:uiautomator-v18:${androidxUiAutomatorVersion}")
+ alias("kotlinx-coroutines-test").to("org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinxCoroutinesVersion")
+
+ // Bundles
+ bundle(
+ "androidx-compose",
+ listOf(
+ "androidx-activity-compose",
+ "androidx-compose-foundation",
+ "androidx-compose-material",
+ "androidx-compose-material-icons-core",
+ "androidx-compose-tooling",
+ "androidx-compose-ui",
+ "androidx-viewmodel-compose"
+ )
+ )
+ bundle(
+ "androidx-test",
+ listOf(
+ "androidx-espresso-core",
+ "androidx-espresso-intents",
+ "androidx-espresso-contrib",
+ "androidx-junit"
+ )
+ )
+ }
+ }
+}
+
+rootProject.name = "zcash-android-app"
+
+includeBuild("build-conventions")
+
+include("app")
diff --git a/tools/detekt.yml b/tools/detekt.yml
new file mode 100644
index 00000000..900f85cf
--- /dev/null
+++ b/tools/detekt.yml
@@ -0,0 +1,797 @@
+build:
+ maxIssues: 0
+ excludeCorrectable: false
+ weights:
+ # complexity: 2
+ # LongParameterList: 1
+ # style: 1
+ # comments: 1
+
+config:
+ validation: true
+ warningsAsErrors: false
+ # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
+ excludes: ''
+
+processors:
+ active: true
+ exclude:
+ - 'DetektProgressListener'
+ # - 'KtFileCountProcessor'
+ # - 'PackageCountProcessor'
+ # - 'ClassCountProcessor'
+ # - 'FunctionCountProcessor'
+ # - 'PropertyCountProcessor'
+ # - 'ProjectComplexityProcessor'
+ # - 'ProjectCognitiveComplexityProcessor'
+ # - 'ProjectLLOCProcessor'
+ # - 'ProjectCLOCProcessor'
+ # - 'ProjectLOCProcessor'
+ # - 'ProjectSLOCProcessor'
+ # - 'LicenseHeaderLoaderExtension'
+
+console-reports:
+ active: true
+ exclude:
+ - 'ProjectStatisticsReport'
+ - 'ComplexityReport'
+ - 'NotificationReport'
+ # - 'FindingsReport'
+ - 'FileBasedFindingsReport'
+
+output-reports:
+ active: true
+ exclude:
+ # - 'TxtOutputReport'
+ # - 'XmlOutputReport'
+ # - 'HtmlOutputReport'
+
+comments:
+ active: true
+ AbsentOrWrongFileLicense:
+ active: false
+ licenseTemplateFile: 'license.template'
+ licenseTemplateIsRegex: false
+ CommentOverPrivateFunction:
+ active: false
+ CommentOverPrivateProperty:
+ active: false
+ DeprecatedBlockTag:
+ active: false
+ EndOfSentenceFormat:
+ active: false
+ endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
+ UndocumentedPublicClass:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ searchInNestedClass: true
+ searchInInnerClass: true
+ searchInInnerObject: true
+ searchInInnerInterface: true
+ UndocumentedPublicFunction:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ UndocumentedPublicProperty:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+
+complexity:
+ active: true
+ ComplexCondition:
+ active: true
+ threshold: 4
+ ComplexInterface:
+ active: false
+ threshold: 10
+ includeStaticDeclarations: false
+ includePrivateDeclarations: false
+ ComplexMethod:
+ active: true
+ threshold: 15
+ ignoreSingleWhenExpression: false
+ ignoreSimpleWhenEntries: false
+ ignoreNestingFunctions: false
+ nestingFunctions:
+ - 'also'
+ - 'apply'
+ - 'forEach'
+ - 'isNotNull'
+ - 'ifNull'
+ - 'let'
+ - 'run'
+ - 'use'
+ - 'with'
+ LabeledExpression:
+ active: false
+ ignoredLabels: []
+ LargeClass:
+ active: true
+ threshold: 600
+ LongMethod:
+ active: true
+ threshold: 60
+ ignoreAnnotated: []
+ LongParameterList:
+ active: true
+ functionThreshold: 6
+ constructorThreshold: 7
+ ignoreDefaultParameters: false
+ ignoreDataClasses: true
+ ignoreAnnotated: []
+ MethodOverloading:
+ active: false
+ threshold: 6
+ NamedArguments:
+ active: false
+ threshold: 3
+ NestedBlockDepth:
+ active: true
+ threshold: 4
+ ReplaceSafeCallChainWithRun:
+ active: false
+ StringLiteralDuplication:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ threshold: 3
+ ignoreAnnotation: true
+ excludeStringsWithLessThan5Characters: true
+ ignoreStringsRegex: '$^'
+ TooManyFunctions:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ thresholdInFiles: 11
+ thresholdInClasses: 11
+ thresholdInInterfaces: 11
+ thresholdInObjects: 11
+ thresholdInEnums: 11
+ ignoreDeprecated: false
+ ignorePrivate: false
+ ignoreOverridden: false
+
+coroutines:
+ active: true
+ GlobalCoroutineUsage:
+ active: false
+ RedundantSuspendModifier:
+ active: false
+ SleepInsteadOfDelay:
+ active: false
+ SuspendFunWithFlowReturnType:
+ active: false
+
+empty-blocks:
+ active: true
+ EmptyCatchBlock:
+ active: true
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ EmptyClassBlock:
+ active: true
+ EmptyDefaultConstructor:
+ active: true
+ EmptyDoWhileBlock:
+ active: true
+ EmptyElseBlock:
+ active: true
+ EmptyFinallyBlock:
+ active: true
+ EmptyForBlock:
+ active: true
+ EmptyFunctionBlock:
+ active: true
+ ignoreOverridden: false
+ EmptyIfBlock:
+ active: true
+ EmptyInitBlock:
+ active: true
+ EmptyKtFile:
+ active: true
+ EmptySecondaryConstructor:
+ active: true
+ EmptyTryBlock:
+ active: true
+ EmptyWhenBlock:
+ active: true
+ EmptyWhileBlock:
+ active: true
+
+exceptions:
+ active: true
+ ExceptionRaisedInUnexpectedLocation:
+ active: true
+ methodNames:
+ - 'equals'
+ - 'finalize'
+ - 'hashCode'
+ - 'toString'
+ InstanceOfCheckForException:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ NotImplementedDeclaration:
+ active: false
+ ObjectExtendsThrowable:
+ active: false
+ PrintStackTrace:
+ active: true
+ RethrowCaughtException:
+ active: true
+ ReturnFromFinally:
+ active: true
+ ignoreLabeled: false
+ SwallowedException:
+ active: true
+ ignoredExceptionTypes:
+ - 'InterruptedException'
+ - 'MalformedURLException'
+ - 'NumberFormatException'
+ - 'ParseException'
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ ThrowingExceptionFromFinally:
+ active: true
+ ThrowingExceptionInMain:
+ active: false
+ ThrowingExceptionsWithoutMessageOrCause:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptions:
+ - 'ArrayIndexOutOfBoundsException'
+ - 'Exception'
+ - 'IllegalArgumentException'
+ - 'IllegalMonitorStateException'
+ - 'IllegalStateException'
+ - 'IndexOutOfBoundsException'
+ - 'NullPointerException'
+ - 'RuntimeException'
+ - 'Throwable'
+ ThrowingNewInstanceOfSameException:
+ active: true
+ TooGenericExceptionCaught:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ exceptionNames:
+ - 'ArrayIndexOutOfBoundsException'
+ - 'Error'
+ - 'Exception'
+ - 'IllegalMonitorStateException'
+ - 'IndexOutOfBoundsException'
+ - 'NullPointerException'
+ - 'RuntimeException'
+ - 'Throwable'
+ allowedExceptionNameRegex: '_|(ignore|expected).*'
+ TooGenericExceptionThrown:
+ active: true
+ exceptionNames:
+ - 'Error'
+ - 'Exception'
+ - 'RuntimeException'
+ - 'Throwable'
+
+formatting:
+ active: true
+ android: false
+ autoCorrect: true
+ AnnotationOnSeparateLine:
+ active: false
+ autoCorrect: true
+ AnnotationSpacing:
+ active: false
+ autoCorrect: true
+ ArgumentListWrapping:
+ active: false
+ autoCorrect: true
+ indentSize: 4
+ maxLineLength: 120
+ ChainWrapping:
+ active: true
+ autoCorrect: true
+ CommentSpacing:
+ active: true
+ autoCorrect: true
+ EnumEntryNameCase:
+ active: false
+ autoCorrect: true
+ Filename:
+ active: true
+ FinalNewline:
+ active: true
+ autoCorrect: true
+ insertFinalNewLine: true
+ ImportOrdering:
+ active: false
+ autoCorrect: true
+ layout: '*,java.**,javax.**,kotlin.**,^'
+ Indentation:
+ active: false
+ autoCorrect: true
+ indentSize: 4
+ continuationIndentSize: 4
+ MaximumLineLength:
+ active: true
+ maxLineLength: 120
+ ignoreBackTickedIdentifier: false
+ ModifierOrdering:
+ active: true
+ autoCorrect: true
+ MultiLineIfElse:
+ active: true
+ autoCorrect: true
+ NoBlankLineBeforeRbrace:
+ active: true
+ autoCorrect: true
+ NoConsecutiveBlankLines:
+ active: true
+ autoCorrect: true
+ NoEmptyClassBody:
+ active: true
+ autoCorrect: true
+ NoEmptyFirstLineInMethodBlock:
+ active: false
+ autoCorrect: true
+ NoLineBreakAfterElse:
+ active: true
+ autoCorrect: true
+ NoLineBreakBeforeAssignment:
+ active: true
+ autoCorrect: true
+ NoMultipleSpaces:
+ active: true
+ autoCorrect: true
+ NoSemicolons:
+ active: true
+ autoCorrect: true
+ NoTrailingSpaces:
+ active: true
+ autoCorrect: true
+ NoUnitReturn:
+ active: true
+ autoCorrect: true
+ NoUnusedImports:
+ active: true
+ autoCorrect: true
+ NoWildcardImports:
+ active: true
+ PackageName:
+ active: true
+ autoCorrect: true
+ ParameterListWrapping:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ maxLineLength: 120
+ SpacingAroundAngleBrackets:
+ active: false
+ autoCorrect: true
+ SpacingAroundColon:
+ active: true
+ autoCorrect: true
+ SpacingAroundComma:
+ active: true
+ autoCorrect: true
+ SpacingAroundCurly:
+ active: true
+ autoCorrect: true
+ SpacingAroundDot:
+ active: true
+ autoCorrect: true
+ SpacingAroundDoubleColon:
+ active: false
+ autoCorrect: true
+ SpacingAroundKeyword:
+ active: true
+ autoCorrect: true
+ SpacingAroundOperators:
+ active: true
+ autoCorrect: true
+ SpacingAroundParens:
+ active: true
+ autoCorrect: true
+ SpacingAroundRangeOperator:
+ active: true
+ autoCorrect: true
+ SpacingAroundUnaryOperator:
+ active: false
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithAnnotations:
+ active: false
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithComments:
+ active: false
+ autoCorrect: true
+ StringTemplate:
+ active: true
+ autoCorrect: true
+
+naming:
+ active: true
+ BooleanPropertyNaming:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ allowedPattern: '^(is|has|are)'
+ ClassNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ classPattern: '[A-Z][a-zA-Z0-9]*'
+ ConstructorParameterNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ EnumNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ enumEntryPattern: '[A-Z][_a-zA-Z0-9]*'
+ ForbiddenClassName:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ forbiddenName: []
+ FunctionMaxLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ maximumFunctionNameLength: 30
+ FunctionMinLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ minimumFunctionNameLength: 3
+ FunctionNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ ignoreAnnotated:
+ - 'Composable'
+ FunctionParameterNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ InvalidPackageDeclaration:
+ active: false
+ excludes: ['**/*.kts']
+ rootPackage: ''
+ MatchingDeclarationName:
+ active: true
+ mustBeFirst: true
+ MemberNameEqualsClassName:
+ active: true
+ ignoreOverridden: true
+ NoNameShadowing:
+ active: false
+ NonBooleanPropertyPrefixedWithIs:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ ObjectPropertyNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*'
+ TopLevelPropertyNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
+ VariableMaxLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ maximumVariableNameLength: 64
+ VariableMinLength:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ minimumVariableNameLength: 1
+ VariableNaming:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+performance:
+ active: true
+ ArrayPrimitive:
+ active: true
+ ForEachOnRange:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ SpreadOperator:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ UnnecessaryTemporaryInstantiation:
+ active: true
+
+potential-bugs:
+ active: true
+ AvoidReferentialEquality:
+ active: false
+ forbiddenTypePatterns:
+ - 'kotlin.String'
+ CastToNullableType:
+ active: false
+ Deprecation:
+ active: false
+ DontDowncastCollectionTypes:
+ active: false
+ DoubleMutabilityForCollection:
+ active: false
+ DuplicateCaseInWhenExpression:
+ active: true
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: true
+ EqualsWithHashCodeExist:
+ active: true
+ ExitOutsideMain:
+ active: false
+ ExplicitGarbageCollectionCall:
+ active: true
+ HasPlatformType:
+ active: false
+ IgnoredReturnValue:
+ active: false
+ restrictToAnnotatedMethods: true
+ returnValueAnnotations:
+ - '*.CheckResult'
+ - '*.CheckReturnValue'
+ ignoreReturnValueAnnotations:
+ - '*.CanIgnoreReturnValue'
+ ImplicitDefaultLocale:
+ active: true
+ ImplicitUnitReturnType:
+ active: false
+ allowExplicitReturnType: true
+ InvalidRange:
+ active: true
+ IteratorHasNextCallsNextMethod:
+ active: true
+ IteratorNotThrowingNoSuchElementException:
+ active: true
+ LateinitUsage:
+ active: false
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ excludeAnnotatedProperties: []
+ ignoreOnClassesPattern: ''
+ MapGetWithNotNullAssertionOperator:
+ active: false
+ MissingWhenCase:
+ active: true
+ allowElseExpression: true
+ NullableToStringCall:
+ active: false
+ RedundantElseInWhen:
+ active: true
+ UnconditionalJumpStatementInLoop:
+ active: false
+ UnnecessaryNotNullOperator:
+ active: true
+ UnnecessarySafeCall:
+ active: true
+ UnreachableCatchBlock:
+ active: false
+ UnreachableCode:
+ active: true
+ UnsafeCallOnNullableType:
+ active: true
+ UnsafeCast:
+ active: true
+ UnusedUnaryOperator:
+ active: false
+ UselessPostfixExpression:
+ active: false
+ WrongEqualsTypeParameter:
+ active: true
+
+style:
+ active: true
+ ClassOrdering:
+ active: false
+ CollapsibleIfStatements:
+ active: false
+ DataClassContainsFunctions:
+ active: false
+ conversionFunctionPrefix: 'to'
+ DataClassShouldBeImmutable:
+ active: false
+ DestructuringDeclarationWithTooManyEntries:
+ active: false
+ maxDestructuringEntries: 3
+ EqualsNullCall:
+ active: true
+ EqualsOnSignatureLine:
+ active: false
+ ExplicitCollectionElementAccessMethod:
+ active: false
+ ExplicitItLambdaParameter:
+ active: false
+ ExpressionBodySyntax:
+ active: false
+ includeLineWrapping: false
+ ForbiddenComment:
+ active: true
+ values:
+ - 'FIXME:'
+ - 'STOPSHIP:'
+ - 'TODO:'
+ allowedPatterns: '\[[^\]]\d+\]'
+ ForbiddenImport:
+ active: false
+ imports: []
+ forbiddenPatterns: ''
+ ForbiddenMethodCall:
+ active: false
+ methods:
+ - 'kotlin.io.print'
+ - 'kotlin.io.println'
+ ForbiddenPublicDataClass:
+ active: true
+ excludes: ['**']
+ ignorePackages:
+ - '*.internal'
+ - '*.internal.*'
+ ForbiddenVoid:
+ active: false
+ ignoreOverridden: false
+ ignoreUsageInGenerics: false
+ FunctionOnlyReturningConstant:
+ active: true
+ ignoreOverridableFunction: true
+ ignoreActualFunction: true
+ excludedFunctions: ''
+ excludeAnnotatedFunction:
+ - 'dagger.Provides'
+ LibraryCodeMustSpecifyReturnType:
+ active: true
+ excludes: ['**']
+ LibraryEntitiesShouldNotBePublic:
+ active: true
+ excludes: ['**']
+ LoopWithTooManyJumpStatements:
+ active: true
+ maxJumpCount: 1
+ MagicNumber:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ ignoreNumbers:
+ - '-1'
+ - '0'
+ - '1'
+ - '2'
+ ignoreHashCodeFunction: true
+ ignorePropertyDeclaration: false
+ ignoreLocalVariableDeclaration: false
+ ignoreConstantDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotation: false
+ ignoreNamedArgument: true
+ ignoreEnums: false
+ ignoreRanges: false
+ ignoreExtensionFunctions: true
+ MandatoryBracesIfStatements:
+ active: false
+ MandatoryBracesLoops:
+ active: false
+ MaxLineLength:
+ active: true
+ maxLineLength: 120
+ excludePackageStatements: true
+ excludeImportStatements: true
+ excludeCommentStatements: false
+ MayBeConst:
+ active: true
+ ModifierOrder:
+ active: true
+ MultilineLambdaItParameter:
+ active: false
+ NestedClassesVisibility:
+ active: true
+ NewLineAtEndOfFile:
+ active: true
+ NoTabs:
+ active: false
+ ObjectLiteralToLambda:
+ active: false
+ OptionalAbstractKeyword:
+ active: true
+ OptionalUnit:
+ active: false
+ OptionalWhenBraces:
+ active: false
+ PreferToOverPairSyntax:
+ active: false
+ ProtectedMemberInFinalClass:
+ active: true
+ RedundantExplicitType:
+ active: false
+ RedundantHigherOrderMapUsage:
+ active: false
+ RedundantVisibilityModifierRule:
+ active: false
+ ReturnCount:
+ active: true
+ max: 2
+ excludedFunctions: 'equals'
+ excludeLabeled: false
+ excludeReturnFromLambda: true
+ excludeGuardClauses: false
+ SafeCast:
+ active: true
+ SerialVersionUIDInSerializableClass:
+ active: true
+ SpacingBetweenPackageAndImports:
+ active: false
+ ThrowsCount:
+ active: true
+ max: 2
+ excludeGuardClauses: false
+ TrailingWhitespace:
+ active: false
+ UnderscoresInNumericLiterals:
+ active: false
+ acceptableDecimalLength: 5
+ UnnecessaryAbstractClass:
+ active: true
+ excludeAnnotatedClasses:
+ - 'dagger.Module'
+ UnnecessaryAnnotationUseSiteTarget:
+ active: false
+ UnnecessaryApply:
+ active: true
+ UnnecessaryFilter:
+ active: false
+ UnnecessaryInheritance:
+ active: true
+ UnnecessaryLet:
+ active: false
+ UnnecessaryParentheses:
+ active: false
+ UntilInsteadOfRangeTo:
+ active: false
+ UnusedImports:
+ active: false
+ UnusedPrivateClass:
+ active: true
+ UnusedPrivateMember:
+ active: true
+ allowedNames: '(_|ignored|expected|serialVersionUID)'
+ UseArrayLiteralsInAnnotations:
+ active: false
+ UseCheckNotNull:
+ active: false
+ UseCheckOrError:
+ active: false
+ UseDataClass:
+ active: false
+ excludeAnnotatedClasses: []
+ allowVars: false
+ UseEmptyCounterpart:
+ active: false
+ UseIfEmptyOrIfBlank:
+ active: false
+ UseIfInsteadOfWhen:
+ active: false
+ UseIsNullOrEmpty:
+ active: false
+ UseOrEmpty:
+ active: false
+ UseRequire:
+ active: false
+ UseRequireNotNull:
+ active: false
+ UselessCallOnNotNull:
+ active: true
+ UtilityClassWithPublicConstructor:
+ active: true
+ VarCouldBeVal:
+ active: true
+ WildcardImport:
+ active: true
+ excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**']
+ excludeImports:
+ - 'java.util.*'
+ - 'kotlinx.android.synthetic.*'