diff options
| author | Danilo M. <danix@danix.xyz> | 2026-11-03 18:50:51 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-11-03 18:50:51 +0100 |
| commit | cebdf8dd4720388d56d9af537fe81fd927e51779 (patch) | |
| tree | 27555259681fae142becbdaeb5d6183dc72ab712 | |
| parent | 1d8b44dd387c3b78f97e1510bb5c3c719c7d4015 (diff) | |
| download | HardenedPass-cebdf8dd4720388d56d9af537fe81fd927e51779.tar.gz HardenedPass-cebdf8dd4720388d56d9af537fe81fd927e51779.zip | |
refactor(app): remove the legacy settings screens
The Compose settings, git config, commit log and proxy screens replace
SettingsActivity with its six preference providers, GitConfigActivity
with TextInputDialog, GitLogActivity with its adapter and GitLogModel,
and ProxySelectorActivity, with their layouts and manifest entries.
The dead OpenKeystore key ID row goes with its string, the Android 14
platform bug summary and normal_margin lose their last users, and the
setup intent loses the two starts only Settings used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 files changed, 22 insertions, 1786 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 546bdde4..677321c7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,12 +34,6 @@ tools:targetApi="s"> <activity - android:name=".ui.proxy.ProxySelectorActivity" - android:exported="false" - android:label="@string/activity_proxy_label" - android:windowSoftInputMode="adjustResize" /> - - <activity android:name=".ui.main.LaunchActivity" android:configChanges="orientation|screenSize" android:exported="true" @@ -67,23 +61,6 @@ android:windowSoftInputMode="stateAlwaysHidden" tools:node="replace" /> - <activity - android:name=".ui.git.config.GitConfigActivity" - android:exported="false" - android:label="@string/title_activity_git_config" - android:windowSoftInputMode="adjustResize" /> - - <activity - android:name=".ui.git.log.GitLogActivity" - android:exported="false" - android:label="@string/title_activity_git_log" /> - - <activity - android:name=".ui.settings.SettingsActivity" - android:exported="false" - android:label="@string/action_settings" - android:parentActivityName=".ui.main.MainActivity" /> - <service android:name=".util.services.ClipboardService" android:exported="false" diff --git a/app/src/main/java/app/passwordstore/ui/dialogs/TextInputDialog.kt b/app/src/main/java/app/passwordstore/ui/dialogs/TextInputDialog.kt deleted file mode 100644 index 58c39968..00000000 --- a/app/src/main/java/app/passwordstore/ui/dialogs/TextInputDialog.kt +++ /dev/null @@ -1,71 +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.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.view.WindowManager -import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment -import androidx.fragment.app.setFragmentResult -import app.passwordstore.databinding.DialogTextInputBinding -import app.passwordstore.util.extensions.finish -import app.passwordstore.util.extensions.unsafeLazy -import com.google.android.material.dialog.MaterialAlertDialogBuilder - -/** - * General purpose [DialogFragment] that can be used to accept a single text input. It provides a - * configurable title and text input hint through the instantiation helper at - * [TextInputDialog.newInstance]. Typical usage would look something like this: - * ```kotlin - * val dialog = TextInputDialog.newInstance(getString(R.string.dialog_title), getString(R.string.dialog_hint)) - * dialog.show(supportFragmentManager, "text_input_dialog") - * dialog.setFragmentResultListener(TextInputDialog.REQUEST_KEY) { _, bundle -> - * doSomething(bundle.getString(TextInputDialog.BUNDLE_KEY_TEXT)) - * } - * ``` - */ -class TextInputDialog : DialogFragment() { - private val binding by unsafeLazy { DialogTextInputBinding.inflate(layoutInflater) } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val builder = MaterialAlertDialogBuilder(requireContext()) - builder.setView(binding.root) - builder.setTitle(arguments?.getString(BUNDLE_KEY_TITLE)) - arguments?.getString(BUNDLE_KEY_HINT)?.let { hint -> - binding.textInputLayout.isHintEnabled = true - binding.textInputLayout.isHintAnimationEnabled = true - binding.textInputLayout.hint = hint - } - builder.setPositiveButton(android.R.string.ok) { dialogInterface, _ -> - setFragmentResult(REQUEST_KEY, bundleOf(BUNDLE_KEY_TEXT to binding.editText.text.toString())) - dialogInterface.dismiss() - } - val dialog = builder.create() - dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) - return dialog - } - - override fun onCancel(dialog: DialogInterface) { - super.onCancel(dialog) - finish() - } - - companion object { - const val REQUEST_KEY = "text_input_dialog" - const val BUNDLE_KEY_TEXT = "text" - private const val BUNDLE_KEY_TITLE = "title" - private const val BUNDLE_KEY_HINT = "hint" - - fun newInstance(title: String, hint: String? = null): TextInputDialog { - val args = bundleOf(BUNDLE_KEY_TITLE to title, BUNDLE_KEY_HINT to hint) - val dialog = TextInputDialog() - dialog.arguments = args - return dialog - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/git/config/GitConfigActivity.kt b/app/src/main/java/app/passwordstore/ui/git/config/GitConfigActivity.kt deleted file mode 100644 index 695029d4..00000000 --- a/app/src/main/java/app/passwordstore/ui/git/config/GitConfigActivity.kt +++ /dev/null @@ -1,176 +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.os.Bundle -import android.os.Handler -import android.os.Looper -import android.util.Patterns -import android.view.MenuItem -import androidx.core.os.postDelayed -import androidx.fragment.app.setFragmentResultListener -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.databinding.ActivityGitConfigBinding -import app.passwordstore.ui.dialogs.TextInputDialog -import app.passwordstore.ui.git.base.BaseGitActivity -import app.passwordstore.ui.git.log.GitLogActivity -import app.passwordstore.util.extensions.asLog -import app.passwordstore.util.extensions.launchActivity -import app.passwordstore.util.extensions.viewBinding -import com.github.michaelbull.result.fold -import com.github.michaelbull.result.getOrElse -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 logcat.LogPriority.ERROR -import logcat.logcat -import org.eclipse.jgit.lib.Constants -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.lib.RepositoryState - -class GitConfigActivity : BaseGitActivity() { - - private val binding by viewBinding(ActivityGitConfigBinding::inflate) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - - if (gitSettings.authorName.isEmpty()) binding.gitUserName.requestFocus() - else binding.gitUserName.setText(gitSettings.authorName) - binding.gitUserEmail.setText(gitSettings.authorEmail) - setupTools() - binding.saveButton.setOnClickListener { - val email = binding.gitUserEmail.text.toString().trim() - val name = binding.gitUserName.text.toString().trim() - if (!email.matches(Patterns.EMAIL_ADDRESS.toRegex())) { - MaterialAlertDialogBuilder(this) - .setMessage(getString(R.string.invalid_email_dialog_text)) - .setPositiveButton(getString(R.string.dialog_ok), null) - .show() - } else { - gitSettings.authorEmail = email - gitSettings.authorName = name - Snackbar.make( - binding.root, - getString(R.string.git_server_config_save_success), - Snackbar.LENGTH_SHORT, - ) - .show() - Handler(Looper.getMainLooper()).postDelayed(500) { finish() } - } - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - onBackPressedDispatcher.onBackPressed() - true - } - else -> super.onOptionsItemSelected(item) - } - } - - /** Sets up the UI components of the tools section. */ - private fun setupTools() { - val repo = PasswordRepository.repository - if (repo != null) { - binding.gitHeadStatus.text = headStatusMsg(repo) - // enable the abort button only if we're rebasing or merging - val needsAbort = - repo.repositoryState.isRebasing || repo.repositoryState == RepositoryState.MERGING - binding.gitAbortRebase.isEnabled = needsAbort - binding.gitAbortRebase.alpha = if (needsAbort) 1.0f else 0.5f - } - binding.gitLog.setOnClickListener { - runCatching { launchActivity(GitLogActivity::class.java) } - .onFailure { ex -> logcat(ERROR) { ex.asLog("Failed to start GitLogActivity") } } - } - binding.gitAbortRebase.setOnClickListener { abortRebase() } - binding.gitResetToRemote.setOnClickListener { resetToRemote() } - binding.gitGc.setOnClickListener { - lifecycleScope.launch { - launchGitOperation(GitOp.GC) - .fold( - success = ::finishOnSuccessHandler, - failure = { err -> promptOnErrorHandler(err) { finish() } }, - ) - } - } - } - - private fun resetToRemote() { - val dialog = - TextInputDialog.newInstance(getString(R.string.git_utils_reset_remote_branch_title)) - dialog.show(supportFragmentManager, "BRANCH_INPUT_DIALOG") - dialog.setFragmentResultListener(TextInputDialog.REQUEST_KEY) { _, bundle -> - val result = bundle.getString(TextInputDialog.BUNDLE_KEY_TEXT) - if (!result.isNullOrEmpty()) { - remoteBranch = result - lifecycleScope.launch { - launchGitOperation(GitOp.RESET) - .fold( - success = ::finishOnSuccessHandler, - failure = { err -> promptOnErrorHandler(err) { finish() } }, - ) - } - } - } - } - - private fun abortRebase() { - lifecycleScope.launch { - launchGitOperation(GitOp.BREAK_OUT_OF_DETACHED) - .fold( - success = { - val branch = PasswordRepository.getCurrentBranch() - MaterialAlertDialogBuilder(this@GitConfigActivity).run { - setTitle(resources.getString(R.string.git_abort_and_push_title)) - setMessage( - resources.getString( - R.string.git_break_out_of_detached_success, - branch, - "conflicting-$branch-...", - ) - ) - setOnDismissListener { finish() } - setPositiveButton(resources.getString(R.string.dialog_ok)) { _, _ -> } - show() - } - }, - failure = { err -> promptOnErrorHandler(err) { finish() } }, - ) - } - } - - /** - * Returns a user-friendly message about the current state of HEAD. - * - * The state is recognized to be either pointing to a branch or detached. - */ - private fun headStatusMsg(repo: Repository): String { - return runCatching { - val headRef = repo.findRef(Constants.HEAD) - if (headRef.isSymbolic) { - val branchName = headRef.target.name - val shortBranchName = Repository.shortenRefName(branchName) - getString(R.string.git_head_on_branch, shortBranchName) - } else { - val commitHash = headRef.objectId.abbreviate(8).name() - getString(R.string.git_head_detached, commitHash) - } - } - .getOrElse { ex -> - logcat(ERROR) { "Error getting HEAD reference\n${ex}" } - getString(R.string.git_head_missing) - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/git/log/GitLogActivity.kt b/app/src/main/java/app/passwordstore/ui/git/log/GitLogActivity.kt deleted file mode 100644 index a72881d1..00000000 --- a/app/src/main/java/app/passwordstore/ui/git/log/GitLogActivity.kt +++ /dev/null @@ -1,49 +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.log - -import android.os.Bundle -import android.view.MenuItem -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import app.passwordstore.databinding.ActivityGitLogBinding -import app.passwordstore.ui.git.base.BaseGitActivity -import app.passwordstore.util.extensions.viewBinding - -/** - * Displays the repository's git commits in git-log fashion. - * - * It provides basic information about each commit by way of a non-interactive RecyclerView. - */ -class GitLogActivity : BaseGitActivity() { - - private val binding by viewBinding(ActivityGitLogBinding::inflate) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - createRecyclerView() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - finish() - true - } - else -> super.onOptionsItemSelected(item) - } - } - - private fun createRecyclerView() { - binding.gitLogRecyclerView.apply { - setHasFixedSize(true) - addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL)) - adapter = GitLogAdapter() - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/git/log/GitLogAdapter.kt b/app/src/main/java/app/passwordstore/ui/git/log/GitLogAdapter.kt deleted file mode 100644 index 95186188..00000000 --- a/app/src/main/java/app/passwordstore/ui/git/log/GitLogAdapter.kt +++ /dev/null @@ -1,65 +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.log - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import app.passwordstore.databinding.GitLogRowLayoutBinding -import app.passwordstore.util.git.GitCommit -import app.passwordstore.util.git.GitLogModel -import java.time.Instant -import java.time.ZoneId -import java.time.format.DateTimeFormatter -import java.time.format.FormatStyle -import java.util.Locale -import logcat.LogPriority.ERROR -import logcat.logcat - -private fun shortHash(hash: String): String { - return hash.substring(0 until 8) -} - -private fun stringFrom(date: Instant): String { - return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) - .withLocale(Locale.getDefault()) - .withZone(ZoneId.systemDefault()) - .format(date) -} - -/** @see GitLogActivity */ -class GitLogAdapter : RecyclerView.Adapter<GitLogAdapter.ViewHolder>() { - - private val model = GitLogModel() - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(parent.context) - val binding = GitLogRowLayoutBinding.inflate(inflater, parent, false) - return ViewHolder(binding) - } - - override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { - val commit = model.get(position) - if (commit == null) { - logcat(ERROR) { "There is no git commit for view holder at position $position." } - return - } - viewHolder.bind(commit) - } - - override fun getItemCount() = model.size - - class ViewHolder(private val binding: GitLogRowLayoutBinding) : - RecyclerView.ViewHolder(binding.root) { - - fun bind(commit: GitCommit) = - with(binding) { - gitLogRowMessage.text = commit.shortMessage - gitLogRowHash.text = shortHash(commit.hash) - gitLogRowTime.text = stringFrom(commit.time) - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt b/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt index 4d618cca..75062f6b 100644 --- a/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt +++ b/app/src/main/java/app/passwordstore/ui/main/MainActivity.kt @@ -258,13 +258,13 @@ class MainActivity : BaseGitActivity() { private const val EXTRA_SETUP = "SETUP" /** - * Opens one setup screen in an instance of its own, for callers outside the graph (legacy - * Settings, autofill, git operations) and for those waiting for a result (the entry view's + * Opens one setup screen in an instance of its own, for callers outside the graph (autofill, + * the git operations' "No SSH key" dialog) and for those waiting for a result (the entry view's * missing key). Leaving that screen finishes the instance; the PGP keys screen sets RESULT_OK * once a key was imported. */ - // ponytail: a second instance instead of in-graph navigation, until steps 6 and 7 move - // Settings and autofill into the graph. + // ponytail: a second instance instead of in-graph navigation, until step 7 moves autofill + // into the graph and the git operations' dialogs become Compose dialogs. fun setupIntent(context: Context, start: SetupStart): Intent = Intent(context, MainActivity::class.java).putExtra(EXTRA_SETUP, start.name) } diff --git a/app/src/main/java/app/passwordstore/ui/proxy/ProxySelectorActivity.kt b/app/src/main/java/app/passwordstore/ui/proxy/ProxySelectorActivity.kt deleted file mode 100644 index cb5fbf40..00000000 --- a/app/src/main/java/app/passwordstore/ui/proxy/ProxySelectorActivity.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 - */ - -package app.passwordstore.ui.proxy - -import android.content.SharedPreferences -import android.net.InetAddresses -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.util.Patterns -import android.view.MenuItem -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.edit -import androidx.core.os.postDelayed -import androidx.core.widget.doOnTextChanged -import app.passwordstore.R -import app.passwordstore.databinding.ActivityProxySelectorBinding -import app.passwordstore.injection.prefs.ProxyPreferences -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.extensions.viewBinding -import app.passwordstore.util.proxy.ProxyUtils -import app.passwordstore.util.settings.GitSettings -import app.passwordstore.util.settings.PreferenceKeys -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject - -@AndroidEntryPoint -class ProxySelectorActivity : AppCompatActivity() { - - @Inject lateinit var gitSettings: GitSettings - @ProxyPreferences @Inject lateinit var proxyPrefs: SharedPreferences - @Inject lateinit var proxyUtils: ProxyUtils - private val binding by viewBinding(ActivityProxySelectorBinding::inflate) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - with(binding) { - proxyHost.setText(proxyPrefs.getString(PreferenceKeys.PROXY_HOST)) - proxyUser.setText(proxyPrefs.getString(PreferenceKeys.PROXY_USERNAME)) - proxyPrefs - .getInt(PreferenceKeys.PROXY_PORT, -1) - .takeIf { it != -1 } - ?.let { proxyPort.setText("$it") } - proxyPassword.setText(proxyPrefs.getString(PreferenceKeys.PROXY_PASSWORD)) - save.setOnClickListener { saveSettings() } - proxyHost.doOnTextChanged { text, _, _, _ -> - if (text != null) { - proxyHost.error = - if (isNumericAddress(text) || text.matches(WEB_ADDRESS_REGEX)) { - null - } else { - getString(R.string.invalid_proxy_url) - } - } - } - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - android.R.id.home -> { - onBackPressedDispatcher.onBackPressed() - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - private fun isNumericAddress(text: CharSequence): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - InetAddresses.isNumericAddress(text as String) - } else { - @Suppress("DEPRECATION") Patterns.IP_ADDRESS.matcher(text).matches() - } - } - - private fun saveSettings() { - proxyPrefs.edit { - binding.proxyHost.text - ?.toString() - ?.takeIf { it.isNotEmpty() } - .let { gitSettings.proxyHost = it } - binding.proxyUser.text - ?.toString() - ?.takeIf { it.isNotEmpty() } - .let { gitSettings.proxyUsername = it } - binding.proxyPort.text - ?.toString() - ?.takeIf { it.isNotEmpty() } - ?.let { gitSettings.proxyPort = it.toInt() } - binding.proxyPassword.text - ?.toString() - ?.takeIf { it.isNotEmpty() } - .let { gitSettings.proxyPassword = it } - } - proxyUtils.setDefaultProxy() - Handler(Looper.getMainLooper()).postDelayed(500) { finish() } - } - - private companion object { - private val WEB_ADDRESS_REGEX = Patterns.WEB_URL.toRegex() - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/AutofillSettings.kt b/app/src/main/java/app/passwordstore/ui/settings/AutofillSettings.kt deleted file mode 100644 index afb2a691..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/AutofillSettings.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import android.annotation.SuppressLint -import android.content.Intent -import android.provider.Settings -import androidx.appcompat.widget.AppCompatTextView -import androidx.core.net.toUri -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver -import app.passwordstore.BuildConfig -import app.passwordstore.R -import app.passwordstore.util.extensions.autofillManager -import app.passwordstore.util.settings.PreferenceKeys -import com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel -import com.github.androidpasswordstore.autofillparser.getInstalledBrowsersWithAutofillSupportLevel -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.editText -import de.Maxr1998.modernpreferences.helpers.onClick -import de.Maxr1998.modernpreferences.helpers.switch -import de.Maxr1998.modernpreferences.preferences.SwitchPreference - -class AutofillSettings(private val activity: FragmentActivity) : SettingsProvider { - - private val isAutofillServiceEnabled: Boolean - get() { - return activity.autofillManager?.hasEnabledAutofillServices() == true - } - - private fun showAutofillDialog(pref: SwitchPreference) { - val observer = LifecycleEventObserver { _, event -> - when (event) { - Lifecycle.Event.ON_RESUME -> { - pref.checked = isAutofillServiceEnabled - } - else -> {} - } - } - MaterialAlertDialogBuilder(activity).run { - setTitle(R.string.pref_autofill_enable_title) - @SuppressLint("InflateParams") - val layout = activity.layoutInflater.inflate(R.layout.oreo_autofill_instructions, null) - val supportedBrowsersTextView = layout.findViewById<AppCompatTextView>(R.id.supportedBrowsers) - supportedBrowsersTextView.text = - getInstalledBrowsersWithAutofillSupportLevel(context).joinToString(separator = "\n") { - val appLabel = it.first - val supportDescription = - when (it.second) { - BrowserAutofillSupportLevel.None -> - activity.getString(R.string.oreo_autofill_no_support) - BrowserAutofillSupportLevel.FlakyFill -> - activity.getString(R.string.oreo_autofill_flaky_fill_support) - BrowserAutofillSupportLevel.PasswordFill -> - activity.getString(R.string.oreo_autofill_password_fill_support) - BrowserAutofillSupportLevel.PasswordFillAndSaveIfNoAccessibility -> - activity.getString( - R.string.oreo_autofill_password_fill_and_conditional_save_support - ) - BrowserAutofillSupportLevel.GeneralFill -> - activity.getString(R.string.oreo_autofill_general_fill_support) - BrowserAutofillSupportLevel.GeneralFillAndSave -> - activity.getString(R.string.oreo_autofill_general_fill_and_save_support) - } - "$appLabel: $supportDescription" - } - setView(layout) - setPositiveButton(R.string.dialog_ok) { _, _ -> - val intent = - Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE).apply { - data = "package:${BuildConfig.APPLICATION_ID}".toUri() - } - activity.startActivity(intent) - } - setNegativeButton(R.string.dialog_cancel, null) - setOnDismissListener { pref.checked = isAutofillServiceEnabled } - activity.lifecycle.addObserver(observer) - show() - } - } - - override fun provideSettings(builder: PreferenceScreen.Builder) { - builder.apply { - switch(PreferenceKeys.AUTOFILL_ENABLE) { - titleRes = R.string.pref_autofill_enable_title - defaultValue = isAutofillServiceEnabled - onClick { - if (isAutofillServiceEnabled) { - activity.autofillManager?.disableAutofillServices() - } else { - showAutofillDialog(this) - } - false - } - } - editText(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME) { - dependency = PreferenceKeys.AUTOFILL_ENABLE - titleRes = R.string.preference_default_username_title - summaryProvider = { activity.getString(R.string.preference_default_username_summary) } - } - editText(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES) { - dependency = PreferenceKeys.AUTOFILL_ENABLE - titleRes = R.string.preference_custom_public_suffixes_title - summaryProvider = { activity.getString(R.string.preference_custom_public_suffixes_summary) } - textInputHintRes = R.string.preference_custom_public_suffixes_hint - } - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/GeneralSettings.kt b/app/src/main/java/app/passwordstore/ui/settings/GeneralSettings.kt deleted file mode 100644 index e3594269..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/GeneralSettings.kt +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import android.content.pm.ShortcutManager -import android.os.Build -import androidx.core.content.edit -import androidx.core.content.getSystemService -import androidx.fragment.app.FragmentActivity -import app.passwordstore.R -import app.passwordstore.util.auth.BiometricAuthenticator -import app.passwordstore.util.auth.BiometricAuthenticator.Result -import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.settings.DirectoryStructure -import app.passwordstore.util.settings.PreferenceKeys -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.onClick -import de.Maxr1998.modernpreferences.helpers.singleChoice -import de.Maxr1998.modernpreferences.helpers.switch -import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem - -class GeneralSettings(private val activity: FragmentActivity) : SettingsProvider { - - override fun provideSettings(builder: PreferenceScreen.Builder) { - builder.apply { - val themeValues = activity.resources.getStringArray(R.array.app_theme_values) - val themeOptions = activity.resources.getStringArray(R.array.app_theme_options) - val themeItems = - themeValues.zip(themeOptions).map { SelectionItem(it.first, it.second, null) } - singleChoice(PreferenceKeys.APP_THEME, themeItems) { - initialSelection = activity.resources.getString(R.string.app_theme_def) - titleRes = R.string.pref_app_theme_title - } - - val values = activity.resources.getStringArray(R.array.directory_structure_values) - val titles = activity.resources.getStringArray(R.array.directory_structure_entries) - val items = values.zip(titles).map { SelectionItem(it.first, it.second, null) } - singleChoice(PreferenceKeys.DIRECTORY_STRUCTURE, items) { - initialSelection = DirectoryStructure.DEFAULT.value - titleRes = R.string.pref_directory_structure - } - - val sortValues = activity.resources.getStringArray(R.array.sort_order_values) - val sortOptions = activity.resources.getStringArray(R.array.sort_order_entries) - val sortItems = sortValues.zip(sortOptions).map { SelectionItem(it.first, it.second, null) } - singleChoice(PreferenceKeys.SORT_ORDER, sortItems) { - initialSelection = sortValues[0] - titleRes = R.string.pref_sort_order_title - } - - switch(PreferenceKeys.DISABLE_SYNC_ACTION) { - titleRes = R.string.pref_disable_sync_on_pull_title - summaryRes = R.string.pref_disable_sync_on_pull_summary - defaultValue = false - } - - switch(PreferenceKeys.FILTER_RECURSIVELY) { - titleRes = R.string.pref_recursive_filter_title - summaryRes = R.string.pref_recursive_filter_summary - defaultValue = true - } - - switch(PreferenceKeys.SEARCH_ON_START) { - titleRes = R.string.pref_search_on_start_title - summaryRes = R.string.pref_search_on_start_summary - defaultValue = false - } - - switch(PreferenceKeys.SHOW_HIDDEN_CONTENTS) { - titleRes = R.string.pref_show_hidden_title - summaryRes = R.string.pref_show_hidden_summary - defaultValue = false - } - - // See https://github.com/android-password-store/Android-Password-Store/issues/2802 - val disableAuth = Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE - val canAuthenticate = BiometricAuthenticator.canAuthenticate(activity) - switch(PreferenceKeys.BIOMETRIC_AUTH_2) { - titleRes = R.string.pref_biometric_auth_title - defaultValue = false - enabled = !disableAuth - summaryRes = - if (disableAuth) R.string.pref_biometric_auth_summary_disabled_platform - else if (canAuthenticate) R.string.pref_biometric_auth_summary - else R.string.pref_biometric_auth_summary_error - onClick { - enabled = false - val isChecked = checked - activity.sharedPrefs.edit { - BiometricAuthenticator.authenticate(activity) { result -> - when (result) { - is Result.Success -> { - // Apply the changes - putBoolean(PreferenceKeys.BIOMETRIC_AUTH_2, checked) - enabled = true - } - is Result.Retry -> {} - else -> { - // If any error occurs, revert back to the previous - // state. This - // catch-all clause includes the cancellation case. - putBoolean(PreferenceKeys.BIOMETRIC_AUTH_2, !checked) - checked = !isChecked - enabled = true - } - } - } - } - activity.getSystemService<ShortcutManager>()?.apply { - removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList()) - } - false - } - } - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/MiscSettings.kt b/app/src/main/java/app/passwordstore/ui/settings/MiscSettings.kt deleted file mode 100644 index 9a5d1e3b..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/MiscSettings.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import android.content.Context -import android.content.Intent -import android.net.Uri -import androidx.activity.result.contract.ActivityResultContracts -import androidx.documentfile.provider.DocumentFile -import androidx.fragment.app.FragmentActivity -import app.passwordstore.BuildConfig -import app.passwordstore.R -import app.passwordstore.util.services.PasswordExportService -import app.passwordstore.util.settings.PreferenceKeys -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.onClick -import de.Maxr1998.modernpreferences.helpers.pref -import de.Maxr1998.modernpreferences.helpers.switch - -class MiscSettings(activity: FragmentActivity) : SettingsProvider { - - private val storeExportAction = - activity.registerForActivityResult( - object : ActivityResultContracts.OpenDocumentTree() { - override fun createIntent(context: Context, input: Uri?): Intent { - return super.createIntent(context, input).apply { - flags = - Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION or - Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or - Intent.FLAG_GRANT_PREFIX_URI_PERMISSION - } - } - } - ) { uri: Uri? -> - if (uri == null) return@registerForActivityResult - val targetDirectory = DocumentFile.fromTreeUri(activity.applicationContext, uri) - - if (targetDirectory != null) { - val service = - Intent(activity.applicationContext, PasswordExportService::class.java).apply { - action = PasswordExportService.ACTION_EXPORT_PASSWORD - putExtra("uri", uri) - } - - activity.startForegroundService(service) - } - } - - override fun provideSettings(builder: PreferenceScreen.Builder) { - builder.apply { - pref(PreferenceKeys.EXPORT_PASSWORDS) { - titleRes = R.string.prefs_export_passwords_title - summaryRes = R.string.prefs_export_passwords_summary - onClick { - storeExportAction.launch(null) - true - } - } - switch(PreferenceKeys.CLEAR_CLIPBOARD_HISTORY) { - defaultValue = false - titleRes = R.string.pref_clear_clipboard_title - summaryRes = R.string.pref_clear_clipboard_summary - } - switch(PreferenceKeys.ENABLE_DEBUG_LOGGING) { - defaultValue = false - titleRes = R.string.pref_debug_logging_title - summaryRes = R.string.pref_debug_logging_summary - visible = !BuildConfig.DEBUG - } - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/PGPSettings.kt b/app/src/main/java/app/passwordstore/ui/settings/PGPSettings.kt deleted file mode 100644 index 46e62faa..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/PGPSettings.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import androidx.core.content.edit -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.lifecycleScope -import app.passwordstore.R -import app.passwordstore.data.crypto.PGPPassphraseCache -import app.passwordstore.ui.main.MainActivity -import app.passwordstore.ui.setup.SetupStart -import app.passwordstore.util.auth.BiometricAuthenticator -import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.features.Feature -import app.passwordstore.util.settings.PreferenceKeys -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.onClick -import de.Maxr1998.modernpreferences.helpers.pref -import de.Maxr1998.modernpreferences.helpers.switch -import kotlinx.coroutines.launch - -class PGPSettings( - private val activity: FragmentActivity, - private val passphraseCache: PGPPassphraseCache, -) : SettingsProvider { - - override fun provideSettings(builder: PreferenceScreen.Builder) { - builder.apply { - pref("_") { - titleRes = R.string.pref_pgp_key_manager_title - persistent = false - onClick { - activity.startActivity(MainActivity.setupIntent(activity, SetupStart.PgpKeys)) - false - } - } - switch(PreferenceKeys.ASCII_ARMOR) { - titleRes = R.string.pref_pgp_ascii_armor_title - persistent = true - } - switch(Feature.EnablePGPPassphraseCache.configKey) { - enabled = BiometricAuthenticator.canAuthenticate(activity) - titleRes = R.string.pref_passphrase_cache_title - summaryRes = R.string.pref_passphrase_cache_summary - defaultValue = false - onClick { - if (BiometricAuthenticator.canAuthenticate(activity)) { - val promptTitle = - if (checked) R.string.pref_passphrase_cache_authenticate_enable - else R.string.pref_passphrase_cache_authenticate_disable - - BiometricAuthenticator.authenticate(activity, promptTitle) { result -> - when (result) { - is BiometricAuthenticator.Result.Success -> { - /* Any successful change of this setting clears the passphrase - * cache for safety */ - activity.lifecycleScope.launch { - passphraseCache.clearAllCachedPassphrases(activity) - } - activity.sharedPrefs.edit { - putBoolean(Feature.EnablePGPPassphraseCache.configKey, checked) - if (!checked) remove(PreferenceKeys.CLEAR_PASSPHRASE_CACHE) - } - } - is BiometricAuthenticator.Result.Retry -> {} - else -> { - /* revert back to previous state in case of error or cancellation */ - checked = !checked - activity.sharedPrefs.edit { - putBoolean(Feature.EnablePGPPassphraseCache.configKey, checked) - } - } - } - } - } else { - /* we may get here if device lock has been disabled while PGP settings - * screen was left open */ - checked = false - enabled = false - activity.sharedPrefs.edit { - putBoolean(Feature.EnablePGPPassphraseCache.configKey, false) - } - } - false - } - } - switch(PreferenceKeys.CLEAR_PASSPHRASE_CACHE) { - dependency = Feature.EnablePGPPassphraseCache.configKey - titleRes = R.string.pref_passphrase_cache_auto_clear_title - summaryRes = R.string.pref_passphrase_cache_auto_clear_summary - defaultValue = true - /* Clear the cache once when unchecking; this is to prevent a malicious user (someone - * knowing the screen-lock pin, but not knowing the PGP passphrase) from bypassing cache - * clearing via the settings. Disabling it requires authentication, and cache clearing - * stays enabled in case of any authentication failure. */ - onClick { - if (!checked) { - if ( - BiometricAuthenticator.canAuthenticate(activity) && - activity.sharedPrefs.getBoolean(Feature.EnablePGPPassphraseCache.configKey, false) - ) { - BiometricAuthenticator.authenticate( - activity, - R.string.pref_passphrase_cache_auto_clear_authenticate_disable, - ) { result -> - when (result) { - is BiometricAuthenticator.Result.Success -> { - activity.sharedPrefs.edit { - putBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, false) - } - activity.lifecycleScope.launch { - passphraseCache.clearAllCachedPassphrases(activity) - } - } - is BiometricAuthenticator.Result.Retry -> {} - else -> { - activity.sharedPrefs.edit { remove(PreferenceKeys.CLEAR_PASSPHRASE_CACHE) } - checked = true - } - } - } - } else { - activity.sharedPrefs.edit { remove(PreferenceKeys.CLEAR_PASSPHRASE_CACHE) } - checked = true - enabled = false - } - } - false - } - } - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/PasswordSettings.kt b/app/src/main/java/app/passwordstore/ui/settings/PasswordSettings.kt deleted file mode 100644 index 9fe4cd42..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/PasswordSettings.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.ui.settings - -import android.text.InputType -import androidx.fragment.app.FragmentActivity -import app.passwordstore.R -import app.passwordstore.util.settings.PreferenceKeys -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.editText -import de.Maxr1998.modernpreferences.helpers.singleChoice -import de.Maxr1998.modernpreferences.helpers.switch -import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem - -class PasswordSettings(private val activity: FragmentActivity) : SettingsProvider { - - override fun provideSettings(builder: PreferenceScreen.Builder) { - builder.apply { - val values = activity.resources.getStringArray(R.array.pwgen_provider_values) - val labels = activity.resources.getStringArray(R.array.pwgen_provider_labels) - val items = values.zip(labels).map { SelectionItem(it.first, it.second, null) } - singleChoice(PreferenceKeys.PREF_KEY_PWGEN_TYPE, items) { - initialSelection = "diceware" - titleRes = R.string.pref_password_generator_type_title - } - editText(PreferenceKeys.GENERAL_SHOW_TIME) { - titleRes = R.string.pref_clipboard_timeout_title - summaryProvider = { timeout -> - activity.getString(R.string.pref_clipboard_timeout_summary, timeout ?: "45") - } - textInputType = InputType.TYPE_CLASS_NUMBER - } - switch(PreferenceKeys.SHOW_PASSWORD) { - titleRes = R.string.show_password_pref_title - summaryRes = R.string.show_password_pref_summary - defaultValue = true - } - switch(PreferenceKeys.COPY_ON_DECRYPT) { - titleRes = R.string.pref_copy_title - summaryRes = R.string.pref_copy_summary - defaultValue = false - } - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt b/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt deleted file mode 100644 index 23dd2829..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/RepositorySettings.kt +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import android.content.SharedPreferences -import android.content.pm.ShortcutManager -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult -import androidx.core.content.edit -import androidx.core.content.getSystemService -import androidx.fragment.app.FragmentActivity -import app.passwordstore.R -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.injection.prefs.GitPreferences -import app.passwordstore.ui.git.config.GitConfigActivity -import app.passwordstore.ui.main.MainActivity -import app.passwordstore.ui.proxy.ProxySelectorActivity -import app.passwordstore.ui.setup.SetupStart -import app.passwordstore.util.extensions.getString -import app.passwordstore.util.extensions.launchActivity -import app.passwordstore.util.extensions.sharedPrefs -import app.passwordstore.util.extensions.snackbar -import app.passwordstore.util.extensions.unsafeLazy -import app.passwordstore.util.git.sshj.SshKey -import app.passwordstore.util.settings.GitSettings -import app.passwordstore.util.settings.PreferenceKeys -import com.github.michaelbull.result.onFailure -import com.github.michaelbull.result.runCatching -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.EntryPoint -import dagger.hilt.InstallIn -import dagger.hilt.android.EntryPointAccessors -import dagger.hilt.components.SingletonComponent -import de.Maxr1998.modernpreferences.Preference -import de.Maxr1998.modernpreferences.PreferenceScreen -import de.Maxr1998.modernpreferences.helpers.onClick -import de.Maxr1998.modernpreferences.helpers.pref -import de.Maxr1998.modernpreferences.helpers.switch - -class RepositorySettings(private val activity: FragmentActivity) : SettingsProvider { - - private val generateSshKey = - activity.registerForActivityResult(StartActivityForResult()) { - showSshKeyPref?.visible = SshKey.canShowSshPublicKey - } - - private val hiltEntryPoint by unsafeLazy { - EntryPointAccessors.fromApplication( - activity.applicationContext, - RepositorySettingsEntryPoint::class.java, - ) - } - - private var showSshKeyPref: Preference? = null - - override fun provideSettings(builder: PreferenceScreen.Builder) { - val encryptedPreferences = hiltEntryPoint.encryptedPreferences() - val gitSettings = hiltEntryPoint.gitSettings() - - builder.apply { - switch(PreferenceKeys.REBASE_ON_PULL) { - titleRes = R.string.pref_rebase_on_pull_title - summaryRes = R.string.pref_rebase_on_pull_summary - summaryOnRes = R.string.pref_rebase_on_pull_summary_on - defaultValue = true - } - pref(PreferenceKeys.GIT_SERVER_INFO) { - titleRes = R.string.pref_edit_git_server_settings - visible = PasswordRepository.isGitRepo() - onClick { - activity.startActivity(MainActivity.setupIntent(activity, SetupStart.GitServer)) - true - } - } - pref(PreferenceKeys.PROXY_SETTINGS) { - titleRes = R.string.pref_edit_proxy_settings - visible = gitSettings.url?.startsWith("https") == true && PasswordRepository.isGitRepo() - onClick { - activity.launchActivity(ProxySelectorActivity::class.java) - true - } - } - pref(PreferenceKeys.GIT_CONFIG) { - titleRes = R.string.pref_edit_git_config - visible = PasswordRepository.isGitRepo() - onClick { - activity.launchActivity(GitConfigActivity::class.java) - true - } - } - pref(PreferenceKeys.SSH_KEY) { - titleRes = R.string.pref_import_ssh_key_title - visible = PasswordRepository.isGitRepo() - onClick { - generateSshKey.launch(MainActivity.setupIntent(activity, SetupStart.Ssh)) - true - } - } - pref(PreferenceKeys.SSH_KEYGEN) { - titleRes = R.string.pref_ssh_keygen_title - onClick { - generateSshKey.launch(MainActivity.setupIntent(activity, SetupStart.SshGenerate)) - true - } - } - showSshKeyPref = - pref(PreferenceKeys.SSH_SEE_KEY) { - titleRes = R.string.pref_ssh_see_key_title - visible = PasswordRepository.isGitRepo() && SshKey.canShowSshPublicKey - onClick { - generateSshKey.launch(MainActivity.setupIntent(activity, SetupStart.Ssh)) - true - } - } - pref(PreferenceKeys.CLEAR_SAVED_PASS) { - fun Preference.updatePref() { - val sshPass = encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) - val httpsPass = encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD) - if (sshPass == null && httpsPass == null) { - visible = false - return - } - titleRes = - when { - httpsPass != null -> R.string.clear_saved_passphrase_https - else -> R.string.clear_saved_passphrase_ssh - } - visible = true - requestRebind() - } - onClick { - updatePref() - true - } - updatePref() - } - pref(PreferenceKeys.SSH_OPENKEYSTORE_CLEAR_KEY_ID) { - titleRes = R.string.pref_title_openkeystore_clear_keyid - visible = - activity.sharedPrefs.getString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)?.isNotEmpty() - ?: false - onClick { - activity.sharedPrefs.edit { putString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID, null) } - visible = false - true - } - } - pref(PreferenceKeys.GIT_DELETE_REPO) { - titleRes = R.string.pref_git_delete_repo_title - summaryRes = R.string.pref_git_delete_repo_summary - onClick { - val repoDir = PasswordRepository.getRepositoryDirectory() - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.pref_dialog_delete_title) - .setMessage(activity.getString(R.string.dialog_delete_msg, repoDir)) - .setCancelable(false) - .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ -> - runCatching { - PasswordRepository.closeRepository() - PasswordRepository.getRepositoryDirectory().let { dir -> - dir.deleteRecursively() - dir.mkdirs() - } - } - .onFailure { it.message?.let { message -> activity.snackbar(message = message) } } - - activity.getSystemService<ShortcutManager>()?.apply { - removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList()) - } - activity.sharedPrefs.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) } - dialogInterface.cancel() - activity.finish() - } - .setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> - run { dialogInterface.cancel() } - } - .show() - true - } - } - } - } - - @EntryPoint - @InstallIn(SingletonComponent::class) - interface RepositorySettingsEntryPoint { - fun gitSettings(): GitSettings - - @GitPreferences fun encryptedPreferences(): SharedPreferences - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt deleted file mode 100644 index 02addd84..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsActivity.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import android.os.Bundle -import android.view.MenuItem -import androidx.activity.OnBackPressedCallback -import androidx.appcompat.app.AppCompatActivity -import androidx.core.os.BundleCompat -import app.passwordstore.R -import app.passwordstore.data.crypto.PGPPassphraseCache -import app.passwordstore.databinding.ActivityPreferenceRecyclerviewBinding -import app.passwordstore.util.extensions.viewBinding -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import de.Maxr1998.modernpreferences.Preference -import de.Maxr1998.modernpreferences.PreferencesAdapter -import de.Maxr1998.modernpreferences.helpers.screen -import de.Maxr1998.modernpreferences.helpers.subScreen -import javax.inject.Inject - -@AndroidEntryPoint -class SettingsActivity : AppCompatActivity() { - - @Inject lateinit var passphraseCache: PGPPassphraseCache - private val miscSettings = MiscSettings(this) - private val autofillSettings = AutofillSettings(this) - private val passwordSettings = PasswordSettings(this) - private val repositorySettings = RepositorySettings(this) - private val generalSettings = GeneralSettings(this) - private lateinit var pgpSettings: PGPSettings - - private val binding by viewBinding(ActivityPreferenceRecyclerviewBinding::inflate) - private val preferencesAdapter: PreferencesAdapter - get() = binding.preferenceRecyclerView.adapter as PreferencesAdapter - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(binding.root) - Preference.Config.dialogBuilderFactory = { context -> MaterialAlertDialogBuilder(context) } - pgpSettings = PGPSettings(this, passphraseCache) - val screen = - screen(this) { - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_general_title - iconRes = R.drawable.app_settings_alt_24px - generalSettings.provideSettings(this) - } - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_autofill_title - iconRes = R.drawable.ic_wysiwyg_24px - autofillSettings.provideSettings(this) - } - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_passwords_title - iconRes = R.drawable.ic_password_24px - passwordSettings.provideSettings(this) - } - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_repository_title - iconRes = R.drawable.ic_call_merge_24px - repositorySettings.provideSettings(this) - } - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_misc_title - iconRes = R.drawable.ic_miscellaneous_services_24px - miscSettings.provideSettings(this) - } - subScreen { - collapseIcon = true - titleRes = R.string.pref_category_pgp_title - iconRes = R.drawable.ic_lock_open_24px - pgpSettings.provideSettings(this) - } - } - val backPressedCallback = - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - preferencesAdapter.goBack() - } - } - onBackPressedDispatcher.addCallback(backPressedCallback) - val adapter = PreferencesAdapter(screen) - adapter.onScreenChangeListener = - PreferencesAdapter.OnScreenChangeListener { subScreen, entering -> - backPressedCallback.isEnabled = entering - supportActionBar?.title = - if (!entering) { - getString(R.string.action_settings) - } else { - getString(subScreen.titleRes) - } - } - if (savedInstanceState != null) { - BundleCompat.getParcelable( - savedInstanceState, - "adapter", - PreferencesAdapter.SavedState::class.java, - ) - ?.let(adapter::loadSavedState) - } - binding.preferenceRecyclerView.adapter = adapter - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putParcelable("adapter", preferencesAdapter.getSavedState()) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> - if (!preferencesAdapter.goBack()) { - super.onOptionsItemSelected(item) - } else { - true - } - else -> super.onOptionsItemSelected(item) - } - } -} diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsProvider.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsProvider.kt deleted file mode 100644 index cacca80f..00000000 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsProvider.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.ui.settings - -import de.Maxr1998.modernpreferences.PreferenceScreen - -/** Used to generate a uniform API for all settings UI classes. */ -interface SettingsProvider { - - /** Inserts the settings items for the class into the given [builder]. */ - fun provideSettings(builder: PreferenceScreen.Builder) -} diff --git a/app/src/main/java/app/passwordstore/ui/setup/SetupGraph.kt b/app/src/main/java/app/passwordstore/ui/setup/SetupGraph.kt index c2c2a2ac..40dfae74 100644 --- a/app/src/main/java/app/passwordstore/ui/setup/SetupGraph.kt +++ b/app/src/main/java/app/passwordstore/ui/setup/SetupGraph.kt @@ -33,10 +33,8 @@ import kotlinx.serialization.Serializable /** The setup screens [MainActivity.setupIntent] opens on their own. */ enum class SetupStart(val route: Any) { - GitServer(GitServerRoute(clone = false)), Ssh(SshKeyRoute), SshGenerate(SshKeyGenRoute), - PgpKeys(PgpKeysRoute()), PgpImport(PgpKeysRoute(KeysMode.Need)), } diff --git a/app/src/main/java/app/passwordstore/util/git/GitLogModel.kt b/app/src/main/java/app/passwordstore/util/git/GitLogModel.kt deleted file mode 100644 index 329db6d5..00000000 --- a/app/src/main/java/app/passwordstore/util/git/GitLogModel.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2014-2024 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package app.passwordstore.util.git - -import app.passwordstore.data.repo.PasswordRepository -import app.passwordstore.util.extensions.asLog -import app.passwordstore.util.extensions.hash -import app.passwordstore.util.extensions.time -import app.passwordstore.util.extensions.unsafeLazy -import com.github.michaelbull.result.getOrElse -import com.github.michaelbull.result.runCatching -import logcat.LogPriority.ERROR -import logcat.logcat -import org.eclipse.jgit.api.Git -import org.eclipse.jgit.revwalk.RevCommit - -private val TAG = GitLogModel::class.java.simpleName - -private fun commits(): Iterable<RevCommit> { - val repo = PasswordRepository.repository - if (repo == null) { - logcat(TAG, ERROR) { "Could not access git repository" } - return listOf() - } - return runCatching { Git(repo).log().call() } - .getOrElse { e -> - logcat(TAG, ERROR) { e.asLog("Failed to obtain git commits") } - listOf() - } -} - -/** - * Provides [GitCommit] s from a git-log of the password git repository. - * - * All commits are acquired on the first request to this object. - */ -class GitLogModel { - - // All commits are acquired here at once. Acquiring the commits in batches would not have been - // entirely sensible because the amount of computation required to obtain commit number n from - // the log includes the amount of computation required to obtain commit number n-1 from the log. - // This is because the commit graph is walked from HEAD to the last commit to obtain. - // Additionally, tests with 1000 commits in the log have not produced a significant delay in the - // user experience. - private val cache: MutableList<GitCommit> by unsafeLazy { - commits() - .map { GitCommit(it.hash, it.shortMessage, it.authorIdent.name, it.time) } - .toMutableList() - } - val size = cache.size - - fun get(index: Int): GitCommit? { - if (index >= size) - logcat(ERROR) { "Cannot get git commit with index $index. There are only $size." } - return cache.getOrNull(index) - } -} diff --git a/app/src/main/res/layout/activity_git_config.xml b/app/src/main/res/layout/activity_git_config.xml deleted file mode 100644 index b43ec6d6..00000000 --- a/app/src/main/res/layout/activity_git_config.xml +++ /dev/null @@ -1,113 +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" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="@dimen/activity_horizontal_margin" - tools:context="app.passwordstore.ui.git.config.GitConfigActivity" - tools:layout_editor_absoluteX="0dp" - tools:layout_editor_absoluteY="81dp"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/username_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:hint="@string/git_user_name_hint" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:layout_editor_absoluteY="64dp"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/git_user_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textPersonName" /> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/email_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:hint="@string/git_user_email" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/username_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/git_user_email" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textEmailAddress" /> - - </com.google.android.material.textfield.TextInputLayout> - - <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/email_input_layout" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/git_tools_title" - style="@style/TextAppearance.MaterialComponents.Headline5" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/git_tools" - android:textSize="24sp" - android:textStyle="bold" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/save_button" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/git_head_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/git_tools_title" - tools:text="HEAD status" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/git_log" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/git_log" - app:layout_constraintTop_toBottomOf="@+id/git_head_status" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/git_abort_rebase" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/abort_rebase" - app:layout_constraintTop_toBottomOf="@+id/git_log" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/git_reset_to_remote" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:text="@string/reset_to_remote" - app:layout_constraintTop_toBottomOf="@id/git_abort_rebase" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/git_gc" - android:layout_marginTop="8dp" - android:text="@string/git_run_gc_job" - app:layout_constraintTop_toBottomOf="@id/git_reset_to_remote" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/activity_git_log.xml b/app/src/main/res/layout/activity_git_log.xml deleted file mode 100644 index e9884db0..00000000 --- a/app/src/main/res/layout/activity_git_log.xml +++ /dev/null @@ -1,22 +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 - --> - -<LinearLayout 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"> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/git_log_recycler_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:scrollbars="vertical" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - tools:context="app.passwordstore.ui.git.log.GitLogActivity" - tools:listitem="@layout/git_log_row_layout" /> - -</LinearLayout> diff --git a/app/src/main/res/layout/activity_preference_recyclerview.xml b/app/src/main/res/layout/activity_preference_recyclerview.xml deleted file mode 100644 index 805bc79a..00000000 --- a/app/src/main/res/layout/activity_preference_recyclerview.xml +++ /dev/null @@ -1,18 +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 - --> - -<LinearLayout 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:orientation="vertical"> - - <androidx.recyclerview.widget.RecyclerView - android:id="@+id/preference_recycler_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> - -</LinearLayout> diff --git a/app/src/main/res/layout/activity_proxy_selector.xml b/app/src/main/res/layout/activity_proxy_selector.xml deleted file mode 100644 index b83ca9f7..00000000 --- a/app/src/main/res/layout/activity_proxy_selector.xml +++ /dev/null @@ -1,106 +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"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/proxy_host_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:hint="@string/proxy_hostname" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:layout_editor_absoluteY="64dp"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/proxy_host" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textUri" - android:nextFocusForward="@id/proxy_user" /> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/proxy_user_input_layout" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/normal_margin" - android:layout_marginEnd="@dimen/normal_margin" - android:hint="@string/username" - app:layout_constraintEnd_toStartOf="@id/proxy_port_input_layout" - app:layout_constraintHorizontal_weight="0.65" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/proxy_host_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/proxy_user" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textWebEmailAddress" - android:nextFocusForward="@id/proxy_port" /> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/proxy_port_input_layout" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/normal_margin" - android:layout_marginTop="@dimen/normal_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:hint="@string/port" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_weight="0.35" - app:layout_constraintStart_toEndOf="@id/proxy_user_input_layout" - app:layout_constraintTop_toBottomOf="@id/proxy_host_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/proxy_port" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="number" - android:nextFocusForward="@id/proxy_password" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/proxy_password_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/normal_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:hint="@string/password" - app:endIconMode="password_toggle" - app:layout_constraintStart_toStartOf="@id/proxy_user_input_layout" - app:layout_constraintTop_toBottomOf="@id/proxy_user_input_layout"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/proxy_password" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="textPassword" /> - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.button.MaterialButton - android:id="@+id/save" - style="?attr/materialButtonOutlinedStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/normal_margin" - android:layout_marginEnd="@dimen/activity_horizontal_margin" - android:text="@string/crypto_save" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/proxy_password_input_layout" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/dialog_text_input.xml b/app/src/main/res/layout/dialog_text_input.xml deleted file mode 100644 index c8539273..00000000 --- a/app/src/main/res/layout/dialog_text_input.xml +++ /dev/null @@ -1,25 +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 - --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:padding="@dimen/activity_horizontal_margin"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/text_input_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/edit_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - <requestFocus /> - </com.google.android.material.textfield.TextInputLayout> - -</LinearLayout> diff --git a/app/src/main/res/layout/git_log_row_layout.xml b/app/src/main/res/layout/git_log_row_layout.xml deleted file mode 100644 index e996f6af..00000000 --- a/app/src/main/res/layout/git_log_row_layout.xml +++ /dev/null @@ -1,45 +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="wrap_content" - android:paddingHorizontal="@dimen/activity_vertical_margin" - android:paddingVertical="@dimen/activity_horizontal_margin"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/git_log_row_message" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/normal_margin" - android:ellipsize="end" - android:maxLines="2" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/git_log_row_hash" - tools:text="Commit message" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/git_log_row_hash" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="?attr/colorSecondary" - android:textStyle="bold" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="Hash" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/git_log_row_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:alpha="0.5" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="Time" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/oreo_autofill_instructions.xml b/app/src/main/res/layout/oreo_autofill_instructions.xml deleted file mode 100644 index 66881390..00000000 --- a/app/src/main/res/layout/oreo_autofill_instructions.xml +++ /dev/null @@ -1,51 +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:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:paddingLeft="24dp" - android:paddingTop="20dp" - android:paddingRight="24dp" - android:paddingBottom="20dp"> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/intro_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/oreo_autofill_enable_dialog_description" - android:textSize="16sp" /> - - <androidx.appcompat.widget.AppCompatTextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:text="@string/oreo_autofill_enable_dialog_instructions" - android:textSize="16sp" /> - - <androidx.appcompat.widget.AppCompatTextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="@dimen/activity_vertical_margin" - android:text="@string/oreo_autofill_enable_dialog_installed_browsers" - android:textSize="16sp" - android:textStyle="bold" /> - - <androidx.appcompat.widget.AppCompatTextView - android:id="@+id/supportedBrowsers" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textSize="16sp" - tools:text="Firefox: Fill and save credentials" /> - - - </LinearLayout> -</ScrollView> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 618aac0d..08b74717 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -215,7 +215,6 @@ <string name="pref_biometric_auth_title">Biometrische Authentifizierung</string> <string name="pref_biometric_auth_summary">Falls aktiviert, werden Sie beim Starten der App nach Ihrem Fingerabdruck gefragt.</string> <string name="pref_biometric_auth_summary_error">Fingerabdrucksensor fehlt oder ist nicht ansprechbar.</string> - <string name="pref_title_openkeystore_clear_keyid">Lösche gespeicherte OpenKeystore SSH-Schlüssel-ID.</string> <string name="your_public_key">Ihr öffentlicher Schlüssel</string> <string name="error_generate_ssh_key">Fehler beim Erstellen des SSH-Schlüssels</string> <string name="pref_show_hidden_title">Alle Dateien und Ordner anzeigen</string> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 4cc7d29d..796884d7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -203,7 +203,6 @@ <string name="pref_biometric_auth_title">Activer l\'authentification biométrique</string> <string name="pref_biometric_auth_summary">Lorsque cette option est activée, Password Store vous demandera votre empreinte digitale au lancement</string> <string name="pref_biometric_auth_summary_error">Lecteur d\'empreinte digitale non accessible ou manquant</string> - <string name="pref_title_openkeystore_clear_keyid">Effacer la clé d\'identification SSH d\'OpenKeystore mémorisée</string> <string name="your_public_key">Votre clé publique</string> <string name="error_generate_ssh_key">Une erreur est survenue pendant la génération de la clé ssh</string> <string name="pref_show_hidden_title">Afficher tous les fichiers et dossiers</string> diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 34b1ce6a..b79e2990 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -217,7 +217,6 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid <string name="pref_biometric_auth_title">Activar autenticación biométrica</string> <string name="pref_biometric_auth_summary">Ao activala, Password Store vaiche pedir a túa pegada dactilar ao iniciar a app</string> <string name="pref_biometric_auth_summary_error">O hardware de pegada dixital non é accesible ou existente</string> - <string name="pref_title_openkeystore_clear_keyid">Eliminar o ID lembrado da chave SSH en OpenKeystore</string> <string name="your_public_key">A túa chave pública</string> <string name="error_generate_ssh_key">Algo fallou ao intentar crear a chave-ssh</string> <string name="pref_show_hidden_title">Mostar cartafoles ocultos</string> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 28a38301..ee7affd2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -198,7 +198,6 @@ <string name="pref_biometric_auth_title">Abilita autenticazione biometrica</string> <string name="pref_biometric_auth_summary">Se attiva, HardenedPass chiede l\'impronta digitale all\'avvio dell\'app</string> <string name="pref_biometric_auth_summary_error">L\'hardware delle impronte digitali non è accessibile o mancante</string> - <string name="pref_title_openkeystore_clear_keyid">Elimina l\'ID della Chiave SSH di OpenKeystore memorizzato</string> <string name="your_public_key">La tua chiave pubblica</string> <string name="error_generate_ssh_key">Errore durante la generazione della chiave SSH</string> <string name="pref_show_hidden_title">Mostra tutti i file e cartelle</string> @@ -303,7 +302,6 @@ <string name="pref_passphrase_cache_auto_clear_authenticate_disable">Autenticati per disattivare la cancellazione automatica</string> <string name="pref_passphrase_cache_auto_clear_title">Cancella automaticamente le frasi segrete memorizzate</string> <string name="pref_passphrase_cache_auto_clear_summary">Cancella le frasi segrete memorizzate allo spegnimento dello schermo</string> - <string name="pref_biometric_auth_summary_disabled_platform">Funzione non disponibile a causa di un bug della piattaforma</string> <string name="password_creation_encrypt_fail_message">Impossibile cifrare la password, non è stato salvato nulla: %1$s</string> <string name="otp_import_failure_no_selection">Nessuna immagine selezionata</string> <string name="invalid_gpg_id">Trovato .gpg-id, ma contiene un ID chiave, un\'impronta o un ID utente non valido</string> diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 06e0a555..b2836176 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -211,7 +211,6 @@ <string name="pref_biometric_auth_title">생체 인증 사용</string> <string name="pref_biometric_auth_summary">비밀번호 저장소를 활성화하면 앱을 시작할 때 지문을 입력하라는 메시지가 표시됩니다</string> <string name="pref_biometric_auth_summary_error">지문 하드웨어에 액세스할 수 없거나 누락된 경우</string> - <string name="pref_title_openkeystore_clear_keyid">저장된 OpenKeyStore SSH 키 ID 지우기</string> <string name="your_public_key">공개 키</string> <string name="error_generate_ssh_key">Ssh 키를 생성하는 동안 오류가 발생했습니다.</string> <string name="pref_show_hidden_title">모든 파일 및 폴더 표시</string> diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 6bdb966c..34272cae 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -210,7 +210,6 @@ <string name="pref_biometric_auth_title">Włącz uwierzytelnianie biometryczne</string> <string name="pref_biometric_auth_summary">Gdy włączone, Password Store zapyta Cię o odcisk palca podczas uruchamiania aplikacji</string> <string name="pref_biometric_auth_summary_error">Czytnik linii papilarnych jest niedostępny</string> - <string name="pref_title_openkeystore_clear_keyid">Wyczyść zapamiętany identyfikator klucza SSH OpenKeystore</string> <string name="your_public_key">Twój klucz publiczny</string> <string name="error_generate_ssh_key">Błąd podczas generowania klucza SSH</string> <string name="pref_show_hidden_title">Pokaż ukryte pliki i foldery</string> diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8f40c831..9f60b64e 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -215,7 +215,6 @@ <string name="pref_biometric_auth_title">Ativar autenticação biométrica</string> <string name="pref_biometric_auth_summary">Quando ativado, o Password Store irá pedir a sua impressão digital ao iniciar o aplicativo</string> <string name="pref_biometric_auth_summary_error">Hardware de impressão digital não acessível ou ausente</string> - <string name="pref_title_openkeystore_clear_keyid">Limpar ID de chave SSH lembrada do OpenKeystore</string> <string name="your_public_key">Sua chave pública</string> <string name="error_generate_ssh_key">Erro ao tentar gerar a chave SSH</string> <string name="pref_show_hidden_title">Mostrar todos os arquivos e pastas</string> diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index af3078bc..298d9508 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -208,7 +208,6 @@ <string name="pref_biometric_auth_title">Включить биометрическую аутентификацию</string> <string name="pref_biometric_auth_summary">Когда ключено, Password Store будет запрашивать ваш опечаток пальца при каждом запуске приложения</string> <string name="pref_biometric_auth_summary_error">Сенсор отпечатка пальца не доступен или отсутствует</string> - <string name="pref_title_openkeystore_clear_keyid">Очистить сохраненный SSH Key идентификатор OpenKystortore</string> <string name="your_public_key">Ваш публичный ключ</string> <string name="error_generate_ssh_key">Возникла ошибка при попытке генерации ssh ключа</string> <string name="pref_show_hidden_title">Показать все файлы и директории</string> diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 959d5fac..85125fc2 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -216,7 +216,6 @@ <string name="pref_biometric_auth_title">启用生物识别认证</string> <string name="pref_biometric_auth_summary">如果启用,Password Store 将在启动应用时提示您使用指纹进行身份验证</string> <string name="pref_biometric_auth_summary_error">指纹硬件无法访问或丢失</string> - <string name="pref_title_openkeystore_clear_keyid">清除记住的 OpenKeystore SSH 密钥ID</string> <string name="your_public_key">你的公开密钥</string> <string name="error_generate_ssh_key">尝试生成SSH密钥时出错</string> <string name="pref_show_hidden_title">显示所有文件和目录</string> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 8351a358..44d4f581 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -7,5 +7,4 @@ <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> - <dimen name="normal_margin">8dp</dimen> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 403ee2ae..653cf72c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -247,8 +247,6 @@ <string name="pref_biometric_auth_title">Enable biometric authentication</string> <string name="pref_biometric_auth_summary">When enabled, HardenedPass will prompt you for your fingerprint when launching the app</string> <string name="pref_biometric_auth_summary_error">Fingerprint hardware not accessible or missing</string> - <string name="pref_biometric_auth_summary_disabled_platform">Feature unavailable due to a platform bug</string> - <string name="pref_title_openkeystore_clear_keyid">Clear remembered OpenKeystore SSH Key ID</string> <string name="your_public_key">Your public key</string> <string name="error_generate_ssh_key">Error while trying to generate the ssh-key</string> <string name="pref_show_hidden_title">Show all files and folders</string> diff --git a/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md b/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md index 94f76479..b3d0f0cd 100644 --- a/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md +++ b/docs/superpowers/specs/2026-11-02-compose-ui-overhaul-design.md @@ -65,8 +65,22 @@ the maintainer's phone stays on 0.1.4 until then. repository is created only then, its `.gpg-id` naming the key by its uppercase long key ID, as `pass init <key ID>` does), and imports the key a store needs: after a clone that no imported key can decrypt, and when an entry is opened with no key imported. -- Screens outside the graph (Settings, autofill, git operations) and the entry view's key - import open these through a `MainActivity` setup intent until steps 6 and 7. +- Settings opens these within the graph. Autofill, the git operations' "No SSH key" dialog and + the entry view's key import open them through a `MainActivity` setup intent until step 7. + +### Settings + +- Settings lists its categories (general, autofill, passwords, repository, misc, PGP); each opens + a screen of rows (switches, single choices, text values, actions) over the existing preference + keys. Switches behind the screen lock store their value only after a successful prompt. +- Rows that lead elsewhere navigate within `MainActivity`: git server, SSH key, key generation, + PGP keys, proxy, and the git configuration screen (author name and email, commit log, abort + rebase, hard reset, garbage collection). +- Destructive actions ask first: deleting the repository (which never follows symlinks out of the + store and returns to the welcome screen), clearing a saved password or passphrase, the hard + reset. +- The list's sort orders are folders first and recently used; "files first" and "type + independent" are gone, and stored values fall back to folders first. ## Architecture @@ -102,7 +116,8 @@ the maintainer's phone stays on 0.1.4 until then. Crypto, git, autofill parsing, preference keys, the Keystore-backed secret storage, clipboard handling and the passphrase cache. Existing stores, keys and settings carry over without -migration. +migration, with two exceptions from step 6: a stored "files first" or "type independent" sort +order (both removed) becomes folders first, and the unused OpenKeychain SSH key ID is dropped. ### Removed at the end |
