diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-03 14:18:42 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-03 14:18:42 +0100 |
| commit | 2911783653f699e41a07bb9536460d9d0a1c3b70 (patch) | |
| tree | b68692cc80bc0b463f671779ac3642c2ad8976cc /app/src | |
| parent | b4e1002735486bf56b67b8233c2c2abfa875eed4 (diff) | |
| download | HardenedPass-2911783653f699e41a07bb9536460d9d0a1c3b70.tar.gz HardenedPass-2911783653f699e41a07bb9536460d9d0a1c3b70.zip | |
refactor(app): remove the legacy setup screens
The Compose welcome, git server, SSH key, key generation and PGP keys
screens replace OnboardingActivity with its fragments,
GitServerConfigActivity with BasicBottomSheet, SshKeyImportActivity,
SshKeyGenActivity, ShowSshKeyFragment, PGPKeyImportActivity and
PGPKeyListActivity with KeyList and PGPKeyListViewModel, their layouts
and manifest entries, and ui:compose's APSAppBar. Resources only they
used are removed too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src')
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> |
