Implemented the Restore feature.
This commit is contained in:
parent
61ec3bed66
commit
899e48b9f3
|
@ -159,6 +159,10 @@ dependencies {
|
|||
// Misc.
|
||||
implementation 'com.airbnb.android:lottie:3.1.0'
|
||||
implementation 'com.facebook.stetho:stetho:1.5.1'
|
||||
implementation 'com.github.gmale:chips-input-layout:4cd87089930fc16176f9fad1362b48a392449e23'
|
||||
|
||||
implementation "com.github.gmale:nachos:8a71c6a"
|
||||
|
||||
|
||||
// Tests
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
|
|||
|
||||
override fun onCreate() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
|
||||
Twig.plant(SilentTwig())
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
creationTime = System.currentTimeMillis()
|
||||
instance = this
|
||||
// Setup handler for uncaught exceptions.
|
||||
|
|
|
@ -4,7 +4,6 @@ package cash.z.ecc.android.di.module
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import cash.z.ecc.android.di.annotation.ActivityScope
|
||||
import cash.z.ecc.android.di.annotation.SynchronizerScope
|
||||
import cash.z.ecc.android.di.annotation.ViewModelKey
|
||||
import cash.z.ecc.android.di.viewmodel.ViewModelFactory
|
||||
import cash.z.ecc.android.ui.setup.WalletSetupViewModel
|
||||
|
@ -26,6 +25,7 @@ abstract class ViewModelsActivityModule {
|
|||
@ViewModelKey(WalletSetupViewModel::class)
|
||||
abstract fun bindWalletSetupViewModel(implementation: WalletSetupViewModel): ViewModel
|
||||
|
||||
|
||||
/**
|
||||
* Factory for view models that are created until before the Synchronizer exists. This is a
|
||||
* little tricky because we cannot make them all in one place or else they won't be available
|
||||
|
|
|
@ -13,6 +13,13 @@ inline fun <reified VM : ViewModel> BaseFragment<*>.viewModel() = object : Lazy<
|
|||
?: ViewModelProvider(this@viewModel, scopedFactory<VM>())[VM::class.java]
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a view model that is scoped to the lifecycle of the activity.
|
||||
*
|
||||
* @param isSynchronizerScope true when this view model depends on the Synchronizer. False when this
|
||||
* viewModel needs to be created before the synchronizer or otherwise has no dependency on it being
|
||||
* available for use.
|
||||
*/
|
||||
inline fun <reified VM : ViewModel> BaseFragment<*>.activityViewModel(isSynchronizerScope: Boolean = true) = object : Lazy<VM> {
|
||||
val cached: VM? = null
|
||||
override fun isInitialized(): Boolean = cached != null
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package cash.z.ecc.android.ext
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.IntegerRes
|
||||
|
@ -28,3 +29,9 @@ internal inline fun @receiver:StringRes Int.toAppString(): String {
|
|||
internal inline fun @receiver:IntegerRes Int.toAppInt(): Int {
|
||||
return ZcashWalletApp.instance.resources.getInteger(this)}
|
||||
|
||||
|
||||
fun Float.toPx() = this * Resources.getSystem().displayMetrics.density
|
||||
|
||||
fun Int.toPx() = (this * Resources.getSystem().displayMetrics.density + 0.5f).toInt()
|
||||
|
||||
fun Int.toDp() = (this / Resources.getSystem().displayMetrics.density + 0.5f).toInt()
|
|
@ -94,7 +94,7 @@ class LandingFragment : BaseFragment<FragmentLandingBinding>() {
|
|||
}
|
||||
|
||||
private fun onRestoreWallet() {
|
||||
Toast.makeText(activity, "Coming soon!", Toast.LENGTH_SHORT).show()
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_landing_to_nav_restore)
|
||||
}
|
||||
|
||||
// AKA import wallet
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
package cash.z.ecc.android.ui.setup
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.view.*
|
||||
import android.view.MotionEvent.ACTION_DOWN
|
||||
import android.view.MotionEvent.ACTION_UP
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.toAppColor
|
||||
import cash.z.ecc.android.ext.toPx
|
||||
import cash.z.ecc.android.ui.base.BaseFragment
|
||||
import cash.z.wallet.sdk.ext.ZcashSdk
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.hootsuite.nachos.ChipConfiguration
|
||||
import com.hootsuite.nachos.chip.ChipCreator
|
||||
import com.hootsuite.nachos.chip.ChipSpan
|
||||
import com.hootsuite.nachos.tokenizer.SpanChipTokenizer
|
||||
import com.tylersuehr.chips.Chip
|
||||
import com.tylersuehr.chips.ChipsAdapter
|
||||
import com.tylersuehr.chips.SeedWordAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class RestoreFragment : BaseFragment<FragmentRestoreBinding>(), View.OnKeyListener {
|
||||
|
||||
private val walletSetup: WalletSetupViewModel by activityViewModel(false)
|
||||
|
||||
private lateinit var seedWordRecycler: RecyclerView
|
||||
private var seedWordAdapter: SeedWordAdapter? = null
|
||||
|
||||
override fun inflate(inflater: LayoutInflater): FragmentRestoreBinding =
|
||||
FragmentRestoreBinding.inflate(inflater)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
seedWordRecycler = binding.chipsInput.findViewById<RecyclerView>(R.id.chips_recycler)
|
||||
seedWordAdapter = SeedWordAdapter(seedWordRecycler.adapter as ChipsAdapter).onDataSetChanged {
|
||||
onChipsModified()
|
||||
}.also { onChipsModified() }
|
||||
seedWordRecycler.adapter = seedWordAdapter
|
||||
|
||||
|
||||
binding.chipsInput.apply {
|
||||
setFilterableChipList(getChips())
|
||||
setDelimiter("[ ;,]", true)
|
||||
}
|
||||
|
||||
binding.buttonDone.setOnClickListener {
|
||||
onDone()
|
||||
}
|
||||
|
||||
binding.buttonSuccess.setOnClickListener {
|
||||
onEnterWallet()
|
||||
}
|
||||
//
|
||||
//
|
||||
// seedWordAdapter!!.editText.setOnKeyListener(this)
|
||||
|
||||
binding.textTitle.setOnClickListener {
|
||||
seedWordAdapter!!.editText.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
|
||||
binding.textSubtitle.setOnClickListener {
|
||||
seedWordAdapter!!.editText.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
mainActivity?.onFragmentBackPressed(this) {
|
||||
if (seedWordAdapter == null || seedWordAdapter?.itemCount == 1) {
|
||||
onExit()
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setMessage("Are you sure? For security, the words that you have entered will be cleared!")
|
||||
.setTitle("Abort?")
|
||||
.setPositiveButton("Stay") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton("Exit") { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
onExit()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// Require one less tap to enter the seed words
|
||||
touchScreenForUser()
|
||||
}
|
||||
|
||||
|
||||
private fun onExit() {
|
||||
hideAutoCompleteWords()
|
||||
setKeyboardShown(false)
|
||||
mainActivity?.navController?.popBackStack()
|
||||
}
|
||||
|
||||
private fun onEnterWallet() {
|
||||
mainActivity?.navController?.navigate(R.id.action_nav_restore_to_nav_home)
|
||||
}
|
||||
|
||||
private fun onDone() {
|
||||
setKeyboardShown(false)
|
||||
val seedPhrase = binding.chipsInput.selectedChips.joinToString(" ") {
|
||||
it.title
|
||||
}
|
||||
var birthday = binding.root.findViewById<TextView>(R.id.input_birthdate).text.toString()
|
||||
.let { birthdateString ->
|
||||
if (birthdateString.isNullOrEmpty()) ZcashSdk.SAPLING_ACTIVATION_HEIGHT else birthdateString.toInt()
|
||||
}.coerceAtLeast(ZcashSdk.SAPLING_ACTIVATION_HEIGHT)
|
||||
|
||||
importWallet(seedPhrase, birthday)
|
||||
}
|
||||
|
||||
private fun importWallet(seedPhrase: String, birthday: Int) {
|
||||
setKeyboardShown(false)
|
||||
mainActivity?.apply {
|
||||
lifecycleScope.launch {
|
||||
mainActivity?.startSync(walletSetup.importWallet(seedPhrase, birthday))
|
||||
}
|
||||
playSound("sound_receive_small.mp3")
|
||||
vibrateSuccess()
|
||||
}
|
||||
|
||||
binding.groupDone.visibility = View.GONE
|
||||
binding.groupStart.visibility = View.GONE
|
||||
binding.groupSuccess.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun onChipsModified() {
|
||||
seedWordAdapter?.editText?.apply {
|
||||
postDelayed({
|
||||
requestFocus()
|
||||
isCursorVisible = false
|
||||
},40L)
|
||||
}
|
||||
setDoneEnabled()
|
||||
}
|
||||
|
||||
private fun setDoneEnabled() {
|
||||
val count = seedWordAdapter?.itemCount ?: 0
|
||||
binding.groupDone.goneIf(count <= 24)
|
||||
}
|
||||
|
||||
private fun setKeyboardShown(isShown: Boolean) {
|
||||
if (isShown) {
|
||||
mainActivity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||
} else {
|
||||
(requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view!!.windowToken, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideAutoCompleteWords() {
|
||||
seedWordAdapter?.editText?.setText("")
|
||||
}
|
||||
|
||||
private fun getChips(): List<Chip> {
|
||||
return resources.getStringArray(R.array.word_list).map {
|
||||
SeedWordChip(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun touchScreenForUser() {
|
||||
seedWordAdapter?.editText?.apply {
|
||||
postDelayed({
|
||||
seedWordAdapter?.editText?.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
|
||||
dispatchTouchEvent(motionEvent(ACTION_DOWN))
|
||||
dispatchTouchEvent(motionEvent(ACTION_UP))
|
||||
}, 100L)
|
||||
}
|
||||
}
|
||||
|
||||
private fun motionEvent(action: Int) = SystemClock.uptimeMillis().let { now ->
|
||||
MotionEvent.obtain(now, now, action, 0f, 0f, 0)
|
||||
}
|
||||
|
||||
override fun onKey(v: View?, keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SeedWordTokenizer(context: Context) : SpanChipTokenizer<ChipSpan>(context, MyChipSpanChipCreator(), ChipSpan::class.java) {
|
||||
override fun applyConfiguration(text: Editable, chipConfiguration: ChipConfiguration) {
|
||||
mChipConfiguration = chipConfiguration
|
||||
|
||||
|
||||
val allChips = findAllChips(0, text.length, text)
|
||||
allChips.forEachIndexed { i, chip ->
|
||||
val chipStart = findChipStart(chip, text)
|
||||
deleteChip(chip, text)
|
||||
val newChip = (mChipCreator as MyChipSpanChipCreator).createChip(mContext, chip, i)
|
||||
text.insert(chipStart, terminateToken(newChip))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MyChipSpanChipCreator : ChipCreator<ChipSpan> {
|
||||
override fun createChip(context: Context, text: CharSequence, data: Any?): ChipSpan {
|
||||
return MyChipSpan(context, text, ContextCompat.getDrawable(context, R.mipmap.ic_launcher), data)
|
||||
}
|
||||
|
||||
fun createChip(context: Context, existingChip: ChipSpan, data: Any?): MyChipSpan {
|
||||
return MyChipSpan(context, existingChip, data)
|
||||
}
|
||||
|
||||
override fun createChip(context: Context, existingChip: ChipSpan): ChipSpan {
|
||||
throw IllegalAccessException("Provide data when creating a chip")
|
||||
}
|
||||
|
||||
override fun configureChip(chip: ChipSpan, chipConfiguration: ChipConfiguration) {
|
||||
val chipHorizontalSpacing = chipConfiguration.chipHorizontalSpacing
|
||||
val chipBackground = chipConfiguration.chipBackground
|
||||
val chipCornerRadius = chipConfiguration.chipCornerRadius
|
||||
val chipTextColor = chipConfiguration.chipTextColor
|
||||
val chipTextSize = chipConfiguration.chipTextSize
|
||||
val chipHeight = chipConfiguration.chipHeight
|
||||
val chipVerticalSpacing = chipConfiguration.chipVerticalSpacing
|
||||
val maxAvailableWidth = chipConfiguration.maxAvailableWidth
|
||||
|
||||
if (chipHorizontalSpacing != -1) {
|
||||
chip.setLeftMargin(chipHorizontalSpacing / 2)
|
||||
chip.setRightMargin(chipHorizontalSpacing / 2)
|
||||
}
|
||||
if (chipBackground != null) {
|
||||
chip.setBackgroundColor(chipBackground)
|
||||
}
|
||||
if (chipCornerRadius != -1) {
|
||||
chip.setCornerRadius(chipCornerRadius)
|
||||
}
|
||||
if (chipTextColor != Color.TRANSPARENT) {
|
||||
chip.setTextColor(chipTextColor)
|
||||
}
|
||||
if (chipTextSize != -1) {
|
||||
chip.setTextSize(chipTextSize)
|
||||
}
|
||||
if (chipHeight != -1) {
|
||||
chip.setChipHeight(chipHeight)
|
||||
}
|
||||
if (chipVerticalSpacing != -1) {
|
||||
chip.setChipVerticalSpacing(chipVerticalSpacing)
|
||||
}
|
||||
if (maxAvailableWidth != -1) {
|
||||
chip.setMaxAvailableWidth(maxAvailableWidth)
|
||||
}
|
||||
|
||||
chip.setShowIconOnLeft(true)
|
||||
}
|
||||
}
|
||||
|
||||
class MyChipSpan : ChipSpan {
|
||||
val index: Int
|
||||
|
||||
constructor(context: Context, text: CharSequence, drawable: Drawable?, data: Any?)
|
||||
: super(context, text, drawable, data) {
|
||||
index = data as? Int ?: 0
|
||||
}
|
||||
|
||||
constructor(context: Context, chip: ChipSpan, data: Any?)
|
||||
: super(context, chip) {
|
||||
index = data as? Int ?: 0
|
||||
}
|
||||
|
||||
override fun drawBackground(canvas: Canvas, x: Float, top: Int, bottom: Int, paint: Paint) {
|
||||
val rect = RectF(x, top.toFloat(), x + mChipWidth, bottom.toFloat())
|
||||
val cornerRadius = 4.0f.toPx()
|
||||
|
||||
paint.color = R.color.background_banner.toAppColor()
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
|
||||
|
||||
paint.color = R.color.background_banner_stroke.toAppColor()
|
||||
paint.style = Paint.Style.STROKE
|
||||
paint.strokeJoin = Paint.Join.ROUND
|
||||
paint.strokeMiter = 10.0f
|
||||
paint.strokeWidth = 1.5f.toPx()
|
||||
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
|
||||
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = mTextColor
|
||||
}
|
||||
}
|
||||
|
||||
class SeedWordChip(val word: String, var index: Int = -1) : Chip() {
|
||||
override fun getSubtitle(): String? = null//"subtitle for $word"
|
||||
override fun getAvatarDrawable(): Drawable? = null
|
||||
override fun getId() = index
|
||||
override fun getTitle() = word
|
||||
override fun getAvatarUri() = null
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.tylersuehr.chips
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import cash.z.ecc.android.R
|
||||
import cash.z.ecc.android.ext.toAppColor
|
||||
|
||||
class SeedWordAdapter : ChipsAdapter {
|
||||
|
||||
constructor(existingAdapter: ChipsAdapter) : super(existingAdapter.mDataSource, existingAdapter.mEditText, existingAdapter.mOptions)
|
||||
|
||||
val editText = mEditText
|
||||
private var onDataSetChangedListener: (() -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return if (viewType == CHIP) SeedWordHolder(SeedWordChipView(parent.context))
|
||||
else object : RecyclerView.ViewHolder(mEditText) {}
|
||||
}
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (getItemViewType(position) == CHIP) { // Chips
|
||||
// Display the chip information on the chip view
|
||||
(holder as SeedWordHolder).seedChipView.bind(mDataSource.getSelectedChip(position), position);
|
||||
} else {
|
||||
val size = mDataSource.selectedChips.size
|
||||
mEditText.hint = if (size < 3) {
|
||||
mEditText.isEnabled = true
|
||||
mEditText.setHintTextColor(R.color.text_light_dimmed.toAppColor())
|
||||
val ordinal = when(size) {2 -> "3rd"; 1 -> "2nd"; else -> "1st"}
|
||||
"Enter $ordinal seed word"
|
||||
} else if(size >= 24) {
|
||||
mEditText.setHintTextColor(R.color.zcashGreen.toAppColor())
|
||||
mEditText.isEnabled = false
|
||||
"done"
|
||||
} else {
|
||||
mEditText.isEnabled = true
|
||||
mEditText.setHintTextColor(R.color.zcashYellow.toAppColor())
|
||||
"${size + 1}"
|
||||
}
|
||||
}
|
||||
onDataSetChangedListener?.invoke()
|
||||
}
|
||||
|
||||
fun onDataSetChanged(block: () -> Unit): SeedWordAdapter {
|
||||
onDataSetChangedListener = block
|
||||
return this
|
||||
}
|
||||
|
||||
override fun onKeyboardActionDone(text: String?) {
|
||||
if (TextUtils.isEmpty(text)) return
|
||||
|
||||
if (mDataSource.originalChips.firstOrNull { it.title == text } != null) {
|
||||
mEditText.setText("");
|
||||
mDataSource.addSelectedChip(DefaultCustomChip(text))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private inner class SeedWordHolder(chipView: SeedWordChipView) : ChipsAdapter.ChipHolder(chipView) {
|
||||
val seedChipView = super.chipView as SeedWordChipView
|
||||
}
|
||||
|
||||
private inner class SeedWordChipView(context: Context) : ChipView(context) {
|
||||
private val indexView: TextView = findViewById(R.id.chip_index)
|
||||
|
||||
fun bind(chip: Chip, index: Int) {
|
||||
super.inflateFromChip(chip)
|
||||
indexView.text = (index + 1).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="3dp" />
|
||||
<stroke android:width="1dp" android:color="#282828"/>
|
||||
<solid android:color="@color/background_banner"/>
|
||||
</shape>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,65 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/chip_height"
|
||||
android:background="@drawable/bg_chip_view"
|
||||
android:clickable="true">
|
||||
|
||||
<com.tylersuehr.chips.CircleImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="@dimen/chip_height"
|
||||
android:layout_height="@dimen/chip_height"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/avatar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chip_index"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="12"
|
||||
android:textSize="12dp"
|
||||
android:fontFamily="@font/inconsolata"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
android:textSize="@dimen/chip_label_text_size"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toRightOf="@+id/chip_index"
|
||||
app:layout_constraintRight_toLeftOf="@+id/button_delete"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="artwork" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_delete"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:alpha=".54"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/chip_delete_icon_20dp"
|
||||
android:tint="@color/zcashWhite_24"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:clickable="true">
|
||||
<com.tylersuehr.chips.CircleImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/avatar"/>
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:textSize="18dp"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/text_light"
|
||||
app:layout_constraintBottom_toTopOf="@+id/subtitle"
|
||||
app:layout_constraintLeft_toRightOf="@+id/image"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title"/>
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:alpha=".56"
|
||||
android:textSize="@dimen/chip_label_text_size"
|
||||
android:textColor="#212121"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
app:layout_constraintLeft_toRightOf="@+id/image"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="Subtitle"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,187 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="@drawable/background_home">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingTop="32dp"
|
||||
android:text="Restoring from a backup"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:paddingBottom="32dp"
|
||||
android:paddingStart="32dp"
|
||||
android:paddingTop="18dp"
|
||||
android:text="You will need to enter all 24 seed words.\nDon't worry, we will find them as you type."
|
||||
android:textColor="@color/text_light_dimmed"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_title" />
|
||||
|
||||
<com.tylersuehr.chips.ChipsInputLayout
|
||||
android:id="@+id/chips_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/zcashBlack_54"
|
||||
android:hint="Enter the 1st seed word..."
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:textColor="@color/text_light"
|
||||
android:textColorHint="#757575"
|
||||
app:hideKeyboardOnChipClick="false"
|
||||
app:allowCustomChips="false"
|
||||
app:chip_showDelete="false"
|
||||
app:chip_showDetails="true"
|
||||
app:chip_textColor="@color/text_light_dimmed"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_subtitle" />
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/chips_input" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/text_light_dimmed"
|
||||
app:layout_constraintBottom_toBottomOf="@id/chips_input"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/text_layout_birthdate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="Enter wallet birthday height (recommended)"
|
||||
android:theme="@style/Zcash.Overlay.TextInputLayout"
|
||||
app:helperText="e.g. 419,200. This determines where to start scanning for transactions. Leave it blank to scan from the beginning, which takes a while."
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/divider_bottom"
|
||||
app:layout_constraintWidth_percent="0.84">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/input_birthdate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:maxLength="6"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/text_light"
|
||||
android:textColorHint="@color/text_light_dimmed" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_done"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Import"
|
||||
android:textColor="@color/text_dark"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_layout_birthdate" />
|
||||
|
||||
<com.airbnb.lottie.LottieAnimationView
|
||||
android:id="@+id/lottie_success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="W,1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.33333"
|
||||
app:layout_constraintWidth_percent="0.4053398058"
|
||||
app:lottie_autoPlay="true"
|
||||
app:lottie_loop="false"
|
||||
app:lottie_rawRes="@raw/lottie_success" />
|
||||
|
||||
<!-- <ImageView-->
|
||||
<!-- android:id="@+id/icon_success"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="0dp"-->
|
||||
<!-- android:tint="#00FF00"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||
<!-- app:layout_constraintDimensionRatio="W,1:1"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
||||
<!-- app:layout_constraintVertical_bias="0.33333"-->
|
||||
<!-- app:layout_constraintWidth_percent="0.4053398058"-->
|
||||
<!-- app:srcCompat="@drawable/ic_check_shield" />-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="Success"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/lottie_success" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="View Wallet"
|
||||
android:textColor="@color/text_dark"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_success"
|
||||
/>
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/group_success"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:constraint_referenced_ids="lottie_success, text_success, button_success" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/group_done"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="button_done, text_layout_birthdate" />
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/group_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:constraint_referenced_ids="text_title, text_subtitle, chips_input, divider_bottom, divider_top" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -156,6 +156,9 @@
|
|||
app:destination="@id/nav_backup"
|
||||
app:popUpTo="@id/nav_landing"
|
||||
app:popUpToInclusive="true"/>
|
||||
<action
|
||||
android:id="@+id/action_nav_landing_to_nav_restore"
|
||||
app:destination="@id/nav_restore" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -164,6 +167,17 @@
|
|||
tools:layout="@layout/fragment_backup" >
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_restore"
|
||||
android:name="cash.z.ecc.android.ui.setup.RestoreFragment"
|
||||
tools:layout="@layout/fragment_restore" >
|
||||
<action
|
||||
android:id="@+id/action_nav_restore_to_nav_home"
|
||||
app:destination="@id/nav_home"
|
||||
app:popUpTo="@id/nav_landing"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
|
||||
<!-- -->
|
||||
<!-- Global actions -->
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,7 @@ allprojects {
|
|||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue