feat: wire up key import functionality

This commit is contained in:
Harsh Shandilya 2023-04-06 18:14:35 +05:30
parent a96f24ac96
commit c4edf7f0e6
No known key found for this signature in database
6 changed files with 105 additions and 25 deletions

View file

@ -9,7 +9,9 @@ import android.os.Bundle
import androidx.activity.addCallback import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import app.passwordstore.R import app.passwordstore.R
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OnboardingActivity : AppCompatActivity(R.layout.activity_onboarding) { class OnboardingActivity : AppCompatActivity(R.layout.activity_onboarding) {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

View file

@ -5,21 +5,68 @@
package app.passwordstore.ui.onboarding.fragments package app.passwordstore.ui.onboarding.fragments
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.databinding.FragmentKeySelectionBinding import app.passwordstore.databinding.FragmentKeySelectionBinding
import app.passwordstore.injection.prefs.SettingsPreferences
import app.passwordstore.ui.pgp.PGPKeyListActivity
import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.commitChange
import app.passwordstore.util.extensions.finish
import app.passwordstore.util.extensions.snackbar
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.settings.PreferenceKeys
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import javax.inject.Inject
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@AndroidEntryPoint
class KeySelectionFragment : Fragment(R.layout.fragment_key_selection) { class KeySelectionFragment : Fragment(R.layout.fragment_key_selection) {
@Inject @SettingsPreferences lateinit var settings: SharedPreferences
@Inject lateinit var dispatcherProvider: DispatcherProvider
private val binding by viewBinding(FragmentKeySelectionBinding::bind) private val binding by viewBinding(FragmentKeySelectionBinding::bind)
private val gpgKeySelectAction =
registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == AppCompatActivity.RESULT_OK) {
val data = result.data ?: return@registerForActivityResult
val selectedKey =
data.getStringExtra(PGPKeyListActivity.EXTRA_SELECTED_KEY)
?: return@registerForActivityResult
lifecycleScope.launch {
withContext(dispatcherProvider.io()) {
val gpgIdentifierFile = File(PasswordRepository.getRepositoryDirectory(), ".gpg-id")
gpgIdentifierFile.writeText(selectedKey)
}
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
requireActivity()
.commitChange(getString(R.string.git_commit_gpg_id, getString(R.string.app_name)))
finish()
}
} else {
requireActivity()
.snackbar(
message = getString(R.string.gpg_key_select_mandatory),
length = Snackbar.LENGTH_LONG,
)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.selectKey.setOnClickListener { binding.selectKey.setOnClickListener {
// TODO(msfjarvis): Restore this functionality gpgKeySelectAction.launch(PGPKeyListActivity.newSelectionActivity(requireContext()))
// gpgKeySelectAction.launch(Intent(requireContext(), GetKeyIdsActivity::class.java))
} }
} }

View file

@ -1,6 +1,7 @@
package app.passwordstore.ui.pgp package app.passwordstore.ui.pgp
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -28,12 +29,14 @@ import androidx.compose.ui.unit.dp
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.GpgIdentifier
import app.passwordstore.ui.compose.theme.APSThemePreview import app.passwordstore.ui.compose.theme.APSThemePreview
import app.passwordstore.util.extensions.conditional
@Composable @Composable
fun KeyList( fun KeyList(
identifiers: List<GpgIdentifier>, identifiers: List<GpgIdentifier>,
onItemClick: (identifier: GpgIdentifier) -> Unit, onItemClick: (identifier: GpgIdentifier) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onKeySelected: ((identifier: GpgIdentifier) -> Unit)? = null,
) { ) {
if (identifiers.isEmpty()) { if (identifiers.isEmpty()) {
Column( Column(
@ -50,7 +53,7 @@ fun KeyList(
} else { } else {
LazyColumn(modifier = modifier) { LazyColumn(modifier = modifier) {
items(identifiers) { identifier -> items(identifiers) { identifier ->
KeyItem(identifier = identifier, onItemClick = onItemClick) KeyItem(identifier = identifier, onItemClick = onItemClick, onKeySelected = onKeySelected)
} }
} }
} }
@ -61,6 +64,7 @@ private fun KeyItem(
identifier: GpgIdentifier, identifier: GpgIdentifier,
onItemClick: (identifier: GpgIdentifier) -> Unit, onItemClick: (identifier: GpgIdentifier) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onKeySelected: ((identifier: GpgIdentifier) -> Unit)? = null,
) { ) {
var isDeleting by remember { mutableStateOf(false) } var isDeleting by remember { mutableStateOf(false) }
DeleteConfirmationDialog( DeleteConfirmationDialog(
@ -77,16 +81,21 @@ private fun KeyItem(
is GpgIdentifier.UserId -> identifier.email is GpgIdentifier.UserId -> identifier.email
} }
Row( Row(
modifier = modifier.padding(16.dp).fillMaxWidth(), modifier =
modifier.padding(16.dp).fillMaxWidth().conditional(onKeySelected != null) {
clickable { onKeySelected?.invoke(identifier) }
},
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Text(text = label) Text(text = label)
IconButton(onClick = { isDeleting = true }) { if (onKeySelected == null) {
Icon( IconButton(onClick = { isDeleting = true }) {
painter = painterResource(R.drawable.ic_delete_24dp), Icon(
stringResource(id = R.string.delete) painter = painterResource(R.drawable.ic_delete_24dp),
) stringResource(id = R.string.delete)
)
}
} }
} }
} }

View file

@ -1,5 +1,6 @@
package app.passwordstore.ui.pgp package app.passwordstore.ui.pgp
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@ -12,12 +13,10 @@ import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.ui.APSAppBar import app.passwordstore.ui.APSAppBar
import app.passwordstore.ui.compose.theme.APSTheme import app.passwordstore.ui.compose.theme.APSTheme
@ -39,6 +38,7 @@ class PGPKeyListActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val isSelecting = intent.extras?.getBoolean(EXTRA_KEY_SELECTION) ?: false
setContent { setContent {
val context = LocalContext.current val context = LocalContext.current
APSTheme(colors = decideColorScheme(context)) { APSTheme(colors = decideColorScheme(context)) {
@ -62,21 +62,33 @@ class PGPKeyListActivity : ComponentActivity() {
} }
} }
) { paddingValues -> ) { paddingValues ->
PGPKeyList(viewModel = viewModel, modifier = Modifier.padding(paddingValues)) KeyList(
identifiers = viewModel.keys,
onItemClick = viewModel::deleteKey,
modifier = Modifier.padding(paddingValues),
onKeySelected =
if (isSelecting) {
{ identifier ->
val result = Intent()
result.putExtra(EXTRA_SELECTED_KEY, identifier.toString())
setResult(RESULT_OK, result)
finish()
}
} else null,
)
} }
} }
} }
} }
}
@Composable companion object {
fun PGPKeyList( const val EXTRA_SELECTED_KEY = "SELECTED_KEY"
modifier: Modifier = Modifier, private const val EXTRA_KEY_SELECTION = "KEY_SELECTION_MODE"
viewModel: PGPKeyListViewModel = viewModel(),
) { fun newSelectionActivity(context: Context): Intent {
KeyList( val intent = Intent(context, PGPKeyListActivity::class.java)
identifiers = viewModel.keys, intent.putExtra(EXTRA_KEY_SELECTION, true)
onItemClick = viewModel::deleteKey, return intent
modifier = modifier, }
) }
} }

