diff --git a/app/src/main/java/com/stevesoltys/backup/restore/RestoreActivity.kt b/app/src/main/java/com/stevesoltys/backup/restore/RestoreActivity.kt index bd9d0660..83f26664 100644 --- a/app/src/main/java/com/stevesoltys/backup/restore/RestoreActivity.kt +++ b/app/src/main/java/com/stevesoltys/backup/restore/RestoreActivity.kt @@ -15,6 +15,8 @@ class RestoreActivity : BackupActivity() { override fun getInitialFragment() = RestoreSetFragment() + override fun isRestoreOperation() = true + override fun onCreate(savedInstanceState: Bundle?) { viewModel = ViewModelProviders.of(this).get(RestoreViewModel::class.java) super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/stevesoltys/backup/settings/SettingsActivity.kt b/app/src/main/java/com/stevesoltys/backup/settings/SettingsActivity.kt index 8aefd061..11b2ae4f 100644 --- a/app/src/main/java/com/stevesoltys/backup/settings/SettingsActivity.kt +++ b/app/src/main/java/com/stevesoltys/backup/settings/SettingsActivity.kt @@ -14,6 +14,8 @@ class SettingsActivity : BackupActivity() { override fun getInitialFragment() = SettingsFragment() + override fun isRestoreOperation() = false + override fun onCreate(savedInstanceState: Bundle?) { viewModel = ViewModelProviders.of(this).get(SettingsViewModel::class.java) super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/stevesoltys/backup/ui/BackupActivity.kt b/app/src/main/java/com/stevesoltys/backup/ui/BackupActivity.kt index 666bdd0a..e01d88a4 100644 --- a/app/src/main/java/com/stevesoltys/backup/ui/BackupActivity.kt +++ b/app/src/main/java/com/stevesoltys/backup/ui/BackupActivity.kt @@ -27,6 +27,8 @@ abstract class BackupActivity : AppCompatActivity() { protected abstract fun getInitialFragment(): Fragment + protected abstract fun isRestoreOperation(): Boolean + @CallSuper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -80,6 +82,7 @@ abstract class BackupActivity : AppCompatActivity() { private fun showRecoveryCodeActivity() { val intent = Intent(this, RecoveryCodeActivity::class.java) + intent.putExtra(INTENT_EXTRA_IS_RESTORE, isRestoreOperation()) startActivityForResult(intent, REQUEST_CODE_RECOVERY_CODE) } diff --git a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeActivity.kt b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeActivity.kt index 69e1673a..e34650f4 100644 --- a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeActivity.kt +++ b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeActivity.kt @@ -6,6 +6,8 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModelProviders import com.stevesoltys.backup.R +internal const val INTENT_EXTRA_IS_RESTORE = "isRestore" + class RecoveryCodeActivity : AppCompatActivity() { private lateinit var viewModel: RecoveryCodeViewModel @@ -16,14 +18,9 @@ class RecoveryCodeActivity : AppCompatActivity() { setContentView(R.layout.activity_recovery_code) viewModel = ViewModelProviders.of(this).get(RecoveryCodeViewModel::class.java) + viewModel.isRestore = isRestore() viewModel.confirmButtonClicked.observeEvent(this, LiveEventHandler { clicked -> - if (clicked) { - val tag = "Confirm" - supportFragmentManager.beginTransaction() - .replace(R.id.fragment, RecoveryCodeInputFragment(), tag) - .addToBackStack(tag) - .commit() - } + if (clicked) showInput(true) }) viewModel.recoveryCodeSaved.observeEvent(this, LiveEventHandler { saved -> if (saved) { @@ -35,9 +32,8 @@ class RecoveryCodeActivity : AppCompatActivity() { supportActionBar!!.setDisplayHomeAsUpEnabled(true) if (savedInstanceState == null) { - supportFragmentManager.beginTransaction() - .add(R.id.fragment, RecoveryCodeOutputFragment(), "Code") - .commit() + if (viewModel.isRestore) showInput(false) + else showOutput() } } @@ -51,4 +47,22 @@ class RecoveryCodeActivity : AppCompatActivity() { } } + private fun showOutput() { + supportFragmentManager.beginTransaction() + .add(R.id.fragment, RecoveryCodeOutputFragment(), "Code") + .commit() + } + + private fun showInput(addToBackStack: Boolean) { + val tag = "Confirm" + val fragmentTransaction = supportFragmentManager.beginTransaction() + .replace(R.id.fragment, RecoveryCodeInputFragment(), tag) + if (addToBackStack) fragmentTransaction.addToBackStack(tag) + fragmentTransaction.commit() + } + + private fun isRestore(): Boolean { + return intent?.getBooleanExtra(INTENT_EXTRA_IS_RESTORE, false) ?: false + } + } diff --git a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeInputFragment.kt index 6841acb5..a8bb5115 100644 --- a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeInputFragment.kt +++ b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeInputFragment.kt @@ -5,6 +5,8 @@ import android.view.LayoutInflater import android.view.View import android.view.View.OnFocusChangeListener import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.AutoCompleteTextView import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.fragment.app.Fragment @@ -13,6 +15,7 @@ import com.stevesoltys.backup.R import com.stevesoltys.backup.isDebugBuild import io.github.novacrypto.bip39.Validation.InvalidChecksumException import io.github.novacrypto.bip39.Validation.WordNotFoundException +import io.github.novacrypto.bip39.wordlists.English import kotlinx.android.synthetic.main.fragment_recovery_code_input.* import kotlinx.android.synthetic.main.recovery_code_input.* @@ -29,15 +32,27 @@ class RecoveryCodeInputFragment : Fragment() { super.onActivityCreated(savedInstanceState) viewModel = ViewModelProviders.of(requireActivity()).get(RecoveryCodeViewModel::class.java) + val adapter = getAdapter() + for (i in 0 until WORD_NUM) { val wordLayout = getWordLayout(i) - wordLayout.editText!!.onFocusChangeListener = OnFocusChangeListener { _, focus -> + val editText = wordLayout.editText as AutoCompleteTextView + editText.onFocusChangeListener = OnFocusChangeListener { _, focus -> if (!focus) wordLayout.isErrorEnabled = false } + editText.setAdapter(adapter) } doneButton.setOnClickListener { done() } - if (isDebugBuild()) debugPreFill() + if (isDebugBuild() && !viewModel.isRestore) debugPreFill() + } + + private fun getAdapter(): ArrayAdapter { + val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1) + for (i in 0 until WORD_LIST_SIZE) { + adapter.add(English.INSTANCE.getWord(i)) + } + return adapter } private fun getInput(): List = ArrayList(WORD_NUM).apply { diff --git a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeViewModel.kt index a4653e0a..8cbc97e2 100644 --- a/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeViewModel.kt +++ b/app/src/main/java/com/stevesoltys/backup/ui/RecoveryCodeViewModel.kt @@ -13,6 +13,7 @@ import java.security.SecureRandom import java.util.* internal const val WORD_NUM = 12 +internal const val WORD_LIST_SIZE = 2048 class RecoveryCodeViewModel(application: Application) : AndroidViewModel(application) { @@ -33,6 +34,8 @@ class RecoveryCodeViewModel(application: Application) : AndroidViewModel(applica private val mRecoveryCodeSaved = MutableLiveEvent() internal val recoveryCodeSaved: LiveEvent = mRecoveryCodeSaved + internal var isRestore: Boolean = false + @Throws(WordNotFoundException::class, InvalidChecksumException::class) fun validateAndContinue(input: List) { try { diff --git a/app/src/main/res/layout/recovery_code_input.xml b/app/src/main/res/layout/recovery_code_input.xml index 03dae663..91b9f658 100644 --- a/app/src/main/res/layout/recovery_code_input.xml +++ b/app/src/main/res/layout/recovery_code_input.xml @@ -19,10 +19,11 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="spread_inside"> - @@ -40,10 +41,11 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/wordLayout1"> - @@ -61,10 +63,11 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/wordLayout2"> - @@ -82,10 +85,11 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/wordLayout3"> - @@ -103,10 +107,11 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/wordLayout4"> - @@ -124,10 +129,11 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/wordLayout5"> - @@ -146,10 +152,11 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="spread_inside"> - @@ -167,10 +174,11 @@ app:layout_constraintStart_toEndOf="@+id/wordLayout1" app:layout_constraintTop_toBottomOf="@+id/wordLayout7"> - @@ -188,10 +196,11 @@ app:layout_constraintStart_toEndOf="@+id/wordLayout1" app:layout_constraintTop_toBottomOf="@+id/wordLayout8"> - @@ -209,10 +218,11 @@ app:layout_constraintStart_toEndOf="@+id/wordLayout1" app:layout_constraintTop_toBottomOf="@+id/wordLayout9"> - @@ -230,10 +240,11 @@ app:layout_constraintStart_toEndOf="@+id/wordLayout1" app:layout_constraintTop_toBottomOf="@+id/wordLayout10"> - @@ -251,10 +262,11 @@ app:layout_constraintStart_toEndOf="@+id/wordLayout1" app:layout_constraintTop_toBottomOf="@+id/wordLayout11"> -