aboutsummaryrefslogtreecommitdiffstats
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml29
-rw-r--r--app/src/main/java/app/passwordstore/ui/dialogs/BasicBottomSheet.kt178
-rw-r--r--app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt304
-rw-r--r--app/src/main/java/app/passwordstore/ui/onboarding/activity/OnboardingActivity.kt27
-rw-r--r--app/src/main/java/app/passwordstore/ui/onboarding/fragments/CloneFragment.kt78
-rw-r--r--app/src/main/java/app/passwordstore/ui/onboarding/fragments/KeySelectionFragment.kt77
-rw-r--r--app/src/main/java/app/passwordstore/ui/onboarding/fragments/WelcomeFragment.kt33
-rw-r--r--app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt109
-rw-r--r--app/src/main/java/app/passwordstore/ui/pgp/PGPKeyList.kt169
-rw-r--r--app/src/main/java/app/passwordstore/ui/pgp/PGPKeyListActivity.kt94
-rw-r--r--app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt39
-rw-r--r--app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt173
-rw-r--r--app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt66
-rw-r--r--app/src/main/java/app/passwordstore/util/viewmodel/PGPKeyListViewModel.kt48
-rw-r--r--app/src/main/res/drawable/ic_add_48dp.xml15
-rw-r--r--app/src/main/res/drawable/ic_arrow_back_black_24dp.xml15
-rw-r--r--app/src/main/res/layout/activity_git_clone.xml94
-rw-r--r--app/src/main/res/layout/activity_onboarding.xml11
-rw-r--r--app/src/main/res/layout/activity_ssh_keygen.xml69
-rw-r--r--app/src/main/res/layout/basic_bottom_sheet.xml62
-rw-r--r--app/src/main/res/layout/fragment_clone.xml96
-rw-r--r--app/src/main/res/layout/fragment_key_selection.xml80
-rw-r--r--app/src/main/res/layout/fragment_welcome.xml67
-rw-r--r--app/src/main/res/values-de/strings.xml5
-rw-r--r--app/src/main/res/values-fr/strings.xml4
-rw-r--r--app/src/main/res/values-gl/strings.xml5
-rw-r--r--app/src/main/res/values-hdpi/dimens.xml10
-rw-r--r--app/src/main/res/values-it/strings.xml5
-rw-r--r--app/src/main/res/values-ko-rKR/strings.xml5
-rw-r--r--app/src/main/res/values-pl-rPL/strings.xml4
-rw-r--r--app/src/main/res/values-pt-rBR/strings.xml5
-rw-r--r--app/src/main/res/values-ru/strings.xml5
-rw-r--r--app/src/main/res/values-zh-rCN/strings.xml7
-rw-r--r--app/src/main/res/values/dimens.xml4
-rw-r--r--app/src/main/res/values/strings.xml5
35 files changed, 0 insertions, 1997 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0429b2ac..546bdde4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -34,11 +34,6 @@
tools:targetApi="s">
<activity
- android:name=".ui.onboarding.activity.OnboardingActivity"
- android:configChanges="orientation|screenSize"
- android:exported="false" />
-
- <activity
android:name=".ui.proxy.ProxySelectorActivity"
android:exported="false"
android:label="@string/activity_proxy_label"
@@ -73,12 +68,6 @@
tools:node="replace" />
<activity
- android:name=".ui.git.config.GitServerConfigActivity"
- android:exported="false"
- android:label="@string/title_activity_git_clone"
- android:windowSoftInputMode="adjustResize" />
-
- <activity
android:name=".ui.git.config.GitConfigActivity"
android:exported="false"
android:label="@string/title_activity_git_config"
@@ -118,16 +107,6 @@
</service>
<activity
- android:name=".ui.sshkeygen.SshKeyImportActivity"
- android:exported="false"
- android:theme="@style/NoBackgroundThemeM3"
- android:windowSoftInputMode="adjustResize" />
- <activity
- android:name=".ui.sshkeygen.SshKeyGenActivity"
- android:exported="false"
- android:label="@string/pref_ssh_keygen_title"
- android:windowSoftInputMode="adjustResize" />
- <activity
android:name=".ui.autofill.AutofillDecryptActivity"
android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />
@@ -150,14 +129,6 @@
android:exported="false"
android:theme="@style/DialogLikeThemeM3"
android:windowSoftInputMode="adjustNothing" />
- <activity
- android:name=".ui.pgp.PGPKeyImportActivity"
- android:theme="@style/NoBackgroundThemeM3" />
- <activity
- android:name=".ui.pgp.PGPKeyListActivity"
- android:exported="false"
- android:label="@string/activity_label_pgp_key_manager"
- android:parentActivityName=".ui.settings.SettingsActivity" />
</application>
<queries>
diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/BasicBottomSheet.kt b/app/src/main/java/app/passwordstore/ui/dialogs/BasicBottomSheet.kt
deleted file mode 100644
index e689f3bd..00000000
--- a/app/src/main/java/app/passwordstore/ui/dialogs/BasicBottomSheet.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.dialogs
-
-import android.content.Context
-import android.content.DialogInterface.OnDismissListener
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewTreeObserver
-import android.widget.FrameLayout
-import androidx.annotation.StringRes
-import androidx.core.view.isVisible
-import app.passwordstore.R
-import app.passwordstore.databinding.BasicBottomSheetBinding
-import app.passwordstore.util.extensions.viewBinding
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-
-/**
- * [BottomSheetDialogFragment] that exposes a simple [androidx.appcompat.app.AlertDialog] like API
- * through [BasicBottomSheet.Builder] to create a similar UI, just at the bottom of the screen.
- */
-class BasicBottomSheet
-private constructor(
- val title: String?,
- val message: String,
- val positiveButtonLabel: String?,
- val negativeButtonLabel: String?,
- val positiveButtonClickListener: View.OnClickListener?,
- val negativeButtonClickListener: View.OnClickListener?,
- val onDismissListener: OnDismissListener?,
-) : BottomSheetDialogFragment() {
-
- private val binding by viewBinding(BasicBottomSheetBinding::bind)
-
- private var behavior: BottomSheetBehavior<FrameLayout>? = null
- private val bottomSheetCallback =
- object : BottomSheetBehavior.BottomSheetCallback() {
- override fun onSlide(bottomSheet: View, slideOffset: Float) {}
-
- override fun onStateChanged(bottomSheet: View, newState: Int) {
- if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
- dismiss()
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View? {
- if (savedInstanceState != null) dismiss()
- return layoutInflater.inflate(R.layout.basic_bottom_sheet, container, false)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- view.viewTreeObserver.addOnGlobalLayoutListener(
- object : ViewTreeObserver.OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- view.viewTreeObserver.removeOnGlobalLayoutListener(this)
- val dialog = dialog as BottomSheetDialog? ?: return
- behavior = dialog.behavior
- behavior?.apply {
- state = BottomSheetBehavior.STATE_EXPANDED
- peekHeight = 0
- addBottomSheetCallback(bottomSheetCallback)
- }
- if (!title.isNullOrEmpty()) {
- binding.bottomSheetTitle.isVisible = true
- binding.bottomSheetTitle.text = title
- }
- binding.bottomSheetMessage.text = message
- if (positiveButtonClickListener != null) {
- positiveButtonLabel?.let { buttonLbl -> binding.bottomSheetOkButton.text = buttonLbl }
- binding.bottomSheetOkButton.isVisible = true
- binding.bottomSheetOkButton.setOnClickListener {
- positiveButtonClickListener.onClick(it)
- dismiss()
- }
- }
- if (negativeButtonClickListener != null) {
- binding.bottomSheetCancelButton.isVisible = true
- negativeButtonLabel?.let { buttonLbl ->
- binding.bottomSheetCancelButton.text = buttonLbl
- }
- binding.bottomSheetCancelButton.setOnClickListener {
- negativeButtonClickListener.onClick(it)
- dismiss()
- }
- }
- if (onDismissListener != null) {
- dialog.setOnDismissListener(onDismissListener)
- }
- }
- }
- )
- }
-
- override fun dismiss() {
- super.dismiss()
- behavior?.removeBottomSheetCallback(bottomSheetCallback)
- }
-
- class Builder(val context: Context) {
-
- private var title: String? = null
- private var message: String? = null
- private var positiveButtonLabel: String? = null
- private var negativeButtonLabel: String? = null
- private var positiveButtonClickListener: View.OnClickListener? = null
- private var negativeButtonClickListener: View.OnClickListener? = null
- private var onDismissListener: OnDismissListener? = null
-
- fun setTitleRes(@StringRes titleRes: Int): Builder {
- this.title = context.resources.getString(titleRes)
- return this
- }
-
- fun setTitle(title: String): Builder {
- this.title = title
- return this
- }
-
- fun setMessageRes(@StringRes messageRes: Int): Builder {
- this.message = context.resources.getString(messageRes)
- return this
- }
-
- fun setMessage(message: String): Builder {
- this.message = message
- return this
- }
-
- fun setPositiveButtonClickListener(
- buttonLabel: String? = null,
- listener: View.OnClickListener,
- ): Builder {
- this.positiveButtonClickListener = listener
- this.positiveButtonLabel = buttonLabel
- return this
- }
-
- fun setNegativeButtonClickListener(
- buttonLabel: String? = null,
- listener: View.OnClickListener,
- ): Builder {
- this.negativeButtonClickListener = listener
- this.negativeButtonLabel = buttonLabel
- return this
- }
-
- fun setOnDismissListener(onDismissListener: OnDismissListener): Builder {
- this.onDismissListener = onDismissListener
- return this
- }
-
- fun build(): BasicBottomSheet {
- require(message != null) { "Message needs to be set" }
- return BasicBottomSheet(
- title,
- message!!,
- positiveButtonLabel,
- negativeButtonLabel,
- positiveButtonClickListener,
- negativeButtonClickListener,
- onDismissListener,
- )
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt b/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt
deleted file mode 100644
index 2a1d3ebd..00000000
--- a/app/src/main/java/app/passwordstore/ui/git/config/GitServerConfigActivity.kt
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package app.passwordstore.ui.git.config
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.view.MenuItem
-import android.view.View
-import androidx.core.os.postDelayed
-import androidx.core.view.isVisible
-import androidx.core.widget.doOnTextChanged
-import androidx.lifecycle.lifecycleScope
-import app.passwordstore.R
-import app.passwordstore.data.repo.PasswordRepository
-import app.passwordstore.databinding.ActivityGitCloneBinding
-import app.passwordstore.ui.dialogs.BasicBottomSheet
-import app.passwordstore.ui.git.base.BaseGitActivity
-import app.passwordstore.util.extensions.snackbar
-import app.passwordstore.util.extensions.viewBinding
-import app.passwordstore.util.settings.AuthMode
-import app.passwordstore.util.settings.GitSettings
-import app.passwordstore.util.settings.Protocol
-import com.github.michaelbull.result.fold
-import com.github.michaelbull.result.onFailure
-import com.github.michaelbull.result.runCatching
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import logcat.LogPriority.ERROR
-import logcat.asLog
-import logcat.logcat
-
-/**
- * Activity that encompasses both the initial clone as well as editing the server config for future
- * changes.
- */
-class GitServerConfigActivity : BaseGitActivity() {
-
- private val binding by viewBinding(ActivityGitCloneBinding::inflate)
-
- private lateinit var newAuthMode: AuthMode
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val isClone = intent?.extras?.getBoolean("cloning") ?: false
- if (isClone) {
- binding.saveButton.text = getString(R.string.clone_button)
- }
- setContentView(binding.root)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
-
- newAuthMode = gitSettings.authMode
-
- binding.authModeGroup.apply {
- when (newAuthMode) {
- AuthMode.SshKey -> check(binding.authModeSshKey.id)
- AuthMode.Password -> check(binding.authModePassword.id)
- AuthMode.None -> clearChecked()
- }
- addOnButtonCheckedListener { _, _, _ ->
- if (checkedButtonIds.isEmpty()) {
- newAuthMode = AuthMode.None
- } else {
- when (checkedButtonId) {
- binding.authModeSshKey.id -> newAuthMode = AuthMode.SshKey
- binding.authModePassword.id -> newAuthMode = AuthMode.Password
- View.NO_ID -> newAuthMode = AuthMode.None
- }
- }
- }
- }
-
- binding.serverUrl.setText(
- gitSettings.url.also {
- if (it.isNullOrEmpty()) return@also
- setAuthModes(it.startsWith("http://") || it.startsWith("https://"))
- }
- )
-
- binding.serverUrl.doOnTextChanged { text, _, _, _ ->
- if (text.isNullOrEmpty()) return@doOnTextChanged
- setAuthModes(text.startsWith("http://") || text.startsWith("https://"))
- }
-
- binding.clearHostKeyButton.isVisible = gitSettings.hasSavedHostKey()
- binding.clearHostKeyButton.setOnClickListener {
- gitSettings.clearSavedHostKey()
- Snackbar.make(
- binding.root,
- getString(R.string.clear_saved_host_key_success),
- Snackbar.LENGTH_LONG,
- )
- .show()
- it.isVisible = false
- }
- binding.saveButton.setOnClickListener {
- val newUrl = binding.serverUrl.text.toString().trim()
- // If url is of type john_doe@example.org:12435/path/to/repo, then not adding `ssh://`
- // in the beginning will cause the port to be seen as part of the path. Let users know
- // about it and offer a quickfix.
- if (newUrl.contains(PORT_REGEX)) {
- if (newUrl.startsWith("https://")) {
- BasicBottomSheet.Builder(this)
- .setTitleRes(R.string.https_scheme_with_port_title)
- .setMessageRes(R.string.https_scheme_with_port_message)
- .setPositiveButtonClickListener {
- binding.serverUrl.setText(newUrl.replace(PORT_REGEX, "/"))
- }
- .build()
- .show(supportFragmentManager, "SSH_SCHEME_WARNING")
- return@setOnClickListener
- } else if (!newUrl.startsWith("ssh://")) {
- BasicBottomSheet.Builder(this)
- .setTitleRes(R.string.ssh_scheme_needed_title)
- .setMessageRes(R.string.ssh_scheme_needed_message)
- .setPositiveButtonClickListener {
- @Suppress("SetTextI18n") binding.serverUrl.setText("ssh://$newUrl")
- }
- .build()
- .show(supportFragmentManager, "SSH_SCHEME_WARNING")
- return@setOnClickListener
- }
- }
- if (newUrl.startsWith("git://")) {
- BasicBottomSheet.Builder(this)
- .setTitleRes(R.string.git_scheme_disallowed_title)
- .setMessageRes(R.string.git_scheme_disallowed_message)
- .setPositiveButtonClickListener {}
- .build()
- .show(supportFragmentManager, "SSH_SCHEME_WARNING")
- return@setOnClickListener
- }
- when (
- val updateResult =
- gitSettings.updateConnectionSettingsIfValid(
- newAuthMode = newAuthMode,
- newUrl = binding.serverUrl.text.toString().trim(),
- )
- ) {
- GitSettings.UpdateConnectionSettingsResult.FailedToParseUrl -> {
- Snackbar.make(
- binding.root,
- getString(R.string.git_server_config_save_error),
- Snackbar.LENGTH_LONG,
- )
- .show()
- }
- is GitSettings.UpdateConnectionSettingsResult.MissingUsername -> {
- when (updateResult.newProtocol) {
- Protocol.Https ->
- BasicBottomSheet.Builder(this)
- .setTitleRes(R.string.ssh_scheme_needed_title)
- .setMessageRes(R.string.git_server_config_save_missing_username_https)
- .setPositiveButtonClickListener {}
- .build()
- .show(supportFragmentManager, "HTTPS_MISSING_USERNAME")
- Protocol.Ssh ->
- BasicBottomSheet.Builder(this)
- .setTitleRes(R.string.ssh_scheme_needed_title)
- .setMessageRes(R.string.git_server_config_save_missing_username_ssh)
- .setPositiveButtonClickListener {}
- .build()
- .show(supportFragmentManager, "SSH_MISSING_USERNAME")
- }
- }
- GitSettings.UpdateConnectionSettingsResult.Valid -> {
- if (isClone && PasswordRepository.repository == null) PasswordRepository.initialize()
- if (!isClone) {
- Snackbar.make(
- binding.root,
- getString(R.string.git_server_config_save_success),
- Snackbar.LENGTH_SHORT,
- )
- .show()
- Handler(Looper.getMainLooper()).postDelayed(500) { finish() }
- } else {
- cloneRepository()
- }
- }
- is GitSettings.UpdateConnectionSettingsResult.AuthModeMismatch -> {
- val message =
- getString(
- R.string.git_server_config_save_auth_mode_mismatch,
- updateResult.newProtocol,
- updateResult.validModes.joinToString(", "),
- )
- Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
- }
- }
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- android.R.id.home -> {
- onBackPressedDispatcher.onBackPressed()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- private fun setAuthModes(isHttps: Boolean) =
- with(binding) {
- if (isHttps) {
- authModeSshKey.isVisible = false
- authModePassword.isVisible = true
- if (authModeGroup.checkedButtonId != authModePassword.id) authModeGroup.clearChecked()
- } else {
- authModeSshKey.isVisible = true
- authModePassword.isVisible = true
- if (authModeGroup.checkedButtonId == View.NO_ID) authModeGroup.check(authModeSshKey.id)
- }
- }
-
- /** Clones the repository, the directory exists, deletes it */
- private fun cloneRepository() {
- val localDir =
- requireNotNull(PasswordRepository.getRepositoryDirectory()) {
- "Repository directory must be set before cloning"
- }
- val localDirFiles = localDir.listFiles() ?: emptyArray()
- // Warn if non-empty folder unless it's a just-initialized store that has just a .git folder
- if (
- localDir.exists() &&
- localDirFiles.isNotEmpty() &&
- !(localDirFiles.size == 1 && localDirFiles[0].name == ".git")
- ) {
- MaterialAlertDialogBuilder(this)
- .setTitle(R.string.dialog_delete_title)
- .setMessage(resources.getString(R.string.dialog_delete_msg, localDir.toString()))
- .setCancelable(false)
- .setPositiveButton(R.string.dialog_delete) { dialog, _ ->
- runCatching {
- lifecycleScope.launch {
- val snackbar =
- snackbar(
- message = getString(R.string.delete_directory_progress_text),
- length = Snackbar.LENGTH_INDEFINITE,
- )
- withContext(dispatcherProvider.io()) {
- localDir.deleteRecursively()
- localDir.mkdirs()
- }
- snackbar.dismiss()
- launchGitOperation(GitOp.CLONE)
- .fold(
- success = {
- setResult(RESULT_OK)
- finish()
- },
- failure = { err -> promptOnErrorHandler(err) { finish() } },
- )
- }
- }
- .onFailure { e ->
- e.printStackTrace()
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
- dialog.cancel()
- }
- .setNegativeButton(R.string.dialog_do_not_delete) { dialog, _ -> dialog.cancel() }
- .show()
- } else {
- runCatching {
- // Silently delete & replace the lone .git folder if it exists
- if (localDir.exists() && localDirFiles.size == 1 && localDirFiles[0].name == ".git") {
- localDir.deleteRecursively()
- }
- }
- .onFailure { e ->
- logcat(ERROR) { e.asLog() }
- MaterialAlertDialogBuilder(this).setMessage(e.message).show()
- }
- lifecycleScope.launch {
- launchGitOperation(GitOp.CLONE)
- .fold(
- success = {
- setResult(RESULT_OK)
- finish()
- },
- failure = { promptOnErrorHandler(it) },
- )
- }
- }
- }
-
- companion object {
-
- private val PORT_REGEX = ":[0-9]{1,5}/".toRegex()
-
- fun createCloneIntent(context: Context): Intent {
- return Intent(context, GitServerConfigActivity::class.java).apply {
- putExtra("cloning", true)
- }
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/onboarding/activity/OnboardingActivity.kt b/app/src/main/java/app/passwordstore/ui/onboarding/activity/OnboardingActivity.kt
deleted file mode 100644
index 709c2302..00000000
--- a/app/src/main/java/app/passwordstore/ui/onboarding/activity/OnboardingActivity.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.onboarding.activity
-
-import android.os.Bundle
-import androidx.activity.addCallback
-import androidx.appcompat.app.AppCompatActivity
-import app.passwordstore.R
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class OnboardingActivity : AppCompatActivity(R.layout.activity_onboarding) {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- supportActionBar?.hide()
- val callback = onBackPressedDispatcher.addCallback(enabled = false) { finishAffinity() }
- supportFragmentManager.addOnBackStackChangedListener {
- if (supportFragmentManager.backStackEntryCount == 0) {
- callback.isEnabled = true
- }
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/CloneFragment.kt b/app/src/main/java/app/passwordstore/ui/onboarding/fragments/CloneFragment.kt
deleted file mode 100644
index a9d933b6..00000000
--- a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/CloneFragment.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.onboarding.fragments
-
-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 app.passwordstore.R
-import app.passwordstore.data.repo.PasswordRepository
-import app.passwordstore.databinding.FragmentCloneBinding
-import app.passwordstore.ui.git.config.GitServerConfigActivity
-import app.passwordstore.util.extensions.finish
-import app.passwordstore.util.extensions.performTransactionWithBackStack
-import app.passwordstore.util.extensions.sharedPrefs
-import app.passwordstore.util.extensions.unsafeLazy
-import app.passwordstore.util.extensions.viewBinding
-import app.passwordstore.util.settings.PreferenceKeys
-import com.github.michaelbull.result.onFailure
-import com.github.michaelbull.result.runCatching
-import logcat.LogPriority.ERROR
-import logcat.asLog
-import logcat.logcat
-
-class CloneFragment : Fragment(R.layout.fragment_clone) {
-
- private val binding by viewBinding(FragmentCloneBinding::bind)
-
- private val settings by unsafeLazy { requireActivity().applicationContext.sharedPrefs }
-
- private val cloneAction =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == AppCompatActivity.RESULT_OK) {
- settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
- finish()
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding.cloneRemote.setOnClickListener { cloneToHiddenDir() }
- binding.createLocal.setOnClickListener { createRepository() }
- }
-
- /** Clones a remote Git repository to the app's private directory */
- private fun cloneToHiddenDir() {
- cloneAction.launch(GitServerConfigActivity.createCloneIntent(requireContext()))
- }
-
- private fun createRepository() {
- val localDir = PasswordRepository.getRepositoryDirectory()
- runCatching {
- check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
- PasswordRepository.createRepository(localDir)
- if (!PasswordRepository.isInitialized) {
- PasswordRepository.initialize()
- }
- parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
- }
- .onFailure { e ->
- logcat(ERROR) { e.asLog() }
- if (!localDir.delete()) {
- logcat { "Failed to delete local repository: $localDir" }
- }
- finish()
- }
- }
-
- companion object {
-
- fun newInstance(): CloneFragment = CloneFragment()
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/KeySelectionFragment.kt b/app/src/main/java/app/passwordstore/ui/onboarding/fragments/KeySelectionFragment.kt
deleted file mode 100644
index 64c2e124..00000000
--- a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/KeySelectionFragment.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.onboarding.fragments
-
-import android.content.SharedPreferences
-import android.os.Bundle
-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.lifecycle.lifecycleScope
-import app.passwordstore.R
-import app.passwordstore.data.repo.PasswordRepository
-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.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) {
-
- @Inject @SettingsPreferences lateinit var settings: SharedPreferences
- @Inject lateinit var dispatcherProvider: DispatcherProvider
- 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?) {
- super.onViewCreated(view, savedInstanceState)
- binding.selectKey.setOnClickListener {
- gpgKeySelectAction.launch(PGPKeyListActivity.newSelectionActivity(requireContext()))
- }
- }
-
- companion object {
-
- fun newInstance() = KeySelectionFragment()
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/WelcomeFragment.kt b/app/src/main/java/app/passwordstore/ui/onboarding/fragments/WelcomeFragment.kt
deleted file mode 100644
index 44881c64..00000000
--- a/app/src/main/java/app/passwordstore/ui/onboarding/fragments/WelcomeFragment.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.onboarding.fragments
-
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.Keep
-import androidx.fragment.app.Fragment
-import app.passwordstore.R
-import app.passwordstore.databinding.FragmentWelcomeBinding
-import app.passwordstore.ui.settings.SettingsActivity
-import app.passwordstore.util.extensions.launchActivity
-import app.passwordstore.util.extensions.performTransactionWithBackStack
-import app.passwordstore.util.extensions.viewBinding
-
-@Keep
-class WelcomeFragment : Fragment(R.layout.fragment_welcome) {
-
- private val binding by viewBinding(FragmentWelcomeBinding::bind)
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding.letsGo.setOnClickListener {
- parentFragmentManager.performTransactionWithBackStack(CloneFragment.newInstance())
- }
- binding.settingsButton.setOnClickListener {
- requireActivity().launchActivity(SettingsActivity::class.java)
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt b/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt
deleted file mode 100644
index d276c552..00000000
--- a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyImportActivity.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-@file:Suppress("BlockingMethodInNonBlockingContext")
-
-package app.passwordstore.ui.pgp
-
-import android.os.Bundle
-import androidx.activity.result.contract.ActivityResultContracts.OpenDocument
-import androidx.appcompat.app.AppCompatActivity
-import app.passwordstore.R
-import app.passwordstore.crypto.KeyUtils.tryGetId
-import app.passwordstore.crypto.PGPKey
-import app.passwordstore.crypto.PGPKeyManager
-import app.passwordstore.crypto.errors.KeyAlreadyExistsException
-import com.github.michaelbull.result.Result
-import com.github.michaelbull.result.runCatching
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import dagger.hilt.android.AndroidEntryPoint
-import javax.inject.Inject
-import kotlinx.coroutines.runBlocking
-
-@AndroidEntryPoint
-class PGPKeyImportActivity : AppCompatActivity() {
-
- /**
- * A [ByteArray] containing the contents of the previously selected file. This is necessary for
- * the replacement case where we do not want users to have to pick the file again.
- */
- private var lastBytes: ByteArray? = null
- @Inject lateinit var keyManager: PGPKeyManager
-
- private val pgpKeyImportAction =
- registerForActivityResult(OpenDocument()) { uri ->
- runCatching {
- if (uri == null) {
- return@runCatching null
- }
- val keyInputStream =
- contentResolver.openInputStream(uri)
- ?: throw IllegalStateException("Failed to open selected file")
- val bytes = keyInputStream.use { `is` -> `is`.readBytes() }
- importKey(bytes, false)
- }
- .run(::handleImportResult)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- pgpKeyImportAction.launch(arrayOf("*/*"))
- }
-
- override fun onDestroy() {
- lastBytes = null
- super.onDestroy()
- }
-
- private fun importKey(bytes: ByteArray, replace: Boolean): PGPKey? {
- lastBytes = bytes
- val (key, error) = runBlocking { keyManager.addKey(PGPKey(bytes), replace = replace) }
- if (replace) {
- lastBytes = null
- }
- if (error != null) throw error
- return key
- }
-
- private fun handleImportResult(result: Result<PGPKey?, Throwable>) {
- if (result.isOk) {
- val key = result.value
- if (key == null) {
- setResult(RESULT_CANCELED)
- finish()
- // This return convinces Kotlin that the control flow for `key == null` definitely
- // terminates here and allows for a smart cast below.
- return
- }
- MaterialAlertDialogBuilder(this)
- .setTitle(getString(R.string.pgp_key_import_succeeded))
- .setMessage(getString(R.string.pgp_key_import_succeeded_message, tryGetId(key)))
- .setPositiveButton(android.R.string.ok) { _, _ ->
- setResult(RESULT_OK)
- finish()
- }
- .setCancelable(false)
- .show()
- } else {
- if (result.error is KeyAlreadyExistsException && lastBytes != null) {
- MaterialAlertDialogBuilder(this)
- .setTitle(getString(R.string.pgp_key_import_failed))
- .setMessage(getString(R.string.pgp_key_import_failed_replace_message))
- .setPositiveButton(R.string.dialog_yes) { _, _ ->
- handleImportResult(runCatching { importKey(lastBytes!!, replace = true) })
- }
- .setNegativeButton(R.string.dialog_no) { _, _ -> finish() }
- .setCancelable(false)
- .show()
- } else {
- MaterialAlertDialogBuilder(this)
- .setTitle(getString(R.string.pgp_key_import_failed))
- .setMessage(result.error.message)
- .setPositiveButton(android.R.string.ok) { _, _ -> finish() }
- .setCancelable(false)
- .show()
- }
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyList.kt b/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyList.kt
deleted file mode 100644
index 312f5fb0..00000000
--- a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyList.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.pgp
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.tooling.preview.Preview
-import app.passwordstore.R
-import app.passwordstore.crypto.PGPIdentifier
-import app.passwordstore.ui.compose.theme.APSTheme
-import app.passwordstore.ui.compose.theme.SpacingLarge
-import app.passwordstore.util.extensions.conditional
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
-import kotlinx.collections.immutable.toPersistentList
-
-@Composable
-fun KeyList(
- identifiers: ImmutableList<PGPIdentifier>,
- onItemClick: (identifier: PGPIdentifier) -> Unit,
- modifier: Modifier = Modifier,
- onKeySelected: ((identifier: PGPIdentifier) -> Unit)? = null,
-) {
- if (identifiers.isEmpty()) {
- Column(
- modifier = modifier.fillMaxSize(),
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- Image(
- painter = painterResource(id = R.drawable.ic_launcher_foreground),
- contentDescription = "Password Store logo",
- )
- Text(stringResource(R.string.pgp_key_manager_no_keys_guidance))
- }
- } else {
- LazyColumn(modifier = modifier) {
- items(identifiers) { identifier ->
- KeyItem(identifier = identifier, onItemClick = onItemClick, onKeySelected = onKeySelected)
- }
- }
- }
-}
-
-@Composable
-private fun KeyItem(
- identifier: PGPIdentifier,
- onItemClick: (identifier: PGPIdentifier) -> Unit,
- modifier: Modifier = Modifier,
- onKeySelected: ((identifier: PGPIdentifier) -> Unit)? = null,
-) {
- var isDeleting by remember { mutableStateOf(false) }
- DeleteConfirmationDialog(
- isDeleting = isDeleting,
- onDismiss = { isDeleting = false },
- onConfirm = {
- onItemClick(identifier)
- isDeleting = false
- },
- )
- val label =
- when (identifier) {
- is PGPIdentifier.KeyId -> identifier.id.toString()
- is PGPIdentifier.UserId -> identifier.email
- }
- Row(
- modifier =
- modifier.padding(SpacingLarge).fillMaxWidth().conditional(onKeySelected != null) {
- clickable { onKeySelected?.invoke(identifier) }
- },
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Text(
- text = label,
- modifier = Modifier.weight(1f),
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- )
- if (onKeySelected == null) {
- IconButton(onClick = { isDeleting = true }) {
- Icon(
- painter = painterResource(R.drawable.ic_delete_24dp),
- stringResource(id = R.string.delete),
- )
- }
- }
- }
-}
-
-@Suppress("NOTHING_TO_INLINE")
-@Composable
-private inline fun DeleteConfirmationDialog(
- isDeleting: Boolean,
- noinline onDismiss: () -> Unit,
- noinline onConfirm: () -> Unit,
-) {
- if (isDeleting) {
- AlertDialog(
- onDismissRequest = onDismiss,
- title = {
- Text(text = stringResource(R.string.pgp_key_manager_delete_confirmation_dialog_title))
- },
- confirmButton = {
- TextButton(onClick = onConfirm) { Text(text = stringResource(R.string.dialog_yes)) }
- },
- dismissButton = {
- TextButton(onClick = onDismiss) { Text(text = stringResource(R.string.dialog_no)) }
- },
- )
- }
-}
-
-@Preview
-@Composable
-private fun KeyListPreview() {
- APSTheme {
- Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
- KeyList(
- identifiers =
- listOfNotNull(
- PGPIdentifier.fromString("ultramicroscopicsilicovolcanoconiosis@example.com"),
- PGPIdentifier.fromString("0xB950AE2813841585"),
- )
- .toPersistentList(),
- onItemClick = {},
- )
- }
- }
-}
-
-@Preview
-@Composable
-private fun EmptyKeyListPreview() {
- APSTheme {
- Box(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
- KeyList(identifiers = persistentListOf(), onItemClick = {})
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyListActivity.kt b/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyListActivity.kt
deleted file mode 100644
index 77b9aaf1..00000000
--- a/app/src/main/java/app/passwordstore/ui/pgp/PGPKeyListActivity.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.pgp
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
-import androidx.activity.viewModels
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.FloatingActionButton
-import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Scaffold
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
-import app.passwordstore.R
-import app.passwordstore.ui.APSAppBar
-import app.passwordstore.ui.compose.theme.APSTheme
-import app.passwordstore.util.viewmodel.PGPKeyListViewModel
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class PGPKeyListActivity : ComponentActivity() {
-
- private val viewModel: PGPKeyListViewModel by viewModels()
- private val keyImportAction =
- registerForActivityResult(StartActivityForResult()) {
- if (it.resultCode == RESULT_OK) {
- viewModel.updateKeySet()
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val isSelecting = intent.extras?.getBoolean(EXTRA_KEY_SELECTION) ?: false
- setContent {
- APSTheme {
- Scaffold(
- topBar = {
- APSAppBar(
- title = stringResource(R.string.activity_label_pgp_key_manager),
- navigationIcon = painterResource(R.drawable.ic_arrow_back_black_24dp),
- onNavigationIconClick = { finish() },
- backgroundColor = MaterialTheme.colorScheme.surface,
- )
- },
- floatingActionButton = {
- FloatingActionButton(
- onClick = { keyImportAction.launch(Intent(this, PGPKeyImportActivity::class.java)) }
- ) {
- Icon(
- painter = painterResource(R.drawable.ic_add_48dp),
- stringResource(R.string.pref_import_pgp_key_title),
- )
- }
- },
- ) { 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,
- )
- }
- }
- }
- }
-
- companion object {
- const val EXTRA_SELECTED_KEY = "SELECTED_KEY"
- private const val EXTRA_KEY_SELECTION = "KEY_SELECTION_MODE"
-
- fun newSelectionActivity(context: Context): Intent {
- val intent = Intent(context, PGPKeyListActivity::class.java)
- intent.putExtra(EXTRA_KEY_SELECTION, true)
- return intent
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt
deleted file mode 100644
index 3f04e35e..00000000
--- a/app/src/main/java/app/passwordstore/ui/sshkeygen/ShowSshKeyFragment.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package app.passwordstore.ui.sshkeygen
-
-import android.app.Dialog
-import android.content.Intent
-import android.os.Bundle
-import androidx.fragment.app.DialogFragment
-import app.passwordstore.R
-import app.passwordstore.util.git.sshj.SshKey
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-
-class ShowSshKeyFragment : DialogFragment() {
-
- override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
- val activity = requireActivity()
- val publicKey = SshKey.sshPublicKey
- return MaterialAlertDialogBuilder(requireActivity()).run {
- setMessage(getString(R.string.ssh_keygen_message, publicKey))
- setTitle(R.string.your_public_key)
- setNegativeButton(R.string.ssh_keygen_later) { _, _ ->
- (activity as? SshKeyGenActivity)?.finish()
- }
- setPositiveButton(R.string.ssh_keygen_share) { _, _ ->
- val sendIntent =
- Intent().apply {
- action = Intent.ACTION_SEND
- type = "text/plain"
- putExtra(Intent.EXTRA_TEXT, publicKey)
- }
- startActivity(Intent.createChooser(sendIntent, null))
- (activity as? SshKeyGenActivity)?.finish()
- }
- create()
- }
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt
deleted file mode 100644
index 618a4be0..00000000
--- a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyGenActivity.kt
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package app.passwordstore.ui.sshkeygen
-
-import android.content.SharedPreferences
-import android.os.Bundle
-import android.security.keystore.UserNotAuthenticatedException
-import android.view.MenuItem
-import android.view.View
-import android.view.inputmethod.InputMethodManager
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.edit
-import androidx.core.content.getSystemService
-import androidx.lifecycle.lifecycleScope
-import app.passwordstore.R
-import app.passwordstore.databinding.ActivitySshKeygenBinding
-import app.passwordstore.injection.prefs.GitPreferences
-import app.passwordstore.util.auth.BiometricAuthenticator
-import app.passwordstore.util.auth.BiometricAuthenticator.Result
-import app.passwordstore.util.coroutines.DispatcherProvider
-import app.passwordstore.util.extensions.keyguardManager
-import app.passwordstore.util.extensions.viewBinding
-import app.passwordstore.util.git.sshj.SshKey
-import com.github.michaelbull.result.fold
-import com.github.michaelbull.result.runCatching
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import dagger.hilt.android.AndroidEntryPoint
-import javax.inject.Inject
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-private enum class KeyGenType(val generateKey: suspend (requireAuthentication: Boolean) -> Unit) {
- Rsa({ requireAuthentication ->
- SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Rsa, requireAuthentication)
- }),
- Ecdsa({ requireAuthentication ->
- SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Ecdsa, requireAuthentication)
- }),
- Ed25519({ requireAuthentication ->
- SshKey.generateKeystoreWrappedEd25519Key(requireAuthentication)
- }),
-}
-
-@AndroidEntryPoint
-class SshKeyGenActivity : AppCompatActivity() {
-
- private var keyGenType = KeyGenType.Ecdsa
- private val binding by viewBinding(ActivitySshKeygenBinding::inflate)
- @GitPreferences @Inject lateinit var gitPrefs: SharedPreferences
- @Inject lateinit var dispatcherProvider: DispatcherProvider
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(binding.root)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- with(binding) {
- generate.setOnClickListener {
- if (SshKey.exists) {
- MaterialAlertDialogBuilder(this@SshKeyGenActivity).run {
- setTitle(R.string.ssh_keygen_existing_title)
- setMessage(R.string.ssh_keygen_existing_message)
- setPositiveButton(R.string.ssh_keygen_existing_replace) { _, _ ->
- lifecycleScope.launch { generate() }
- }
- setNegativeButton(R.string.ssh_keygen_existing_keep) { _, _ ->
- setResult(RESULT_CANCELED)
- finish()
- }
- show()
- }
- } else {
- lifecycleScope.launch { generate() }
- }
- }
- keyTypeGroup.check(R.id.key_type_ecdsa)
- keyTypeExplanation.setText(R.string.ssh_keygen_explanation_ecdsa)
- keyTypeGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
- if (isChecked) {
- keyGenType =
- when (checkedId) {
- R.id.key_type_ed25519 -> KeyGenType.Ed25519
- R.id.key_type_ecdsa -> KeyGenType.Ecdsa
- R.id.key_type_rsa -> KeyGenType.Rsa
- else -> throw IllegalStateException("Impossible key type selection")
- }
- keyTypeExplanation.setText(
- when (keyGenType) {
- KeyGenType.Ed25519 -> R.string.ssh_keygen_explanation_ed25519
- KeyGenType.Ecdsa -> R.string.ssh_keygen_explanation_ecdsa
- KeyGenType.Rsa -> R.string.ssh_keygen_explanation_rsa
- }
- )
- }
- }
- keyRequireAuthentication.isEnabled = keyguardManager.isDeviceSecure
- keyRequireAuthentication.isChecked = keyRequireAuthentication.isEnabled
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- android.R.id.home -> {
- onBackPressedDispatcher.onBackPressed()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- private suspend fun generate() {
- binding.generate.apply {
- text = getString(R.string.ssh_key_gen_generating_progress)
- isEnabled = false
- }
- binding.generate.text = getString(R.string.ssh_key_gen_generating_progress)
- val result = runCatching {
- withContext(dispatcherProvider.io()) {
- val requireAuthentication = binding.keyRequireAuthentication.isChecked
- if (requireAuthentication) {
- val result =
- withContext(dispatcherProvider.main()) {
- suspendCoroutine { cont ->
- BiometricAuthenticator.authenticate(
- this@SshKeyGenActivity,
- R.string.biometric_prompt_title_ssh_keygen,
- ) { result ->
- // Do not cancel on failed attempts as these are handled by the
- // authenticator UI.
- if (result !is Result.Retry) cont.resume(result)
- }
- }
- }
- if (result !is Result.Success)
- throw UserNotAuthenticatedException(getString(R.string.biometric_auth_generic_failure))
- }
- keyGenType.generateKey(requireAuthentication)
- }
- }
- gitPrefs.edit { remove("ssh_key_local_passphrase") }
- binding.generate.apply {
- text = getString(R.string.ssh_keygen_generate)
- isEnabled = true
- }
- result.fold(
- success = { ShowSshKeyFragment().show(supportFragmentManager, "public_key") },
- failure = { e ->
- e.printStackTrace()
- MaterialAlertDialogBuilder(this)
- .setTitle(getString(R.string.error_generate_ssh_key))
- .setMessage(getString(R.string.ssh_key_error_dialog_text) + e.message)
- .setPositiveButton(getString(R.string.dialog_ok)) { _, _ ->
- setResult(RESULT_OK)
- finish()
- }
- .show()
- },
- )
- hideKeyboard()
- }
-
- private fun hideKeyboard() {
- val imm = getSystemService<InputMethodManager>() ?: return
- var view = currentFocus
- if (view == null) {
- view = View(this)
- }
- imm.hideSoftInputFromWindow(view.windowToken, 0)
- }
-}
diff --git a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt b/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt
deleted file mode 100644
index e4740741..00000000
--- a/app/src/main/java/app/passwordstore/ui/sshkeygen/SshKeyImportActivity.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.ui.sshkeygen
-
-import android.net.Uri
-import android.os.Bundle
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import app.passwordstore.R
-import app.passwordstore.util.git.sshj.SshKey
-import com.github.michaelbull.result.onFailure
-import com.github.michaelbull.result.runCatching
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-
-class SshKeyImportActivity : AppCompatActivity() {
-
- private val sshKeyImportAction =
- registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? ->
- if (uri == null) {
- finish()
- return@registerForActivityResult
- }
- runCatching {
- SshKey.import(uri)
- Toast.makeText(
- this,
- resources.getString(R.string.ssh_key_success_dialog_title),
- Toast.LENGTH_LONG,
- )
- .show()
- setResult(RESULT_OK)
- finish()
- }
- .onFailure { e ->
- MaterialAlertDialogBuilder(this)
- .setTitle(resources.getString(R.string.ssh_key_error_dialog_title))
- .setMessage(e.message)
- .setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ -> finish() }
- .show()
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- if (SshKey.exists) {
- MaterialAlertDialogBuilder(this).run {
- setTitle(R.string.ssh_keygen_existing_title)
- setMessage(R.string.ssh_keygen_existing_message)
- setPositiveButton(R.string.ssh_keygen_existing_replace) { _, _ -> importSshKey() }
- setNegativeButton(R.string.ssh_keygen_existing_keep) { _, _ -> finish() }
- setOnCancelListener { finish() }
- show()
- }
- } else {
- importSshKey()
- }
- }
-
- private fun importSshKey() {
- sshKeyImportAction.launch(arrayOf("*/*"))
- }
-}
diff --git a/app/src/main/java/app/passwordstore/util/viewmodel/PGPKeyListViewModel.kt b/app/src/main/java/app/passwordstore/util/viewmodel/PGPKeyListViewModel.kt
deleted file mode 100644
index 5c7429cd..00000000
--- a/app/src/main/java/app/passwordstore/util/viewmodel/PGPKeyListViewModel.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package app.passwordstore.util.viewmodel
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import app.passwordstore.crypto.KeyUtils
-import app.passwordstore.crypto.PGPIdentifier
-import app.passwordstore.crypto.PGPKeyManager
-import com.github.michaelbull.result.map
-import com.github.michaelbull.result.onSuccess
-import dagger.hilt.android.lifecycle.HiltViewModel
-import javax.inject.Inject
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
-import kotlinx.collections.immutable.toPersistentList
-import kotlinx.coroutines.launch
-
-@HiltViewModel
-class PGPKeyListViewModel @Inject constructor(private val keyManager: PGPKeyManager) : ViewModel() {
- var keys: ImmutableList<PGPIdentifier> by mutableStateOf(persistentListOf())
-
- init {
- updateKeySet()
- }
-
- fun updateKeySet() {
- viewModelScope.launch {
- keyManager
- .getAllKeys()
- .map { keys -> keys.mapNotNull { key -> KeyUtils.tryGetEmail(key) } }
- .onSuccess { keys = it.toPersistentList() }
- }
- }
-
- fun deleteKey(identifier: PGPIdentifier) {
- viewModelScope.launch {
- keyManager.removeKey(identifier)
- updateKeySet()
- }
- }
-}
diff --git a/app/src/main/res/drawable/ic_add_48dp.xml b/app/src/main/res/drawable/ic_add_48dp.xml
deleted file mode 100644
index 7192e79d..00000000
--- a/app/src/main/res/drawable/ic_add_48dp.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:tint="?attr/colorControlNormal"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
-</vector>
diff --git a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml
deleted file mode 100644
index 234364a9..00000000
--- a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:tint="?attr/colorControlNormal"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
-</vector>
diff --git a/app/src/main/res/layout/activity_git_clone.xml b/app/src/main/res/layout/activity_git_clone.xml
deleted file mode 100644
index b2f699b1..00000000
--- a/app/src/main/res/layout/activity_git_clone.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<ScrollView 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="match_parent"
- android:background="?android:attr/windowBackground"
- android:padding="@dimen/activity_horizontal_margin"
- tools:context="app.passwordstore.ui.git.config.GitServerConfigActivity">
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/label_server_url"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:hint="@string/server_url"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent">
-
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/server_url"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:imeOptions="actionNext"
- android:inputType="textWebEmailAddress"
- android:nextFocusForward="@id/auth_mode_group" />
-
- </com.google.android.material.textfield.TextInputLayout>
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/label_auth_mode"
- style="@style/TextAppearance.MaterialComponents.Headline6"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="16dp"
- android:text="@string/connection_mode"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/label_server_url" />
-
- <com.google.android.material.button.MaterialButtonToggleGroup
- android:id="@+id/auth_mode_group"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/label_auth_mode"
- app:singleSelection="true">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/auth_mode_ssh_key"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/connection_mode_ssh_key" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/auth_mode_password"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/connection_mode_basic_authentication" />
-
- </com.google.android.material.button.MaterialButtonToggleGroup>
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/save_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/crypto_save"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/auth_mode_group" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/clear_host_key_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/clear_saved_host_key"
- android:visibility="gone"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/auth_mode_group"
- tools:visibility="visible" />
- </androidx.constraintlayout.widget.ConstraintLayout>
-</ScrollView>
diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml
deleted file mode 100644
index d1153f5d..00000000
--- a/app/src/main/res/layout/activity_onboarding.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_first_run"
- android:name="app.passwordstore.ui.onboarding.fragments.WelcomeFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:tag="welcome_fragment" />
diff --git a/app/src/main/res/layout/activity_ssh_keygen.xml b/app/src/main/res/layout/activity_ssh_keygen.xml
deleted file mode 100644
index 49548b36..00000000
--- a/app/src/main/res/layout/activity_ssh_keygen.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<ScrollView 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">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- android:paddingRight="@dimen/activity_horizontal_margin">
-
- <com.google.android.material.button.MaterialButtonToggleGroup
- android:id="@+id/key_type_group"
- style="@style/TextAppearance.MaterialComponents.Headline1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:selectionRequired="true"
- app:singleSelection="true">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/key_type_rsa"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ssh_keygen_label_rsa" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/key_type_ecdsa"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ssh_keygen_label_ecdsa" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/key_type_ed25519"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ssh_keygen_label_ed25519" />
- </com.google.android.material.button.MaterialButtonToggleGroup>
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/key_type_explanation"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp" />
-
- <com.google.android.material.checkbox.MaterialCheckBox
- android:id="@+id/key_require_authentication"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/ssh_keygen_require_authentication" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/generate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:layout_marginTop="8dp"
- android:text="@string/ssh_keygen_generate" />
- </LinearLayout>
-</ScrollView>
diff --git a/app/src/main/res/layout/basic_bottom_sheet.xml b/app/src/main/res/layout/basic_bottom_sheet.xml
deleted file mode 100644
index b45fc9c9..00000000
--- a/app/src/main/res/layout/basic_bottom_sheet.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2024 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"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="24dp">
-
- <com.google.android.material.textview.MaterialTextView
- android:id="@+id/bottom_sheet_title"
- style="?attr/textAppearanceTitleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="8dp"
- android:visibility="gone"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:text="Bottom sheet title" />
-
- <com.google.android.material.textview.MaterialTextView
- android:id="@+id/bottom_sheet_message"
- style="?attr/textAppearanceBodyLarge"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginTop="16dp"
- android:layout_marginEnd="8dp"
- app:layout_constraintTop_toBottomOf="@id/bottom_sheet_title"
- tools:text="A long body of text that serves as the bottom sheet message" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/bottom_sheet_cancel_button"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:layout_marginEnd="8dp"
- android:text="@string/dialog_cancel"
- android:visibility="gone"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message"
- tools:visibility="visible" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/bottom_sheet_ok_button"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
- android:layout_marginEnd="8dp"
- android:text="@string/dialog_ok"
- android:visibility="gone"
- app:layout_constraintEnd_toStartOf="@id/bottom_sheet_cancel_button"
- app:layout_constraintTop_toBottomOf="@id/bottom_sheet_message"
- tools:visibility="visible" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/fragment_clone.xml b/app/src/main/res/layout/fragment_clone.xml
deleted file mode 100644
index 9cb34761..00000000
--- a/app/src/main/res/layout/fragment_clone.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<ScrollView 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="?android:attr/colorBackground">
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/app_icon"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_marginStart="32dp"
- android:layout_marginTop="@dimen/onboarding_icon_margin_top"
- 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="0dp"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_marginStart="@dimen/fab_compat_margin"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/app_name"
- android:textAppearance="?attr/textAppearanceTitleLarge"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="@id/app_icon"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/app_icon"
- app:layout_constraintTop_toTopOf="@+id/app_icon" />
-
- <TextView
- android:id="@+id/repo_type"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="48dp"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/select_n_repository_type"
- android:textAppearance="?attr/textAppearanceHeadlineSmall"
- 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/tv_repo_type_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/onboarding_desc_margin_top"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/select_repo_type_text"
- android:textAppearance="?attr/textAppearanceTitleSmall"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@id/repo_type"
- app:layout_constraintTop_toBottomOf="@id/repo_type" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/clone_remote"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginTop="@dimen/onboarding_button_margin_top"
- android:layout_marginEnd="16dp"
- android:maxWidth="300dp"
- android:minWidth="100dp"
- android:text="@string/clone_remote_repo"
- app:layout_constraintBottom_toTopOf="@id/create_local"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/tv_repo_type_text" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/create_local"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="128dp"
- android:maxWidth="300dp"
- android:minWidth="100dp"
- android:text="@string/create_local_repo"
- app:layout_constraintEnd_toEndOf="@id/clone_remote"
- app:layout_constraintStart_toStartOf="@id/clone_remote"
- app:layout_constraintTop_toBottomOf="@id/clone_remote" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
-</ScrollView>
diff --git a/app/src/main/res/layout/fragment_key_selection.xml b/app/src/main/res/layout/fragment_key_selection.xml
deleted file mode 100644
index b9141960..00000000
--- a/app/src/main/res/layout/fragment_key_selection.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2024 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="?android:attr/colorBackground"
- 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="@dimen/onboarding_icon_margin_top"
- 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="0dp"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_marginStart="@dimen/fab_compat_margin"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/app_name"
- android:textAppearance="?attr/textAppearanceTitleLarge"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="@id/app_icon"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/app_icon"
- app:layout_constraintTop_toTopOf="@+id/app_icon" />
-
- <TextView
- android:id="@+id/gpg_key"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="48dp"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/select_gpg_key_title"
- android:textAppearance="?attr/textAppearanceHeadlineSmall"
- 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="@dimen/onboarding_desc_margin_top"
- android:layout_marginEnd="16dp"
- android:text="@string/select_gpg_key_message"
- android:textAppearance="?attr/textAppearanceTitleSmall"
- 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>
diff --git a/app/src/main/res/layout/fragment_welcome.xml b/app/src/main/res/layout/fragment_welcome.xml
deleted file mode 100644
index c68cd56a..00000000
--- a/app/src/main/res/layout/fragment_welcome.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- ~ Copyright © 2014-2024 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="?android:attr/colorBackground"
- android:orientation="vertical">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/settings_button"
- style="?attr/borderlessButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/activity_horizontal_margin"
- android:text="@string/action_settings"
- android:textAllCaps="true"
- android:textColor="?attr/colorOnBackground"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/app_icon"
- android:layout_width="100dp"
- android:layout_height="100dp"
- android:contentDescription="@string/app_icon_hint"
- android:src="@mipmap/ic_launcher"
- android:transitionName="transition_first_app_icon"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- 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_marginTop="16dp"
- android:text="@string/app_name"
- android:textAppearance="?attr/textAppearanceTitleLarge"
- android:textStyle="bold"
- android:transitionName="transition_first_run_app_name"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/app_icon" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/lets_go"
- 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/let_s_go"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/app_name" />
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index a9fa4b00..493391a8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -171,7 +171,6 @@
<string name="show_password_pref_title">Zeige das Passwort.</string>
<string name="show_password_pref_summary">Soll das entschlüsselte Passwort sichtbar sein? Dies deaktiviert nicht das Kopieren.</string>
<string name="send_plaintext_password_to">Passwort unverschlüsselt senden an…</string>
- <string name="app_icon_hint">App-Icon</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Eintrag zum Ausfüllen auswählen</string>
<string name="oreo_autofill_strict_domain_search">Phishing-resistente Suche</string>
@@ -283,16 +282,12 @@
<string name="git_break_out_of_detached_success">Es gab einen Konflikt während des Rebase-Prozesses. Ihr lokaler Branch %1$s wurde auf einen anderen Branch namens %2$s gepusht.\nNutzen Sie diesen Branch, um den Konflikt auf dem Server zu beheben</string>
<string name="git_break_out_of_detached_unneeded">Das Repository befindet sich nicht im Rebase-Prozess; es ist nicht nötig, den Zustand auf einen weiteren Branch zu pushen.</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">Um fortzufahren, muss ein PGP-Schlüssel ausgewählt werden.</string>
<string name="folder_creation_err_file_exists">Dateiname bereits vergeben</string>
<string name="folder_creation_err_folder_exists">Ein Ordner mit diesem Namen existiert bereits.</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Los geht\'s</string>
- <string name="select_n_repository_type">Wählen Sie einen \nRepository-Typ.</string>
<string name="select_repo_type_text">Wählen Sie aus, ob Sie ein lokales Repository anlegen, oder ein Remote-Repository klonen möchten.</string>
<string name="clone_remote_repo">Remote-Repository klonen</string>
<string name="create_local_repo">Lokales Repository anlegen</string>
- <string name="select_gpg_key_title">Wählen Sie einen\nPGP-Schlüssel.</string>
<string name="select_gpg_key_message">Wählen einen PGP-Schlüssel, mit dem der Store initialisiert werden soll.</string>
<string name="gpg_key_select">Schlüssel auswählen</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index d41048f8..d2a469e7 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -159,7 +159,6 @@
<string name="show_password_pref_title">Montrer le mot de passe</string>
<string name="show_password_pref_summary">Contrôle la visibilité des mots de passe une fois déchifrées, ceci n\'empêche pas de copier le mot de passe</string>
<string name="send_plaintext_password_to">Envoyer le mot de passe en clair via…</string>
- <string name="app_icon_hint">Icône de l\'application</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Sélectionnez l\'entrée à remplir</string>
<string name="oreo_autofill_strict_domain_search">Recherche protégée contre le hameçonnage</string>
@@ -271,12 +270,9 @@
<string name="folder_creation_err_file_exists">Un fichier portant ce nom existe déjà</string>
<string name="folder_creation_err_folder_exists">Un dossier portant ce nom existe déjà</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Allons-y</string>
- <string name="select_n_repository_type">Sélectionnez le type de dépôt \n</string>
<string name="select_repo_type_text">Sélectionnez si vous voulez créer un dépôt local ou cloner un dépôt distant.</string>
<string name="clone_remote_repo">Cloner le dépôt distant</string>
<string name="create_local_repo">Créer un dépôt local</string>
- <string name="select_gpg_key_title">Sélectionnez\nla clé GPG</string>
<string name="select_gpg_key_message">Sélectionnez une clé GPG pour initialiser votre magasin</string>
<string name="gpg_key_select">Sélectionner la clé</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 3fb321e7..ad4c663d 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -172,7 +172,6 @@
<string name="show_password_pref_title">Mostrar contrasinal</string>
<string name="show_password_pref_summary">Controlar a visibilidade do contrasinal unha vez descifrado, esto non desactiva o copiado do contrasinal</string>
<string name="send_plaintext_password_to">Enviar contrasinal como texto plano usando…</string>
- <string name="app_icon_hint">Icona da app</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Elixe o elemento a completar</string>
<string name="oreo_autofill_strict_domain_search">Busca resistente ao phishing</string>
@@ -285,16 +284,12 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid
<string name="git_break_out_of_detached_success">Hai un conflito ó realizar rebase. A túa póla local %1$s fixo push a outra póla chamada %2$s\nUsa esta póla para resolver o conflito na túa computadora</string>
<string name="git_break_out_of_detached_unneeded">O repositorio non cambiou de base, non é preciso cambiar a outra póla</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">Hai que elexir unha chave GPG para continuar</string>
<string name="folder_creation_err_file_exists">Xa existe un ficheiro con ese nome</string>
<string name="folder_creation_err_folder_exists">Xa existe un cartafol con ese nome</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Imos alá</string>
- <string name="select_n_repository_type">Escolle \nTipo de repositorio</string>
<string name="select_repo_type_text">Escolle se queres crear un repositorio local ou clonar un remoto.</string>
<string name="clone_remote_repo">Clonar repositorio remoto</string>
<string name="create_local_repo">Crear repositorio local</string>
- <string name="select_gpg_key_title">Elixe\nChave\nGPG</string>
<string name="select_gpg_key_message">Elixe a chave GPG coa que queres iniciar a almacenaxe</string>
<string name="gpg_key_select">Elixe chave</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-hdpi/dimens.xml b/app/src/main/res/values-hdpi/dimens.xml
deleted file mode 100644
index f9a452aa..00000000
--- a/app/src/main/res/values-hdpi/dimens.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<resources>
- <dimen name="onboarding_icon_margin_top">50dp</dimen>
- <dimen name="onboarding_button_margin_top">70dp</dimen>
- <dimen name="onboarding_desc_margin_top">48dp</dimen>
-</resources>
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 72bcb009..8edc9d3d 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -154,7 +154,6 @@
<string name="show_password_pref_title">Mostra la password</string>
<string name="show_password_pref_summary">Controlla la visibilità delle password una volta decrittografate. Questo non disabilita la copia negli appunti.</string>
<string name="send_plaintext_password_to">Invia password come testo semplice usando…</string>
- <string name="app_icon_hint">Icona app</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Seleziona la voce da compilare</string>
<string name="oreo_autofill_strict_domain_search">Ricerca resistente al phishing</string>
@@ -265,12 +264,9 @@
<string name="folder_creation_err_file_exists">Un file con quel nome esiste già</string>
<string name="folder_creation_err_folder_exists">Una cartella con quel nome esiste già</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Andiamo</string>
- <string name="select_n_repository_type">Seleziona \nTipo di repository</string>
<string name="select_repo_type_text">Seleziona se vuoi creare un repo locale o clonarne uno remoto.</string>
<string name="clone_remote_repo">Clona repo remoto</string>
<string name="create_local_repo">Crea repo locale</string>
- <string name="select_gpg_key_title">Seleziona\nChiave\nGPG</string>
<string name="select_gpg_key_message">Seleziona una chiave GPG con cui inizializzare il tuo archivio</string>
<string name="gpg_key_select">Seleziona chiave</string>
<!-- SSH port validation -->
@@ -315,7 +311,6 @@
<string name="invalid_gpg_id">Trovato .gpg-id, ma contiene un ID chiave, un\'impronta o un ID utente non valido</string>
<string name="short_gpg_id">Trovato .gpg-id, ma contiene un ID esadecimale corto, che non è supportato</string>
<string name="git_pull_merge_fail_error">Merge non riuscito, il repository è in conflitto.</string>
- <string name="gpg_key_select_mandatory">Per proseguire devi selezionare una chiave GPG</string>
<string name="git_scheme_disallowed_title">Il protocollo git:// è sconsigliato</string>
<string name="git_scheme_disallowed_message">Il protocollo git di git-daemon non cifra il trasporto e non è adatto a operazioni sicure.</string>
<string name="activity_proxy_label">Impostazioni proxy</string>
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index 3efbed68..aaa438dd 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -167,7 +167,6 @@
<string name="show_password_pref_title">비밀번호 표시하기</string>
<string name="show_password_pref_summary">암호가 해독된 후 비밀번호의 공개 여부를 제어합니다. 그렇다고 해서 클립보드 복사가 비활성화되지는 않습니다.</string>
<string name="send_plaintext_password_to">비밀번호를 일반 텍스트로 보내는 중…</string>
- <string name="app_icon_hint">앱 아이콘</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">입력할 항목 선택</string>
<string name="oreo_autofill_strict_domain_search">피싱 방지 검색</string>
@@ -281,16 +280,12 @@ username@example.com:…</string>
<string name="git_break_out_of_detached_success">리베이스하는 동안 충돌이 발생했습니다. 로컬 %1$s 브랜치가 %2$s이라는 다른 브랜치로 푸시되었습니다.\n이 브랜치를 사용하여 컴퓨터에서 충돌을 해결하세요.</string>
<string name="git_break_out_of_detached_unneeded">이 저장소는 리베이스가 아니므로 다른 브랜치로 푸시할 필요가 없습니다.</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">계속하려면 GPG 키를 선택해야 합니다</string>
<string name="folder_creation_err_file_exists">동일한 이름의 파일이 이미 있습니다</string>
<string name="folder_creation_err_folder_exists">이 이름의 폴더가 이미 존재합니다</string>
<!-- Onboarding flow -->
- <string name="let_s_go">시작합니다!</string>
- <string name="select_n_repository_type">저장소 유형 \n선택</string>
<string name="select_repo_type_text">로컬 리포지토리를 만들 것인지, 원격 리포지토리를 복제할 것인지 선택합니다.</string>
<string name="clone_remote_repo">원격 저장소 복제</string>
<string name="create_local_repo">로컬 저장소 생성</string>
- <string name="select_gpg_key_title">GPG 키\n선택</string>
<string name="select_gpg_key_message">스토어를 초기화할 GPG 키를 선택합니다</string>
<string name="gpg_key_select">키 선택</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 65f14ce5..815cf41c 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -168,7 +168,6 @@
<string name="show_password_pref_title">Pokaż hasło</string>
<string name="show_password_pref_summary">Kontroluj widoczność haseł po odszyfrowaniu. To nie wyłącza kopiowania do schowka.</string>
<string name="send_plaintext_password_to">Wyślij niezaszyfrowane hasło, używając…</string>
- <string name="app_icon_hint">Ikona aplikacji</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Wybierz wpis do wypełnienia</string>
<string name="oreo_autofill_strict_domain_search">Wyszukiwanie odporne na wyłudzenie</string>
@@ -278,12 +277,9 @@
<string name="folder_creation_err_file_exists">Plik o tej nazwie już istnieje</string>
<string name="folder_creation_err_folder_exists">Folder o tej nazwie już istnieje</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Zaczynajmy</string>
- <string name="select_n_repository_type">Wybierz \ntyp repozytorium</string>
<string name="select_repo_type_text">Zaznacz, czy chcesz utworzyć lokalne repozytorium, czy sklonować zdalne repozytorium.</string>
<string name="clone_remote_repo">Klonuj zdalne repozytorium</string>
<string name="create_local_repo">Utwórz lokalne repozytorium</string>
- <string name="select_gpg_key_title">Wybierz\nklucz GPG</string>
<string name="select_gpg_key_message">Wybierz klucz GPG, którym zainicjować swoje repozytorium</string>
<string name="gpg_key_select">Wybierz klucz</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 86ebfb24..a1d1a38c 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -171,7 +171,6 @@
<string name="show_password_pref_title">Mostrar a senha</string>
<string name="show_password_pref_summary">Controle a visibilidade das senhas quando descriptografadas. Isso não desativa a cópia para a área de transferência.</string>
<string name="send_plaintext_password_to">Enviar senha como texto simples usando…</string>
- <string name="app_icon_hint">Ícone do aplicativo</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Selecione a entrada para preencher</string>
<string name="oreo_autofill_strict_domain_search">Busca resistente a phishing</string>
@@ -283,16 +282,12 @@
<string name="git_break_out_of_detached_success">Houve um conflito ao executar o rebase. Houve um push de branch %1$s local para outra branch chamada %2$s\n Use esta branch para resolver o conflito no seu computador</string>
<string name="git_break_out_of_detached_unneeded">O repositório não está sendo rebased, não há necessidade de fazer push para outro branch</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">A seleção de uma chave GPG é necessária para prosseguir</string>
<string name="folder_creation_err_file_exists">Já existe um arquivo com esse nome</string>
<string name="folder_creation_err_folder_exists">Já existe uma pasta com esse nome</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Vamos lá</string>
- <string name="select_n_repository_type">Selecione \nTipo de Repositório</string>
<string name="select_repo_type_text">Selecione se você quer criar um repositório local ou clonar um repositório remoto.</string>
<string name="clone_remote_repo">Clonar repositório remoto</string>
<string name="create_local_repo">Criar repositório local</string>
- <string name="select_gpg_key_title">Selecione\nChave GPG</string>
<string name="select_gpg_key_message">Selecione uma chave GPG para inicializar o armazenamento com</string>
<string name="gpg_key_select">Selecione a chave</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 8873b34f..d0934a4d 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -164,7 +164,6 @@
<string name="show_password_pref_title">Показывать пароли</string>
<string name="show_password_pref_summary">Видимость расшифрованных паролей, не влияет на копирование в буфер.</string>
<string name="send_plaintext_password_to">Поделиться паролем в открытом виде с помощью</string>
- <string name="app_icon_hint">Иконка приложения</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Выберите запись для заполнения</string>
<string name="oreo_autofill_strict_domain_search">Устойчивый к фишингу поиск</string>
@@ -275,16 +274,12 @@
<string name="git_break_out_of_detached_success">При попытке rebase\'а возник конфликт. Ваша локальная ветка %1$s была запушена %2$s\n Используйте эту ветку для разрешения конфликтов с помощью компьютера</string>
<string name="git_break_out_of_detached_unneeded">Не нужно пушить в другую ветку - rebase не был запущен</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">Для продолжения необходимо выбрать GPG-ключ</string>
<string name="folder_creation_err_file_exists">Файл с таким именем уже существует</string>
<string name="folder_creation_err_folder_exists">Папка с таким именем уже существует</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Поехали</string>
- <string name="select_n_repository_type">Выберите \nтип репозитория</string>
<string name="select_repo_type_text">Создать локальное хранилище, или клонировать удаленный репозиторий.</string>
<string name="clone_remote_repo">Клонировать удаленный репозиторий</string>
<string name="create_local_repo">Создать локальное хранилище</string>
- <string name="select_gpg_key_title">Выберите\nGPG ключ</string>
<string name="select_gpg_key_message">Выберите GPG ключ для инициализации хранилища</string>
<string name="gpg_key_select">Выберите ключ</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 746b74d2..ddcdda1a 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -172,7 +172,6 @@
<string name="show_password_pref_title">显示密码</string>
<string name="show_password_pref_summary">控制密码解密后的可见性,这不会禁用复制到剪贴板</string>
<string name="send_plaintext_password_to">使用明文发送密码…</string>
- <string name="app_icon_hint">应用图标</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">选择要填写的条目</string>
<string name="oreo_autofill_strict_domain_search">防钓鱼搜索</string>
@@ -285,18 +284,12 @@ personal.com</string>
使用此分支解决计算机上的冲突</string>
<string name="git_break_out_of_detached_unneeded">存储库没有变更,无需推送到另一个分支</string>
<!-- GPG key selection in folder creation -->
- <string name="gpg_key_select_mandatory">选择一个 GPG 密钥才能继续</string>
<string name="folder_creation_err_file_exists">该名称的文件已存在</string>
<string name="folder_creation_err_folder_exists">该名称的文件夹已存在</string>
<!-- Onboarding flow -->
- <string name="let_s_go">开始配置</string>
- <string name="select_n_repository_type">选择仓库模式</string>
<string name="select_repo_type_text">你是要同步仓库还是现在创建一个仓库呢?</string>
<string name="clone_remote_repo">同步远程仓库</string>
<string name="create_local_repo">创建本地仓库</string>
- <string name="select_gpg_key_title">选择
-GPG
-密钥</string>
<string name="select_gpg_key_message">选择一个GPG密钥来初始化您的仓库</string>
<string name="gpg_key_select">选择密钥</string>
<!-- SSH port validation -->
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index e5d7ff94..8351a358 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -7,9 +7,5 @@
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
- <dimen name="fab_compat_margin">16dp</dimen>
<dimen name="normal_margin">8dp</dimen>
- <dimen name="onboarding_icon_margin_top">100dp</dimen>
- <dimen name="onboarding_button_margin_top">140dp</dimen>
- <dimen name="onboarding_desc_margin_top">48dp</dimen>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index dc8069b8..59ba74cb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -199,7 +199,6 @@
<string name="show_password_pref_title">Show the password</string>
<string name="show_password_pref_summary">Control the visibility of the passwords once decrypted. This does not disable copying to clipboard.</string>
<string name="send_plaintext_password_to">Send password as plaintext using…</string>
- <string name="app_icon_hint">App icon</string>
<!-- Oreo Autofill -->
<string name="oreo_autofill_select_and_fill_into">Select entry to fill into</string>
@@ -321,17 +320,13 @@
<string name="git_break_out_of_detached_unneeded">The repository is not rebasing, no need to push to another branch</string>
<!-- 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_folder_exists">A folder by that name already exists</string>
<!-- Onboarding flow -->
- <string name="let_s_go">Let\'s Go</string>
- <string name="select_n_repository_type">Select \nRepository Type</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="create_local_repo">Create Local Repo</string>
- <string name="select_gpg_key_title">Select\nGPG Key</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>