View file

@ -22,6 +22,7 @@ import android.view.View
import android.view.autofill.AutofillManager import android.view.autofill.AutofillManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
@ -153,3 +154,12 @@ fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): Ap
@Suppress("DEPRECATION") getApplicationInfo(packageName, flags) @Suppress("DEPRECATION") getApplicationInfo(packageName, flags)
} }
} }
/** Allows conditionally applying the given [modifier] if [isEnabled] is `true`. */
fun Modifier.conditional(isEnabled: Boolean, modifier: Modifier.() -> Modifier): Modifier {
return if (isEnabled) {
then(modifier())
} else {
this
}
}

View file

@ -43,6 +43,7 @@
<string name="git_commit_remove_text">Remove %1$s from store.</string> <string name="git_commit_remove_text">Remove %1$s from store.</string>
<string name="git_commit_move_text">Rename %1$s to %2$s.</string> <string name="git_commit_move_text">Rename %1$s to %2$s.</string>
<string name="git_commit_move_multiple_text">Move multiple passwords to %1$s.</string> <string name="git_commit_move_multiple_text">Move multiple passwords to %1$s.</string>
<string name="git_commit_gpg_id">Initialize GPG IDs in %1$s.</string>
<!-- PGPHandler --> <!-- PGPHandler -->
<string name="clipboard_copied_text">Copied to clipboard</string> <string name="clipboard_copied_text">Copied to clipboard</string>
@ -318,9 +319,8 @@
<string name="git_break_out_of_detached_success">There was a conflict when trying to rebase. Your local %1$s branch was pushed to another branch named %2$s\n Use this branch to resolve conflict on your computer</string> <string name="git_break_out_of_detached_success">There was a conflict when trying to rebase. Your local %1$s branch was pushed to another branch named %2$s\n Use this branch to resolve conflict on your computer</string>
<string name="git_break_out_of_detached_unneeded">The repository is not rebasing, no need to push to another branch</string> <string name="git_break_out_of_detached_unneeded">The repository is not rebasing, no need to push to another branch</string>
<!-- OpenKeychain not installed -->
<!-- GPG key selection in folder creation --> <!-- GPG key selection in folder creation -->
<string name="gpg_key_select_mandatory">Selecting a GPG key is necessary to proceed</string>
<string name="folder_creation_err_file_exists">A file by that name already exists</string> <string name="folder_creation_err_file_exists">A file by that name already exists</string>
<string name="folder_creation_err_folder_exists">A folder by that name already exists</string> <string name="folder_creation_err_folder_exists">A folder by that name already exists</string>