Merge pull request #154 from zcash/task/clean-up-dependencies

Task/clean up dependencies
This commit is contained in:
Kevin Gorham 2020-06-10 10:26:14 -04:00 committed by GitHub
commit 41422992ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 479 additions and 300 deletions

View File

@ -5,15 +5,15 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric'
apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'com.google.firebase.crashlytics'
//apply plugin: 'com.github.ben-manes.versions'
archivesBaseName = 'zcash-android-wallet'
group = 'cash.z.ecc.android'
version = '1.0.0-alpha25'
version = '1.0.0-alpha28'
android {
ndkVersion "21.1.6352462"
compileSdkVersion Deps.compileSdkVersion
buildToolsVersion Deps.buildToolsVersion
viewBinding.enabled = true
@ -27,7 +27,6 @@ android {
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
testInstrumentationRunnerArguments clearPackageData: 'true'
multiDexEnabled true
// manifestPlaceholders = [rollbarToken: properties["rollbarToken"]]
}
flavorDimensions 'network'
productFlavors {
@ -102,40 +101,46 @@ android {
}
}
crashlytics {
enableNdk true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':qrecycler')
implementation project(':feedback')
implementation project(':mnemonic')
implementation project(':lockbox')
implementation project(':sdk')
implementation project(':chipsinputlayout')
// Zcash
implementation 'com.github.zcash:zcash-android-wallet-plugins:1.0.0'
implementation Deps.Zcash.ANDROID_WALLET_PLUGINS
zcashtestnetImplementation Deps.Zcash.Sdk.TESTNET
zcashmainnetImplementation Deps.Zcash.Sdk.MAINNET
// Kotlin
implementation Deps.Kotlin.STDLIB
// Android
implementation Deps.AndroidX.ANNOTATION
implementation Deps.AndroidX.APPCOMPAT
implementation Deps.AndroidX.CORE_KTX
implementation Deps.AndroidX.CONSTRAINT_LAYOUT
implementation Deps.AndroidX.Lifecycle.LIFECYCLE_RUNTIME_KTX
implementation Deps.AndroidX.CORE_KTX
implementation Deps.AndroidX.FRAGMENT_KTX
implementation Deps.AndroidX.LEGACY
implementation Deps.AndroidX.PAGING
implementation Deps.AndroidX.CameraX.CAMERA2
implementation Deps.AndroidX.CameraX.CORE
implementation Deps.AndroidX.CameraX.LIFECYCLE
implementation Deps.AndroidX.CameraX.View.EXT
implementation Deps.AndroidX.CameraX.View.VIEW
implementation Deps.AndroidX.Lifecycle.LIFECYCLE_EXTENSIONS
implementation Deps.AndroidX.Lifecycle.LIFECYCLE_RUNTIME_KTX
implementation Deps.AndroidX.Navigation.FRAGMENT_KTX
implementation Deps.AndroidX.Navigation.UI_KTX
implementation "androidx.room:room-ktx:2.2.3"
implementation "androidx.paging:paging-runtime-ktx:2.1.1"
implementation 'com.google.guava:guava:27.0.1-android'
kapt "androidx.room:room-compiler:2.2.3"
implementation Deps.AndroidX.Room.ROOM_KTX
kapt Deps.AndroidX.Room.ROOM_COMPILER
// Google
implementation Deps.Google.GUAVA
implementation Deps.Google.MATERIAL
implementation Deps.Google.ML_VISION // QR Scanner
// Dagger
implementation Deps.Dagger.ANDROID_SUPPORT
@ -143,44 +148,23 @@ dependencies {
kapt Deps.Dagger.COMPILER
// grpc-java
implementation "io.grpc:grpc-okhttp:1.25.0"
implementation "io.grpc:grpc-android:1.25.0"
implementation "io.grpc:grpc-protobuf-lite:1.25.0"
implementation "io.grpc:grpc-stub:1.25.0"
implementation 'javax.annotation:javax.annotation-api:1.3.2'
// solves error: Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules jetified-guava-26.0-android.jar (com.google.guava:guava:26.0-android) and listenablefuture-1.0.jar (com.google.guava:listenablefuture:1.0)
// per this recommendation from Chris Povirk, given guava's decision to split ListenableFuture away from Guava: https://groups.google.com/d/msg/guava-discuss/GghaKwusjcY/bCIAKfzOEwAJ
implementation 'com.google.guava:guava:27.0.1-android'
implementation Deps.Grpc.ANDROID
implementation Deps.Grpc.OKHTTP
implementation Deps.Grpc.PROTOBUG
implementation Deps.Grpc.STUB
implementation Deps.JavaX.JAVA_ANNOTATION
// Analytics
implementation 'com.mixpanel.android:mixpanel-android:5.6.3'
implementation 'com.google.firebase:firebase-analytics:17.2.2'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.crashlytics.sdk.android:crashlytics-ndk:2.1.1'
implementation 'com.google.firebase:firebase-perf:19.0.5'
// QR Scanning
implementation 'com.google.firebase:firebase-ml-vision:24.0.1'
implementation 'androidx.camera:camera-core:1.0.0-alpha10'
implementation 'androidx.camera:camera-camera2:1.0.0-alpha10'
implementation "androidx.camera:camera-view:1.0.0-alpha07"
implementation "androidx.camera:camera-extensions:1.0.0-alpha07"
implementation "androidx.camera:camera-lifecycle:1.0.0-alpha10"
// Analytics (for dogfooding/crash-reporting/feedback only on internal team builds)
implementation Deps.Analytics.CRASHLYTICS
implementation Deps.Analytics.MIXPANEL
// Misc.
implementation 'com.airbnb.android:lottie:3.1.0'
implementation 'com.facebook.stetho:stetho:1.5.1'
// check for build errors at https://jitpack.io/com/github/gmale/chips-input-layout/<version>/build.log
// implementation 'com.github.gmale:chips-input-layout:4750760a7222bc04ca48266b387456d2b03541a7'
implementation 'androidx.annotation:annotation:1.1.0'
implementation Deps.Misc.LOTTIE
// Tests
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation Deps.Test.COROUTINES_TEST
testImplementation Deps.Test.JUNIT
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.3'
testImplementation Deps.Test.MOKITO
androidTestImplementation Deps.Test.Android.JUNIT
androidTestImplementation Deps.Test.Android.ESPRESSO
}

View File

@ -5,7 +5,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import cash.z.ecc.android.lockbox.LockBox
import cash.z.ecc.kotlin.mnemonic.Mnemonics
import cash.z.wallet.sdk.Initializer
import cash.z.ecc.android.sdk.Initializer
import okio.Buffer
import okio.GzipSink
import okio.Okio

View File

@ -33,6 +33,8 @@
<!-- Firebase options -->
<meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="barcode" />
<!-- All reporting is opt-in, only -->
<meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />
<!-- Mixpanel options -->
<meta-data android:name="com.mixpanel.android.MPConfig.AutoShowMixpanelUpdates" android:value="false" />

View File

@ -8,10 +8,10 @@ import androidx.camera.core.CameraXConfig
import cash.z.ecc.android.di.component.AppComponent
import cash.z.ecc.android.di.component.DaggerAppComponent
import cash.z.ecc.android.feedback.FeedbackCoordinator
import cash.z.wallet.sdk.ext.SilentTwig
import cash.z.wallet.sdk.ext.TroubleshootingTwig
import cash.z.wallet.sdk.ext.Twig
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.SilentTwig
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import cash.z.ecc.android.sdk.ext.Twig
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.*
import javax.inject.Inject
@ -37,7 +37,6 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
override fun onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
Twig.plant(TroubleshootingTwig())
creationTime = System.currentTimeMillis()
instance = this
// Setup handler for uncaught exceptions.

View File

@ -4,7 +4,7 @@ import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.di.annotation.ActivityScope
import cash.z.ecc.android.di.annotation.SynchronizerScope
import cash.z.ecc.android.di.module.InitializerModule
import cash.z.wallet.sdk.Initializer
import cash.z.ecc.android.sdk.Initializer
import dagger.BindsInstance
import dagger.Subcomponent

View File

@ -3,8 +3,8 @@ package cash.z.ecc.android.di.component
import androidx.lifecycle.ViewModelProvider
import cash.z.ecc.android.di.annotation.SynchronizerScope
import cash.z.ecc.android.di.module.SynchronizerModule
import cash.z.wallet.sdk.Initializer
import cash.z.wallet.sdk.Synchronizer
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.Synchronizer
import dagger.BindsInstance
import dagger.Subcomponent
import javax.inject.Named

View File

@ -2,9 +2,14 @@ package cash.z.ecc.android.di.module
import android.content.ClipboardManager
import android.content.Context
import android.content.SharedPreferences
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.di.component.MainActivitySubcomponent
import cash.z.ecc.android.feedback.*
import cash.z.ecc.android.sdk.ext.SilentTwig
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import cash.z.ecc.android.sdk.ext.Twig
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
@ -27,6 +32,11 @@ class AppModule {
// Feedback
//
@Provides
@Singleton
fun providePreferences(context: Context): SharedPreferences
= context.getSharedPreferences("Application", Context.MODE_PRIVATE)
@Provides
@Singleton
fun provideFeedback(): Feedback = Feedback()
@ -35,8 +45,16 @@ class AppModule {
@Singleton
fun provideFeedbackCoordinator(
feedback: Feedback,
preferences: SharedPreferences,
defaultObservers: Set<@JvmSuppressWildcards FeedbackCoordinator.FeedbackObserver>
): FeedbackCoordinator = FeedbackCoordinator(feedback, defaultObservers)
): FeedbackCoordinator {
return preferences.getBoolean(FeedbackCoordinator.ENABLED, false).let { isEnabled ->
// observe nothing unless feedback is enabled
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isEnabled)
Twig.plant(if (isEnabled) TroubleshootingTwig() else SilentTwig())
FeedbackCoordinator(feedback, if (isEnabled) defaultObservers else setOf())
}
}
//

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.di.module
import android.content.Context
import cash.z.wallet.sdk.Initializer
import cash.z.ecc.android.sdk.Initializer
import dagger.Module
import dagger.Provides
import dagger.Reusable

View File

@ -2,8 +2,8 @@ package cash.z.ecc.android.di.module
import android.content.Context
import cash.z.ecc.android.di.annotation.SynchronizerScope
import cash.z.wallet.sdk.Initializer
import cash.z.wallet.sdk.Synchronizer
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.Synchronizer
import dagger.Module
import dagger.Provides

View File

@ -0,0 +1,97 @@
package cash.z.ecc.android.ext
import android.app.ActivityManager
import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.provider.Settings
import androidx.core.content.getSystemService
import com.google.android.material.dialog.MaterialAlertDialogBuilder
fun Context.showClearDataConfirmation(onDismiss: () -> Unit = {}, onCancel: () -> Unit = {}): Dialog {
return MaterialAlertDialogBuilder(this)
.setTitle("Nuke Wallet?")
.setMessage("WARNING: Potential Loss of Funds\n\nClearing all wallet data and can result in a loss of funds, if you cannot locate your correct seed phrase.\n\nPlease confirm that you have your 24-word seed phrase available before proceeding.")
.setCancelable(false)
.setPositiveButton("Cancel") { dialog, _ ->
dialog.dismiss()
onDismiss()
onCancel()
}
.setNegativeButton("Erase Wallet") { dialog, _ ->
dialog.dismiss()
onDismiss()
getSystemService<ActivityManager>()?.clearApplicationUserData()
}
.show()
}
fun Context.showUninitializedError(error: Throwable? = null, onDismiss: () -> Unit = {}): Dialog {
return MaterialAlertDialogBuilder(this)
.setTitle("Wallet Improperly Initialized")
.setMessage("This wallet has not been initialized correctly! Perhaps an error occurred during install.\n\nThis can be fixed with a reset. First, locate your backup seed phrase, then CLEAR DATA and reimport it.")
.setCancelable(false)
.setPositiveButton("Exit") { dialog, _ ->
dialog.dismiss()
onDismiss()
if (error != null) throw error
}
.setNegativeButton("Clear Data") { dialog, _ ->
showClearDataConfirmation(onDismiss, onCancel = {
// do not let the user back into the app because we cannot recover from this case
showUninitializedError(error, onDismiss)
})
}
.show()
}
fun Context.showInvalidSeedPhraseError(error: Throwable? = null, onDismiss: () -> Unit = {}): Dialog {
return MaterialAlertDialogBuilder(this)
.setTitle("Oops! Invalid Seed Phrase")
.setMessage("That seed phrase appears to be invalid! Please double-check it and try again.\n\n${error?.message ?: ""}")
.setCancelable(false)
.setPositiveButton("Retry") { dialog, _ ->
dialog.dismiss()
onDismiss()
}
.show()
}
fun Context.showScanFailure(error: Throwable?, onCancel: () -> Unit = {}, onDismiss: () -> Unit = {}): Dialog {
val message = if (error == null) {
"Unknown error"
} else {
"${error.message}${if (error.cause != null) "\n\nCaused by: ${error.cause}" else ""}"
}
return MaterialAlertDialogBuilder(this)
.setTitle("Scan Failure")
.setMessage(message)
.setCancelable(true)
.setPositiveButton("Retry") { d, _ ->
d.dismiss()
onDismiss()
}
.setNegativeButton("Ignore") { d, _ ->
d.dismiss()
onCancel()
onDismiss()
}
.show()
}
fun Context.showCriticalProcessorError(error: Throwable?, onRetry: () -> Unit = {}): Dialog {
return MaterialAlertDialogBuilder(this)
.setTitle("Processor Error")
.setMessage(error?.message ?: "Critical error while processing blocks!")
.setCancelable(false)
.setPositiveButton("Retry") { d, _ ->
d.dismiss()
onRetry()
}
.setNegativeButton("Exit") { dialog, _ ->
dialog.dismiss()
throw error ?: RuntimeException("Critical error while processing blocks and the user chose to exit.")
}
.show()
}

View File

@ -3,9 +3,9 @@ package cash.z.ecc.android.ext
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.widget.EditText
import android.widget.TextView
import cash.z.wallet.sdk.ext.convertZecToZatoshi
import cash.z.wallet.sdk.ext.safelyConvertToBigDecimal
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.convertZecToZatoshi
import cash.z.ecc.android.sdk.ext.safelyConvertToBigDecimal
import cash.z.ecc.android.sdk.ext.twig
fun EditText.onEditorActionDone(block: (EditText) -> Unit) {
this.setOnEditorActionListener { _, actionId, _ ->

View File

@ -1,6 +1,6 @@
package cash.z.ecc.android.feedback
import com.crashlytics.android.Crashlytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
class FeedbackCrashlytics : FeedbackCoordinator.FeedbackObserver {
/**
@ -18,7 +18,7 @@ class FeedbackCrashlytics : FeedbackCoordinator.FeedbackObserver {
)
else -> null
}
exception?.let { Crashlytics.logException(it) }
exception?.let { FirebaseCrashlytics.getInstance().recordException(it) }
}
private class ReorgException(errorHeight: Int, rewindHeight: Int, reorgMesssage: String) :

View File

@ -63,6 +63,7 @@ object Report {
val errorHeight: Int by propertyMap
val rewindHeight: Int by propertyMap
}
class TxUpdateFailed(t: Throwable) : Feedback.AppError("txupdate", t, false)
}
}

View File

@ -38,10 +38,10 @@ import cash.z.ecc.android.feedback.Report.Error.NonFatal.Reorg
import cash.z.ecc.android.feedback.Report.NonUserAction.FEEDBACK_STOPPED
import cash.z.ecc.android.feedback.Report.NonUserAction.SYNC_START
import cash.z.ecc.android.feedback.Report.Tap.COPY_ADDRESS
import cash.z.wallet.sdk.Initializer
import cash.z.wallet.sdk.exception.CompactBlockProcessorException
import cash.z.wallet.sdk.ext.ZcashSdk
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.twig
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.launch
@ -224,6 +224,13 @@ class MainActivity : AppCompatActivity() {
}
}
suspend fun isValidAddress(address: String): Boolean {
try {
return !synchronizerComponent.synchronizer().validateAddress(address).isNotValid
} catch (t: Throwable) { }
return false
}
fun copyText(textToCopy: String, label: String = "zECC Wallet Text") {
clipboard.setPrimaryClip(
ClipData.newPlainText(label, textToCopy)

View File

@ -5,7 +5,7 @@ import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import cash.z.ecc.android.R
import cash.z.wallet.sdk.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
class TransactionAdapter<T : ConfirmedTransaction> :
PagedListAdapter<T, TransactionViewHolder<T>>(

View File

@ -3,6 +3,7 @@ package cash.z.ecc.android.ui.detail
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import cash.z.ecc.android.R
import cash.z.ecc.android.ext.goneIf
@ -11,9 +12,10 @@ import cash.z.ecc.android.ui.MainActivity
import cash.z.ecc.android.ui.send.SendViewModel
import cash.z.ecc.android.ui.util.INCLUDE_MEMO_PREFIX
import cash.z.ecc.android.ui.util.toUtf8Memo
import cash.z.wallet.sdk.entity.ConfirmedTransaction
import cash.z.wallet.sdk.ext.*
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.ext.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.*
@ -28,88 +30,89 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
private val addressRegex = """zs\d\w{65,}""".toRegex()
fun bindTo(transaction: T?) {
(itemView.context as MainActivity).lifecycleScope.launch {
// update view
var lineOne: String = ""
var lineTwo: String = ""
var amountZec: String = ""
var amountDisplay: String = ""
var amountColor: Int = R.color.text_light_dimmed
var lineOneColor: Int = R.color.text_light
var lineTwoColor: Int = R.color.text_light_dimmed
var indicatorBackground: Int = R.drawable.background_indicator_unknown
// update view
var lineOne: String = ""
var lineTwo: String = ""
var amountZec: String = ""
var amountDisplay: String = ""
var amountColor: Int = R.color.text_light_dimmed
var lineOneColor: Int = R.color.text_light
var lineTwoColor: Int = R.color.text_light_dimmed
var indicatorBackground: Int = R.drawable.background_indicator_unknown
transaction?.apply {
itemView.setOnClickListener {
onTransactionClicked(this)
}
itemView.setOnLongClickListener {
onTransactionLongPressed(this)
true
}
amountZec = value.convertZatoshiToZecString()
// TODO: these might be good extension functions
val timestamp = formatter.format(blockTimeInSeconds * 1000L)
val isMined = blockTimeInSeconds != 0L
when {
!toAddress.isNullOrEmpty() -> {
lineOne = "You paid ${toAddress?.toAbbreviatedAddress()}"
lineTwo = if (isMined) "Sent $timestamp" else "Pending confirmation"
amountDisplay = "- $amountZec"
if (isMined) {
amountColor = R.color.zcashRed
indicatorBackground = R.drawable.background_indicator_outbound
} else {
lineOneColor = R.color.text_light_dimmed
lineTwoColor = R.color.text_light
transaction?.apply {
itemView.setOnClickListener {
onTransactionClicked(this)
}
itemView.setOnLongClickListener {
onTransactionLongPressed(this)
true
}
amountZec = value.convertZatoshiToZecString()
// TODO: these might be good extension functions
val timestamp = formatter.format(blockTimeInSeconds * 1000L)
val isMined = blockTimeInSeconds != 0L
when {
!toAddress.isNullOrEmpty() -> {
lineOne = "You paid ${toAddress?.toAbbreviatedAddress()}"
lineTwo = if (isMined) "Sent $timestamp" else "Pending confirmation"
amountDisplay = "- $amountZec"
if (isMined) {
amountColor = R.color.zcashRed
indicatorBackground = R.drawable.background_indicator_outbound
} else {
lineOneColor = R.color.text_light_dimmed
lineTwoColor = R.color.text_light
}
}
toAddress.isNullOrEmpty() && value > 0L && minedHeight > 0 -> {
lineOne = getSender(transaction)
lineTwo = "Received $timestamp"
amountDisplay = "+ $amountZec"
amountColor = R.color.zcashGreen
indicatorBackground = R.drawable.background_indicator_inbound
}
else -> {
lineOne = "Unknown"
lineTwo = "Unknown"
amountDisplay = "$amountZec"
amountColor = R.color.text_light
}
}
toAddress.isNullOrEmpty() && value > 0L && minedHeight > 0 -> {
lineOne = getSender(transaction)
lineTwo = "Received $timestamp"
amountDisplay = "+ $amountZec"
amountColor = R.color.zcashGreen
indicatorBackground = R.drawable.background_indicator_inbound
}
else -> {
lineOne = "Unknown"
lineTwo = "Unknown"
amountDisplay = "$amountZec"
amountColor = R.color.text_light
// sanitize amount
if (value < ZcashSdk.MINERS_FEE_ZATOSHI) amountDisplay = "< 0.001"
else if (amountZec.length > 10) { // 10 allows 3 digits to the left and 6 to the right of the decimal
amountDisplay = "tap to view"
}
}
// sanitize amount
if (value < ZcashSdk.MINERS_FEE_ZATOSHI) amountDisplay = "< 0.001"
else if (amountZec.length > 10) { // 10 allows 3 digits to the left and 6 to the right of the decimal
amountDisplay = "tap to view"
}
topText.text = lineOne
bottomText.text = lineTwo
amountText.text = amountDisplay
amountText.setTextColor(amountColor.toAppColor())
topText.setTextColor(lineOneColor.toAppColor())
bottomText.setTextColor(lineTwoColor.toAppColor())
val context = itemView.context
indicator.background = context.resources.getDrawable(indicatorBackground)
shieldIcon.goneIf((transaction?.raw != null || transaction?.expiryHeight != null) && !transaction?.toAddress.isShielded())
}
topText.text = lineOne
bottomText.text = lineTwo
amountText.text = amountDisplay
amountText.setTextColor(amountColor.toAppColor())
topText.setTextColor(lineOneColor.toAppColor())
bottomText.setTextColor(lineTwoColor.toAppColor())
val context = itemView.context
indicator.background = context.resources.getDrawable(indicatorBackground)
shieldIcon.goneIf((transaction?.raw != null || transaction?.expiryHeight != null) && !transaction?.toAddress.isShielded())
}
private fun getSender(transaction: ConfirmedTransaction): String {
private suspend fun getSender(transaction: ConfirmedTransaction): String {
val memo = transaction.memo.toUtf8Memo()
return when {
memo.contains(INCLUDE_MEMO_PREFIX) -> {
val address = memo.split(INCLUDE_MEMO_PREFIX)[1].trim()
val address = memo.split(INCLUDE_MEMO_PREFIX)[1].trim().validateAddress() ?: "Unknown"
"${address.toAbbreviatedAddress()} paid you"
}
memo.contains("eply to:") -> {
val address = memo.split("eply to:")[1].trim()
val address = memo.split("eply to:")[1].trim().validateAddress() ?: "Unknown"
"${address.toAbbreviatedAddress()} paid you"
}
memo.contains("zs") -> {
val who = extractAddress(memo)?.toAbbreviatedAddress() ?: "Unknown"
val who = extractAddress(memo).validateAddress()?.toAbbreviatedAddress() ?: "Unknown"
"$who paid you"
}
else -> "Unknown paid you"
@ -145,6 +148,11 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
(itemView.context as MainActivity).copyText(it, "Transaction Address")
}
}
private suspend fun String?.validateAddress(): String? {
if (this == null) return null
return if ((itemView.context as MainActivity).isValidAddress(this)) this else null
}
}
private fun ByteArray.toTxId(): String {

View File

@ -15,12 +15,12 @@ import cash.z.ecc.android.ext.toColoredSpan
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Tap.DETAIL_BACK
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.block.CompactBlockProcessor.WalletBalance
import cash.z.wallet.sdk.entity.ConfirmedTransaction
import cash.z.wallet.sdk.ext.collectWith
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.WalletBalance
import cash.z.ecc.android.sdk.db.entity.ConfirmedTransaction
import cash.z.ecc.android.sdk.ext.collectWith
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.launch

View File

@ -1,8 +1,8 @@
package cash.z.ecc.android.ui.detail
import androidx.lifecycle.ViewModel
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.ext.twig
import javax.inject.Inject
class WalletDetailViewModel @Inject constructor() : ViewModel() {

View File

@ -18,13 +18,13 @@ import cash.z.ecc.android.ui.home.HomeFragment.BannerAction.*
import cash.z.ecc.android.ui.send.SendViewModel
import cash.z.ecc.android.ui.setup.WalletSetupViewModel
import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.NO_SEED
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.Synchronizer.Status.*
import cash.z.wallet.sdk.block.CompactBlockProcessor
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.ext.convertZecToZatoshi
import cash.z.wallet.sdk.ext.safelyConvertToBigDecimal
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.Synchronizer.Status.*
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.convertZecToZatoshi
import cash.z.ecc.android.sdk.ext.safelyConvertToBigDecimal
import cash.z.ecc.android.sdk.ext.twig
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*

View File

@ -1,14 +1,14 @@
package cash.z.ecc.android.ui.home
import androidx.lifecycle.ViewModel
import cash.z.wallet.sdk.SdkSynchronizer
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.Synchronizer.Status.*
import cash.z.wallet.sdk.block.CompactBlockProcessor
import cash.z.wallet.sdk.exception.RustLayerException
import cash.z.wallet.sdk.ext.ZcashSdk.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.ext.ZcashSdk.ZATOSHI_PER_ZEC
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.SdkSynchronizer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.Synchronizer.Status.*
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.exception.RustLayerException
import cash.z.ecc.android.sdk.ext.ZcashSdk.MINERS_FEE_ZATOSHI
import cash.z.ecc.android.sdk.ext.ZcashSdk.ZATOSHI_PER_ZEC
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.flow.*
import javax.inject.Inject

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.ui.home
import android.animation.ValueAnimator
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.twig
import com.airbnb.lottie.LottieAnimationView
class MagicSnakeLoader(

View File

@ -19,8 +19,8 @@ import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.UserFeedback
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.launch
import okio.Okio
import java.io.File

View File

@ -1,8 +1,8 @@
package cash.z.ecc.android.ui.profile
import androidx.lifecycle.ViewModel
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.ext.twig
import javax.inject.Inject
class ProfileViewModel @Inject constructor() : ViewModel() {

View File

@ -13,8 +13,8 @@ import cash.z.ecc.android.ext.onClickNavTo
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

View File

@ -1,8 +1,8 @@
package cash.z.ecc.android.ui.receive
import androidx.lifecycle.ViewModel
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.ext.twig
import javax.inject.Inject
class ReceiveViewModel @Inject constructor() : ViewModel() {

View File

@ -2,9 +2,9 @@ package cash.z.ecc.android.ui.scan
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import cash.z.wallet.sdk.ext.retrySimple
import cash.z.wallet.sdk.ext.retryUpTo
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.retrySimple
import cash.z.ecc.android.sdk.ext.retryUpTo
import cash.z.ecc.android.sdk.ext.twig
import com.google.android.gms.tasks.Task
import com.google.firebase.ml.vision.FirebaseVision
import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode

View File

@ -3,12 +3,10 @@ package cash.z.ecc.android.ui.scan
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import cash.z.ecc.android.R
@ -18,11 +16,15 @@ import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.ext.onClickNavBack
import cash.z.ecc.android.ext.onClickNavTo
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.feedback.Report.Tap.SCAN_BACK
import cash.z.ecc.android.feedback.Report.Tap.SCAN_RECEIVE
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.send.SendViewModel
import com.google.common.util.concurrent.ListenableFuture
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.launch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class ScanFragment : BaseFragment<FragmentScanBinding>() {
@ -33,11 +35,15 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
private var cameraExecutor: ExecutorService? = null
override fun inflate(inflater: LayoutInflater): FragmentScanBinding =
FragmentScanBinding.inflate(inflater)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (cameraExecutor != null) cameraExecutor?.shutdown()
cameraExecutor = Executors.newSingleThreadExecutor()
binding.buttonReceive.onClickNavTo(R.id.action_nav_scan_to_nav_receive) { tapped(SCAN_RECEIVE) }
binding.backButtonHitArea.onClickNavBack() { tapped(SCAN_BACK) }
@ -56,26 +62,67 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
}, ContextCompat.getMainExecutor(context))
}
override fun onDestroyView() {
super.onDestroyView()
cameraExecutor?.shutdown()
cameraExecutor = null
}
private fun bindPreview(cameraProvider: ProcessCameraProvider) {
Preview.Builder().setTargetName("Preview").build().let { preview ->
preview.setSurfaceProvider(binding.preview.previewSurfaceProvider)
// Most of the code here is adapted from: https://github.com/android/camera-samples/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt
// it's worth keeping tabs on that implementation because they keep making breaking changes to these APIs!
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
// Get screen metrics used to setup camera for full screen resolution
val metrics = DisplayMetrics().also { binding.preview.display.getRealMetrics(it) }
val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
val rotation = binding.preview.display.rotation
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
val preview =
Preview.Builder().setTargetName("Preview").setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(rotation).build()
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), QrAnalyzer { q, i ->
onQrScanned(q, i)
})
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val imageAnalysis = ImageAnalysis.Builder().setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(rotation)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(cameraExecutor!!, QrAnalyzer { q, i ->
onQrScanned(q, i)
})
// Must unbind the use-cases before rebinding them
cameraProvider.unbindAll()
try {
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
preview.setSurfaceProvider(binding.preview.createSurfaceProvider())
} catch (t: Throwable) {
// TODO: consider bubbling this up to the user
mainActivity?.feedback?.report(t)
twig("Error while opening the camera: $t")
}
}
/**
* Adapted from: https://github.com/android/camera-samples/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt#L350
*/
private fun aspectRatio(width: Int, height: Int): Int {
val previewRatio = kotlin.math.max(width, height).toDouble() / kotlin.math.min(
width,
height
)
if (kotlin.math.abs(previewRatio - (4.0 / 3.0))
<= kotlin.math.abs(previewRatio - (16.0 / 9.0))) {
return AspectRatio.RATIO_4_3
}
return AspectRatio.RATIO_16_9
}
private fun onQrScanned(qrContent: String, image: ImageProxy) {
resumedScope.launch {
if (viewModel.isNotValid(qrContent)) image.close() // continue scanning

View File

@ -1,8 +1,8 @@
package cash.z.ecc.android.ui.scan
import androidx.lifecycle.ViewModel
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.ext.twig
import javax.inject.Inject
class ScanViewModel @Inject constructor() : ViewModel() {

View File

@ -16,9 +16,10 @@ import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.Send
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.block.CompactBlockProcessor.WalletBalance
import cash.z.wallet.sdk.ext.*
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.WalletBalance
import cash.z.ecc.android.sdk.ext.*
import cash.z.ecc.android.sdk.validate.AddressType
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -85,9 +86,9 @@ class SendAddressFragment : BaseFragment<FragmentSendAddressBinding>(),
private fun onAddressChanged(address: String) {
resumedScope.launch {
var type = when (sendViewModel.validateAddress(address)) {
is Synchronizer.AddressType.Transparent -> "This is a valid transparent address" to R.color.zcashGreen
is Synchronizer.AddressType.Shielded -> "This is a valid shielded address" to R.color.zcashGreen
is Synchronizer.AddressType.Invalid -> "This address appears to be invalid" to R.color.zcashRed
is AddressType.Transparent -> "This is a valid transparent address" to R.color.zcashGreen
is AddressType.Shielded -> "This is a valid shielded address" to R.color.zcashGreen
is AddressType.Invalid -> "This address appears to be invalid" to R.color.zcashRed
}
if (address == sendViewModel.synchronizer.getAddress()) type =
"Warning, this appears to be your address!" to R.color.zcashRed

View File

@ -13,8 +13,8 @@ import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.Send
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import kotlinx.coroutines.launch
class SendConfirmFragment : BaseFragment<FragmentSendConfirmBinding>() {

View File

@ -12,11 +12,10 @@ import cash.z.ecc.android.ext.goneIf
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.entity.*
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
import cash.z.wallet.sdk.ext.twig
import com.crashlytics.android.Crashlytics
import cash.z.ecc.android.sdk.db.entity.*
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
@ -112,8 +111,8 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
} catch(t: Throwable) {
val message = "ERROR: error while handling pending transaction update! $t"
twig(message)
Crashlytics.log(message)
Crashlytics.logException(t)
mainActivity?.feedback?.report(Report.Error.NonFatal.TxUpdateFailed(t))
mainActivity?.feedback?.report(t)
}
}

View File

@ -14,13 +14,13 @@ import cash.z.ecc.android.feedback.Report.MetricType.*
import cash.z.ecc.android.lockbox.LockBox
import cash.z.ecc.android.ui.setup.WalletSetupViewModel
import cash.z.ecc.android.ui.util.INCLUDE_MEMO_PREFIX
import cash.z.wallet.sdk.Initializer
import cash.z.wallet.sdk.Synchronizer
import cash.z.wallet.sdk.entity.*
import cash.z.wallet.sdk.ext.ZcashSdk
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.ext.twig
import com.crashlytics.android.Crashlytics
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.db.entity.*
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.validate.AddressType
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@ -94,7 +94,7 @@ class SendViewModel @Inject constructor() : ViewModel() {
}
}
suspend fun validateAddress(address: String): Synchronizer.AddressType =
suspend fun validateAddress(address: String): AddressType =
synchronizer.validateAddress(address)
fun validate(maxZatoshi: Long?) = flow<String?> {
@ -143,7 +143,7 @@ class SendViewModel @Inject constructor() : ViewModel() {
report(metricId)
}
} catch (t: Throwable) {
Crashlytics.logException(RuntimeException("Error while updating Metrics", t))
feedback.report(t)
}
}

View File

@ -10,11 +10,9 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import cash.z.ecc.android.R
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.databinding.FragmentBackupBinding
import cash.z.ecc.android.di.viewmodel.activityViewModel
import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.MetricType.SEED_PHRASE_LOADED
import cash.z.ecc.android.feedback.Report.Tap.BACKUP_DONE

View File

@ -18,7 +18,7 @@ import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.SEED_WITHOUT_BACKUP
import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.SEED_WITH_BACKUP
import cash.z.wallet.sdk.Initializer
import cash.z.ecc.android.sdk.Initializer
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

View File

@ -17,12 +17,13 @@ import cash.z.ecc.android.R
import cash.z.ecc.android.databinding.FragmentRestoreBinding
import cash.z.ecc.android.di.viewmodel.activityViewModel
import cash.z.ecc.android.ext.goneIf
import cash.z.ecc.android.ext.showInvalidSeedPhraseError
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.Restore
import cash.z.ecc.android.feedback.Report.Tap.*
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.wallet.sdk.ext.ZcashSdk
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.twig
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tylersuehr.chips.Chip
import com.tylersuehr.chips.ChipsAdapter
@ -118,7 +119,12 @@ class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListen
if (birthdateString.isNullOrEmpty()) ZcashSdk.SAPLING_ACTIVATION_HEIGHT else birthdateString.toInt()
}.coerceAtLeast(ZcashSdk.SAPLING_ACTIVATION_HEIGHT)
importWallet(seedPhrase, birthday)
try {
walletSetup.validatePhrase(seedPhrase)
importWallet(seedPhrase, birthday)
} catch (t: Throwable) {
mainActivity?.showInvalidSeedPhraseError(t)
}
}
private fun importWallet(seedPhrase: String, birthday: Int) {

View File

@ -11,7 +11,7 @@ import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.Funnel.Restore
import cash.z.ecc.android.ui.MainActivity
import cash.z.ecc.android.ui.setup.SeedWordChip
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.ext.twig
class SeedWordAdapter : ChipsAdapter {

View File

@ -8,11 +8,11 @@ import cash.z.ecc.android.feedback.measure
import cash.z.ecc.android.lockbox.LockBox
import cash.z.ecc.android.ui.setup.WalletSetupViewModel.WalletSetupState.*
import cash.z.ecc.kotlin.mnemonic.Mnemonics
import cash.z.wallet.sdk.Initializer
import cash.z.wallet.sdk.Initializer.DefaultBirthdayStore
import cash.z.wallet.sdk.Initializer.DefaultBirthdayStore.Companion.ImportedWalletBirthdayStore
import cash.z.wallet.sdk.Initializer.DefaultBirthdayStore.Companion.NewWalletBirthdayStore
import cash.z.wallet.sdk.ext.twig
import cash.z.ecc.android.sdk.Initializer
import cash.z.ecc.android.sdk.Initializer.DefaultBirthdayStore
import cash.z.ecc.android.sdk.Initializer.DefaultBirthdayStore.Companion.ImportedWalletBirthdayStore
import cash.z.ecc.android.sdk.Initializer.DefaultBirthdayStore.Companion.NewWalletBirthdayStore
import cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@ -142,6 +142,13 @@ class WalletSetupViewModel @Inject constructor() : ViewModel() {
}
}
/**
* Throw an exception if the seed phrase is bad.
*/
fun validatePhrase(seedPhrase: String) {
mnemonics.validate(seedPhrase.toCharArray())
}
object LockBoxKey {
const val SEED = "cash.z.ecc.android.SEED"
const val SEED_PHRASE = "cash.z.ecc.android.SEED_PHRASE"

View File

@ -2,7 +2,7 @@ package cash.z.ecc.android
import cash.z.ecc.android.feedback.Feedback
import cash.z.ecc.android.ui.send.SendViewModel
import cash.z.wallet.sdk.entity.*
import cash.z.ecc.android.sdk.entity.*
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import com.nhaarman.mockitokotlin2.whenever

View File

@ -1,9 +1,6 @@
import cash.z.ecc.android.Deps
buildscript {
ext {
kotlin_version = '1.3.61'
}
repositories {
google()
jcenter()
@ -12,11 +9,11 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.0-rc02'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.google.gms:google-services:4.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
classpath 'io.fabric.tools:gradle:1.31.2'
classpath 'com.google.firebase:perf-plugin:1.3.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
}
}

View File

@ -3,7 +3,7 @@ package cash.z.ecc.android
object Deps {
// For use in the top-level build.gradle which gives an error when provided
// `Deps.Kotlin.version` directly
const val kotlinVersion = "1.3.61"
const val kotlinVersion = "1.3.72"
const val compileSdkVersion = 29
const val buildToolsVersion = "29.0.2"
@ -11,18 +11,34 @@ object Deps {
const val targetSdkVersion = 29
object AndroidX {
const val ANNOTATION = "androidx.annotation:annotation:1.1.0"
const val APPCOMPAT = "androidx.appcompat:appcompat:1.1.0"
const val CORE_KTX = "androidx.core:core-ktx:1.1.0"
const val CONSTRAINT_LAYOUT = "androidx.constraintlayout:constraintlayout:1.1.3"
const val CORE_KTX = "androidx.core:core-ktx:1.1.0"
const val FRAGMENT_KTX = "androidx.fragment:fragment-ktx:1.1.0-beta01"
const val LEGACY = "androidx.legacy:legacy-support-v4:1.0.0"
const val MULTIDEX = "androidx.multidex:multidex:2.0.1"
const val PAGING = "androidx.paging:paging-runtime-ktx:2.1.2"
object CameraX : Version("1.0.0-beta04") {
val CAMERA2 = "androidx.camera:camera-camera2:1.0.0-beta04"
val CORE = "androidx.camera:camera-core:1.0.0-beta04"
val LIFECYCLE = "androidx.camera:camera-lifecycle:1.0.0-beta04"
object View : Version("1.0.0-alpha11") {
val EXT = "androidx.camera:camera-extensions:1.0.0-alpha11"
val VIEW = "androidx.camera:camera-view:1.0.0-alpha11"
}
}
object Lifecycle : Version("2.2.0-rc02") {
val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
val LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:$version"
}
object Navigation : Version("2.2.0") {
val FRAGMENT_KTX = "androidx.navigation:navigation-fragment-ktx:$version"
val UI_KTX = "androidx.navigation:navigation-ui-ktx:$version"
}
object Lifecycle: Version("2.2.0-rc02") {
val LIFECYCLE_RUNTIME_KTX = "androidx.lifecycle:lifecycle-runtime-ktx:$version"
val LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:$version"
object Room : Version("2.2.5") {
val ROOM_COMPILER = "androidx.room:room-compiler:$version"
val ROOM_KTX = "androidx.room:room-ktx:$version"
}
}
object Dagger : Version("2.25.2") {
@ -31,35 +47,58 @@ object Deps {
val COMPILER = "com.google.dagger:dagger-compiler:$version"
}
object Google {
// solves error: Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules jetified-guava-26.0-android.jar (com.google.guava:guava:26.0-android) and listenablefuture-1.0.jar (com.google.guava:listenablefuture:1.0)
// per this recommendation from Chris Povirk, given guava's decision to split ListenableFuture away from Guava: https://groups.google.com/d/msg/guava-discuss/GghaKwusjcY/bCIAKfzOEwAJ
const val GUAVA = "com.google.guava:guava:27.0.1-android"
const val MATERIAL = "com.google.android.material:material:1.1.0-beta01"
// QR Scanner
const val ML_VISION = "com.google.firebase:firebase-ml-vision:24.0.3"
}
object Grpc : Version("1.25.0") {
val ANDROID = "io.grpc:grpc-android:$version"
val OKHTTP = "io.grpc:grpc-okhttp:$version"
val PROTOBUG = "io.grpc:grpc-protobuf-lite:$version"
val STUB = "io.grpc:grpc-stub:$version"
}
object Analytics { // for dogfooding/crash-reporting/feedback only on internal team builds
val CRASHLYTICS = "com.google.firebase:firebase-crashlytics:17.0.1"
val MIXPANEL = "com.mixpanel.android:mixpanel-android:5.6.3"
}
object JavaX {
const val INJECT = "javax.inject:javax.inject:1"
const val INJECT = "javax.inject:javax.inject:1"
const val JAVA_ANNOTATION = "javax.annotation:javax.annotation-api:1.3.2"
}
object Kotlin : Version(kotlinVersion) {
val STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
val STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
object Coroutines : Version("1.3.2") {
val ANDROID = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version"
val CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version"
val TEST = "org.jetbrains.kotlinx:kotlinx-coroutines-test:$version"
val ANDROID = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version"
val CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version"
val TEST = "org.jetbrains.kotlinx:kotlinx-coroutines-test:$version"
}
}
object Zcash {
val ANDROID_WALLET_PLUGINS = "com.github.zcash:zcash-android-wallet-plugins:1.0.1"
const val ANDROID_WALLET_PLUGINS = "cash.z.ecc.android:zcash-android-wallet-plugins:1.0.0"
const val KOTLIN_BIP39 = "cash.z.ecc.android:kotlin-bip39:1.0.0-beta08"
object Sdk : Version("1.1.0-beta01") {
val MAINNET = "cash.z.ecc.android:sdk-mainnet:$version"
val TESTNET = "cash.z.ecc.android:sdk-testnet:$version"
}
}
object Misc {
const val LOTTIE = "com.airbnb.android:lottie:3.1.0"
object Plugins {
val SECURE_STORAGE = "de.adorsys.android:securestoragelibrary:1.2.2"
val ANDROID_BIP39 = "cash.z.ecc.android:android-bip39:1.0.0-beta07"
val QR_SCANNER = "com.google.zxing:core:3.2.1"
const val SECURE_STORAGE = "de.adorsys.android:securestoragelibrary:1.2.2"
const val QR_SCANNER = "com.google.zxing:core:3.2.1"
}
}
object Test {
const val JUNIT = "junit:junit:4.12"
const val JUNIT = "junit:junit:4.12"
const val MOKITO = "junit:junit:4.12"
const val COROUTINES_TEST = "junit:junit:4.12"
object Android {
const val JUNIT = "androidx.test.ext:junit:1.1.1"
const val ESPRESSO = "androidx.test.espresso:espresso-core:3.2.0"
const val JUNIT = "androidx.test.ext:junit:1.1.1"
const val ESPRESSO = "androidx.test.espresso:espresso-core:3.2.0"
}
}
}

View File

@ -125,6 +125,7 @@ class FeedbackCoordinator(val feedback: Feedback, defaultObservers: Set<Feedback
}
companion object {
const val ENABLED = "setting.feedbackcoordinater.enabled"
private val mutex: Mutex = Mutex()
}
}

View File

@ -1,6 +1,6 @@
#Tue Jan 07 12:00:21 EST 2020
#Fri May 29 19:00:53 EDT 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

View File

@ -8,7 +8,7 @@ dependencies {
// Zcash
implementation Deps.Zcash.ANDROID_WALLET_PLUGINS
implementation Deps.Misc.Plugins.ANDROID_BIP39
implementation Deps.Zcash.KOTLIN_BIP39
testImplementation Deps.Test.JUNIT
}

View File

@ -1,57 +1,26 @@
package cash.z.ecc.kotlin.mnemonic
import cash.z.android.plugin.MnemonicPlugin
import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode
import cash.z.ecc.android.bip39.Mnemonics.WordCount
import cash.z.ecc.android.bip39.toEntropy
import cash.z.ecc.android.bip39.toSeed
import java.util.*
import java.util.Locale.ENGLISH
import javax.inject.Inject
class Mnemonics @Inject constructor(): MnemonicPlugin {
override fun fullWordList(languageCode: String): List<String> {
return cash.z.ecc.android.bip39.Mnemonics.getCachedWords(Locale.ENGLISH.language)
}
class Mnemonics @Inject constructor() : MnemonicPlugin {
override fun fullWordList(languageCode: String) = Mnemonics.getCachedWords(Locale.ENGLISH.language)
override fun nextEntropy(): ByteArray = WordCount.COUNT_24.toEntropy()
override fun nextMnemonic(): CharArray = MnemonicCode(WordCount.COUNT_24).chars
override fun nextMnemonic(entropy: ByteArray): CharArray = MnemonicCode(entropy).chars
override fun nextMnemonicList(): List<CharArray> = MnemonicCode(WordCount.COUNT_24).words
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> = MnemonicCode(entropy).words
override fun toSeed(mnemonic: CharArray): ByteArray = MnemonicCode(mnemonic).toSeed()
override fun toWordList(mnemonic: CharArray): List<CharArray> = MnemonicCode(mnemonic).words
override fun nextEntropy(): ByteArray {
return WordCount.COUNT_24.toEntropy()
}
override fun nextMnemonic(): CharArray {
return nextMnemonic(nextEntropy())
}
override fun nextMnemonic(entropy: ByteArray): CharArray {
return MnemonicCode(entropy).chars
}
override fun nextMnemonicList(): List<CharArray> {
return nextMnemonicList(nextEntropy())
}
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> {
return MnemonicCode(entropy).map { it.toCharArray() }
}
override fun toSeed(mnemonic: CharArray): ByteArray {
return MnemonicCode(mnemonic).toSeed()
}
override fun toWordList(mnemonic: CharArray): List<CharArray> {
val wordList = mutableListOf<CharArray>()
var cursor = 0
repeat(mnemonic.size) { i ->
val isSpace = mnemonic[i] == ' '
if (isSpace || i == (mnemonic.size - 1)) {
val wordSize = i - cursor + if (isSpace) 0 else 1
wordList.add(CharArray(wordSize).apply {
repeat(wordSize) {
this[it] = mnemonic[cursor + it]
}
})
cursor = i + 1
}
}
return wordList
fun validate(mnemonic: CharArray) {
MnemonicCode(mnemonic).validate()
}
}

View File

@ -1,4 +1,3 @@
rootProject.name='Zcash Wallet'
include ':app', ':qrecycler', ':feedback', ':mnemonic', ':lockbox', ':sdk', ':chipsinputlayout'
project(":sdk").projectDir = file("../zcash-android-wallet-sdk")
include ':app', ':qrecycler', ':feedback', ':mnemonic', ':lockbox', ':chipsinputlayout'
project(":chipsinputlayout").projectDir = file("../../clones/chips-input-layout/library")