Add key selection step to onboarding flow

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-10-01 20:46:21 +05:30
parent 944efee00e
commit 14b51d5808
No known key found for this signature in database
GPG key ID: 366D7BBAD1031E80
4 changed files with 156 additions and 9 deletions

View file

@ -0,0 +1,66 @@
/*
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
* SPDX-License-Identifier: GPL-3.0-only
*/
package com.zeapo.pwdstore.ui.onboarding.fragments
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.crypto.GetKeyIdsActivity
import com.zeapo.pwdstore.databinding.FragmentKeySelectionBinding
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.commitChange
import com.zeapo.pwdstore.utils.finish
import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.viewBinding
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.msfjarvis.openpgpktx.util.OpenPgpApi
class KeySelectionFragment : Fragment(R.layout.fragment_key_selection) {
private val settings by lazy { requireActivity().applicationContext.sharedPrefs }
private val binding by viewBinding(FragmentKeySelectionBinding::bind)
private val gpgKeySelectAction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == AppCompatActivity.RESULT_OK) {
result.data?.getStringArrayExtra(OpenPgpApi.EXTRA_KEY_IDS)?.let { keyIds ->
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val gpgIdentifierFile = File(PasswordRepository.getRepositoryDirectory(), ".gpg-id")
gpgIdentifierFile.writeText(keyIds.joinToString("\n"))
}
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
requireActivity().commitChange(getString(
R.string.git_commit_gpg_id,
getString(R.string.app_name)
))
}
}
} else {
throw IllegalStateException("Failed to initialize repository state.")
}
finish()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.selectKey.setOnClickListener { gpgKeySelectAction.launch(Intent(requireContext(), GetKeyIdsActivity::class.java)) }
}
companion object {
fun newInstance() = KeySelectionFragment()
}
}

View file

@ -14,6 +14,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.e
import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -26,14 +27,14 @@ import com.zeapo.pwdstore.utils.finish
import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.getString
import com.zeapo.pwdstore.utils.isPermissionGranted import com.zeapo.pwdstore.utils.isPermissionGranted
import com.zeapo.pwdstore.utils.listFilesRecursively import com.zeapo.pwdstore.utils.listFilesRecursively
import com.zeapo.pwdstore.utils.performTransactionWithBackStack
import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import java.io.File import java.io.File
class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) { class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
private val firstRunActivity by lazy { requireActivity() } private val settings by lazy { requireActivity().applicationContext.sharedPrefs }
private val settings by lazy { firstRunActivity.applicationContext.sharedPrefs }
private val binding by viewBinding(FragmentRepoLocationBinding::bind) private val binding by viewBinding(FragmentRepoLocationBinding::bind)
private val sortOrder: PasswordRepository.PasswordSortOrder private val sortOrder: PasswordRepository.PasswordSortOrder
get() = PasswordRepository.PasswordSortOrder.getSortOrder(settings) get() = PasswordRepository.PasswordSortOrder.getSortOrder(settings)
@ -151,18 +152,14 @@ class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
if (!PasswordRepository.isInitialized) { if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize() PasswordRepository.initialize()
} }
if (File(localDir.absolutePath + "/.gpg-id").createNewFile()) { parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
} else {
throw IllegalStateException("Failed to initialize repository state.")
}
}.onFailure { e -> }.onFailure { e ->
e.printStackTrace() e(e)
if (!localDir.delete()) { if (!localDir.delete()) {
d { "Failed to delete local repository: $localDir" } d { "Failed to delete local repository: $localDir" }
} }
finish()
} }
finish()
} }
private fun initializeRepositoryInfo() { private fun initializeRepositoryInfo() {

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
~ SPDX-License-Identifier: GPL-3.0-only
-->
<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"
android:background="?attr/colorPrimary"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/app_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="32dp"
android:layout_marginTop="100dp"
android:contentDescription="@string/app_icon_hint"
android:src="@mipmap/ic_launcher"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginStart="16dp"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
android:textColor="@color/color_control_normal"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/app_icon"
app:layout_constraintStart_toEndOf="@id/app_icon"
app:layout_constraintTop_toTopOf="@+id/app_icon" />
<TextView
android:id="@+id/gpg_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="@string/select_gpg_key_title"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4"
android:textColor="@color/color_control_normal"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="@id/app_icon"
app:layout_constraintTop_toBottomOf="@id/app_icon" />
<TextView
android:id="@+id/gpg_key_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:layout_marginEnd="16dp"
android:text="@string/select_gpg_key_message"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="@color/color_control_normal"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/gpg_key"
app:layout_constraintTop_toBottomOf="@id/gpg_key" />
<com.google.android.material.button.MaterialButton
android:id="@+id/select_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="48dp"
android:layout_marginEnd="16dp"
android:maxWidth="300dp"
android:minWidth="100dp"
android:text="@string/gpg_key_select"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gpg_key_text" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -400,6 +400,9 @@
<string name="select_repo_type_text">Select if you want to create a local repo or clone a remote repo.</string> <string name="select_repo_type_text">Select if you want to create a local repo or clone a remote repo.</string>
<string name="clone_remote_repo">Clone Remote Repo</string> <string name="clone_remote_repo">Clone Remote Repo</string>
<string name="create_local_repo">Create Local Repo</string> <string name="create_local_repo">Create Local Repo</string>
<string name="select_gpg_key_title">Select\nGPG\nKey</string>
<string name="select_gpg_key_message">Select a GPG key to initialize your store with</string>
<string name="gpg_key_select">Select key</string>
<!-- SSH port validation --> <!-- SSH port validation -->
<string name="ssh_scheme_needed_title">Potentially incorrect URL</string> <string name="ssh_scheme_needed_title">Potentially incorrect URL</string>