Merge pull request #154 from zcash/task/clean-up-dependencies
Task/clean up dependencies
This commit is contained in:
commit
41422992ce
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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, _ ->
|
||||
|
|
|
@ -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) :
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>>(
|
||||
|
|
|
@ -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,7 +30,7 @@ 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 = ""
|
||||
|
@ -96,20 +98,21 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
|
|||
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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
||||
// 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 preview =
|
||||
Preview.Builder().setTargetName("Preview").setTargetAspectRatio(screenAspectRatio)
|
||||
.setTargetRotation(rotation).build()
|
||||
|
||||
val cameraSelector = CameraSelector.Builder()
|
||||
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
||||
.build()
|
||||
|
||||
val imageAnalysis = ImageAnalysis.Builder()
|
||||
val imageAnalysis = ImageAnalysis.Builder().setTargetAspectRatio(screenAspectRatio)
|
||||
.setTargetRotation(rotation)
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
.build()
|
||||
|
||||
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), QrAnalyzer { q, i ->
|
||||
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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
try {
|
||||
walletSetup.validatePhrase(seedPhrase)
|
||||
importWallet(seedPhrase, birthday)
|
||||
} catch (t: Throwable) {
|
||||
mainActivity?.showInvalidSeedPhraseError(t)
|
||||
}
|
||||
}
|
||||
|
||||
private fun importWallet(seedPhrase: String, birthday: Int) {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,10 +47,26 @@ 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 JAVA_ANNOTATION = "javax.annotation:javax.annotation-api:1.3.2"
|
||||
}
|
||||
object Kotlin : Version(kotlinVersion) {
|
||||
val STDLIB = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
|
||||
|
@ -45,18 +77,25 @@ object Deps {
|
|||
}
|
||||
}
|
||||
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 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"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue