aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2021-01-12 11:19:28 +0530
committerGitHub <noreply@github.com>2021-01-12 11:19:28 +0530
commit8bd156dea6e87eb667f76f4738fde992323ce0cf (patch)
tree505b93720ff21cdb2d7a7144253a7d9b352d041d
parent91e00d897f190ec6ab5330f7b3f43d6d7f3e57fb (diff)
Rework settings to use ModernAndroidPreferences (#1236)
Co-authored-by: Fabian Henneke <fabian@hen.ne.ke>
-rw-r--r--CHANGELOG.md1
-rw-r--r--app/build.gradle.kts5
-rw-r--r--app/src/main/AndroidManifest.xml16
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt12
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/WelcomeFragment.kt4
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt7
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/AutofillSettings.kt126
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt56
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/GeneralSettings.kt104
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/MiscSettings.kt76
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/PasswordSettings.kt119
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt198
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsActivity.kt93
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsProvider.kt19
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/UserPreference.kt674
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyGenActivity.kt1
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyImportActivity.kt61
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt7
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/git/operation/GitOperation.kt11
-rw-r--r--app/src/main/res/drawable/app_settings_alt_24px.xml14
-rw-r--r--app/src/main/res/drawable/ic_call_merge_24px.xml14
-rw-r--r--app/src/main/res/drawable/ic_miscellaneous_services_24px.xml17
-rw-r--r--app/src/main/res/drawable/ic_wysiwyg_24px.xml14
-rw-r--r--app/src/main/res/layout/activity_preference_recyclerview.xml18
-rw-r--r--app/src/main/res/values-de/strings.xml64
-rw-r--r--app/src/main/res/values-fr/strings.xml60
-rw-r--r--app/src/main/res/values-gl/strings.xml66
-rw-r--r--app/src/main/res/values-it/strings.xml66
-rw-r--r--app/src/main/res/values-pt-rBR/strings.xml64
-rw-r--r--app/src/main/res/values-ru/strings.xml64
-rw-r--r--app/src/main/res/values-v29/arrays.xml6
-rw-r--r--app/src/main/res/values/arrays.xml6
-rw-r--r--app/src/main/res/values/strings.xml67
-rw-r--r--app/src/main/res/xml/preference.xml174
-rw-r--r--buildSrc/src/main/java/Dependencies.kt1
35 files changed, 1197 insertions, 1108 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 88686572..58e172d3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file.
### Changed
- Accessibility autofill has been removed completely due to being buggy, insecure and lacking in features. Upgrade to Android 8 or preferably later to gain access to our advanced Autofill implementation.
+- The settings UI has been completely re-done to dramatically improve discoverability and navigation for users
## [1.13.1] - 2020-10-23
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 90322274..a1b2583b 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -73,6 +73,7 @@ dependencies {
implementation(Dependencies.FirstParty.zxing_android_embedded)
+ implementation(Dependencies.ThirdParty.bouncycastle)
implementation(Dependencies.ThirdParty.commons_codec)
implementation(Dependencies.ThirdParty.eddsa)
implementation(Dependencies.ThirdParty.fastscroll)
@@ -80,10 +81,10 @@ dependencies {
exclude(group = "org.apache.httpcomponents", module = "httpclient")
}
implementation(Dependencies.ThirdParty.kotlin_result)
- implementation(Dependencies.ThirdParty.sshj)
- implementation(Dependencies.ThirdParty.bouncycastle)
+ implementation(Dependencies.ThirdParty.modern_android_prefs)
implementation(Dependencies.ThirdParty.plumber)
implementation(Dependencies.ThirdParty.ssh_auth)
+ implementation(Dependencies.ThirdParty.sshj)
implementation(Dependencies.ThirdParty.timber)
implementation(Dependencies.ThirdParty.timberkt)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3e1d743e..69632359 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -33,7 +33,8 @@
android:name=".ui.onboarding.activity.OnboardingActivity"
android:configChanges="orientation|screenSize" />
- <activity android:name=".ui.proxy.ProxySelectorActivity"
+ <activity
+ android:name=".ui.proxy.ProxySelectorActivity"
android:windowSoftInputMode="adjustResize" />
<activity
@@ -70,8 +71,13 @@
android:label="@string/title_activity_git_log" />
<activity
- android:name=".ui.settings.UserPreference"
- android:label="@string/action_settings" />
+ android:name=".ui.settings.SettingsActivity"
+ android:label="@string/action_settings"
+ android:parentActivityName=".ui.passwords.PasswordStore" />
+
+ <activity
+ android:name=".ui.settings.DirectorySelectionActivity"
+ android:theme="@style/NoBackgroundTheme" />
<activity
android:name=".ui.crypto.PasswordCreationActivity"
@@ -105,6 +111,10 @@
<activity android:name=".ui.folderselect.SelectFolderActivity" />
<activity
+ android:name=".ui.sshkeygen.SshKeyImportActivity"
+ android:theme="@style/NoBackgroundTheme"
+ android:windowSoftInputMode="adjustResize" />
+ <activity
android:name=".ui.sshkeygen.SshKeyGenActivity"
android:label="@string/pref_ssh_keygen_title"
android:windowSoftInputMode="adjustResize" />
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt
index 08090d1b..37c23cea 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt
@@ -19,9 +19,9 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.msfjarvis.aps.R
-import dev.msfjarvis.aps.ui.settings.UserPreference
import dev.msfjarvis.aps.databinding.FragmentRepoLocationBinding
import dev.msfjarvis.aps.data.repo.PasswordRepository
+import dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity
import dev.msfjarvis.aps.util.settings.PasswordSortOrder
import dev.msfjarvis.aps.util.settings.PreferenceKeys
import dev.msfjarvis.aps.util.extensions.finish
@@ -31,11 +31,13 @@ import dev.msfjarvis.aps.util.extensions.listFilesRecursively
import dev.msfjarvis.aps.util.extensions.performTransactionWithBackStack
import dev.msfjarvis.aps.util.extensions.sharedPrefs
import dev.msfjarvis.aps.util.extensions.viewBinding
+import android.content.Intent
import java.io.File
class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
private val settings by lazy(LazyThreadSafetyMode.NONE) { requireActivity().applicationContext.sharedPrefs }
+ private val directorySelectIntent by lazy(LazyThreadSafetyMode.NONE) { Intent(requireContext(), DirectorySelectionActivity::class.java) }
private val binding by viewBinding(FragmentRepoLocationBinding::bind)
private val sortOrder: PasswordSortOrder
get() = PasswordSortOrder.getSortOrder(settings)
@@ -57,7 +59,7 @@ class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
}
private val externalDirPermGrantedAction = createPermGrantedAction {
- externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
+ externalDirectorySelectAction.launch(directorySelectIntent)
}
private val repositoryUsePermGrantedAction = createPermGrantedAction {
@@ -65,7 +67,7 @@ class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
}
private val repositoryChangePermGrantedAction = createPermGrantedAction {
- repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
+ repositoryInitAction.launch(directorySelectIntent)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -102,7 +104,7 @@ class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
} else {
// Unlikely we have storage permissions without user ever selecting a directory,
// but let's not assume.
- externalDirectorySelectAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
+ externalDirectorySelectAction.launch(directorySelectIntent)
}
} else {
MaterialAlertDialogBuilder(requireActivity())
@@ -119,7 +121,7 @@ class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
repositoryChangePermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
- repositoryInitAction.launch(UserPreference.createDirectorySelectionIntent(requireContext()))
+ repositoryInitAction.launch(directorySelectIntent)
}
}
.show()
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/WelcomeFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/WelcomeFragment.kt
index 696aba17..34459208 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/WelcomeFragment.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/WelcomeFragment.kt
@@ -11,8 +11,8 @@ import android.view.View
import androidx.annotation.Keep
import androidx.fragment.app.Fragment
import dev.msfjarvis.aps.R
-import dev.msfjarvis.aps.ui.settings.UserPreference
import dev.msfjarvis.aps.databinding.FragmentWelcomeBinding
+import dev.msfjarvis.aps.ui.settings.SettingsActivity
import dev.msfjarvis.aps.util.extensions.performTransactionWithBackStack
import dev.msfjarvis.aps.util.extensions.viewBinding
@@ -25,6 +25,6 @@ class WelcomeFragment : Fragment(R.layout.fragment_welcome) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.letsGo.setOnClickListener { parentFragmentManager.performTransactionWithBackStack(CloneFragment.newInstance()) }
- binding.settingsButton.setOnClickListener { startActivity(Intent(requireContext(), UserPreference::class.java)) }
+ binding.settingsButton.setOnClickListener { startActivity(Intent(requireContext(), SettingsActivity::class.java)) }
}
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt b/app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt
index 49620ac8..bdd8c9bb 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt
@@ -42,7 +42,6 @@ import dev.msfjarvis.aps.ui.main.LaunchActivity
import dev.msfjarvis.aps.R
import dev.msfjarvis.aps.util.viewmodel.SearchableRepositoryViewModel
import dev.msfjarvis.aps.ui.folderselect.SelectFolderActivity
-import dev.msfjarvis.aps.ui.settings.UserPreference
import dev.msfjarvis.aps.util.autofill.AutofillMatcher
import dev.msfjarvis.aps.ui.crypto.BasePgpActivity.Companion.getLongName
import dev.msfjarvis.aps.ui.crypto.DecryptActivity
@@ -55,6 +54,8 @@ import dev.msfjarvis.aps.ui.dialogs.FolderCreationDialogFragment
import dev.msfjarvis.aps.ui.onboarding.activity.OnboardingActivity
import dev.msfjarvis.aps.data.password.PasswordItem
import dev.msfjarvis.aps.data.repo.PasswordRepository
+import dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity
+import dev.msfjarvis.aps.ui.settings.SettingsActivity
import dev.msfjarvis.aps.util.settings.PreferenceKeys
import dev.msfjarvis.aps.util.extensions.base64
import dev.msfjarvis.aps.util.extensions.commitChange
@@ -300,7 +301,7 @@ class PasswordStore : BaseGitActivity() {
when (id) {
R.id.user_pref -> {
runCatching {
- startActivity(Intent(this, UserPreference::class.java))
+ startActivity(Intent(this, SettingsActivity::class.java))
}.onFailure { e ->
e.printStackTrace()
}
@@ -377,7 +378,7 @@ class PasswordStore : BaseGitActivity() {
private fun checkLocalRepository() {
val repo = PasswordRepository.initialize()
if (repo == null) {
- directorySelectAction.launch(UserPreference.createDirectorySelectionIntent(this))
+ directorySelectAction.launch(Intent(this, DirectorySelectionActivity::class.java))
} else {
checkLocalRepository(PasswordRepository.getRepositoryDirectory())
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/AutofillSettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/AutofillSettings.kt
new file mode 100644
index 00000000..74b407fd
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/AutofillSettings.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.PreferenceScreen
+import de.Maxr1998.modernpreferences.helpers.editText
+import de.Maxr1998.modernpreferences.helpers.onClick
+import de.Maxr1998.modernpreferences.helpers.singleChoice
+import de.Maxr1998.modernpreferences.helpers.switch
+import de.Maxr1998.modernpreferences.preferences.SwitchPreference
+import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem
+import dev.msfjarvis.aps.BuildConfig
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.autofill.DirectoryStructure
+import dev.msfjarvis.aps.util.extensions.autofillManager
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.provider.Settings
+import androidx.annotation.RequiresApi
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel
+import com.github.androidpasswordstore.autofillparser.getInstalledBrowsersWithAutofillSupportLevel
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+
+class AutofillSettings(private val activity: FragmentActivity) : SettingsProvider {
+
+ private val isAutofillServiceEnabled: Boolean
+ get() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
+ return activity.autofillManager?.hasEnabledAutofillServices() == true
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ 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 = Uri.parse("package:${BuildConfig.APPLICATION_ID}")
+ }
+ 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
+ visible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ defaultValue = isAutofillServiceEnabled
+ onClick {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return@onClick true
+ if (isAutofillServiceEnabled) {
+ activity.autofillManager?.disableAutofillServices()
+ } else {
+ showAutofillDialog(this)
+ }
+ false
+ }
+ }
+ val values = activity.resources.getStringArray(R.array.oreo_autofill_directory_structure_values)
+ val titles = activity.resources.getStringArray(R.array.oreo_autofill_directory_structure_entries)
+ val items = values.zip(titles).map { SelectionItem(it.first, it.second, null) }
+ singleChoice(PreferenceKeys.OREO_AUTOFILL_DIRECTORY_STRUCTURE, items) {
+ initialSelection = DirectoryStructure.DEFAULT.value
+ dependency = PreferenceKeys.AUTOFILL_ENABLE
+ titleRes = R.string.oreo_autofill_preference_directory_structure
+ }
+ 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/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt
new file mode 100644
index 00000000..41cd254b
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.net.Uri
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.os.Environment
+import android.provider.DocumentsContract
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.edit
+import com.github.ajalt.timberkt.d
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+
+class DirectorySelectionActivity : AppCompatActivity() {
+
+ @Suppress("DEPRECATION")
+ private val directorySelectAction = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri? ->
+ if (uri == null) return@registerForActivityResult
+
+ d { "Selected repository URI is $uri" }
+ // TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile
+ val docId = DocumentsContract.getTreeDocumentId(uri)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val path = if (split.size > 1) split[1] else split[0]
+ val repoPath = "${Environment.getExternalStorageDirectory()}/$path"
+ val prefs = sharedPrefs
+
+ d { "Selected repository path is $repoPath" }
+
+ if (Environment.getExternalStorageDirectory().path == repoPath) {
+ MaterialAlertDialogBuilder(this)
+ .setTitle(resources.getString(R.string.sdcard_root_warning_title))
+ .setMessage(resources.getString(R.string.sdcard_root_warning_message))
+ .setPositiveButton(resources.getString(R.string.sdcard_root_warning_remove_everything)) { _, _ ->
+ prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri.path) }
+ }
+ .setNegativeButton(R.string.dialog_cancel, null)
+ .show()
+ }
+ prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) }
+ setResult(RESULT_OK)
+ finish()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ directorySelectAction.launch(null)
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/GeneralSettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/GeneralSettings.kt
new file mode 100644
index 00000000..91df87d0
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/GeneralSettings.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.PreferenceScreen
+import de.Maxr1998.modernpreferences.helpers.checkBox
+import de.Maxr1998.modernpreferences.helpers.onClick
+import de.Maxr1998.modernpreferences.helpers.singleChoice
+import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.auth.BiometricAuthenticator
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.content.pm.ShortcutManager
+import android.os.Build
+import androidx.core.content.edit
+import androidx.core.content.getSystemService
+import androidx.fragment.app.FragmentActivity
+
+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 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
+ }
+
+ checkBox(PreferenceKeys.FILTER_RECURSIVELY) {
+ titleRes = R.string.pref_recursive_filter_title
+ summaryRes = R.string.pref_recursive_filter_summary
+ defaultValue = true
+ }
+
+ checkBox(PreferenceKeys.SEARCH_ON_START) {
+ titleRes = R.string.pref_search_on_start_title
+ summaryRes = R.string.pref_search_on_start_summary
+ defaultValue = false
+ }
+
+ checkBox(PreferenceKeys.SHOW_HIDDEN_CONTENTS) {
+ titleRes = R.string.pref_show_hidden_title
+ summaryRes = R.string.pref_show_hidden_summary
+ defaultValue = false
+ }
+
+ checkBox(PreferenceKeys.BIOMETRIC_AUTH) {
+ titleRes = R.string.pref_biometric_auth_title
+ defaultValue = false
+ }.apply {
+ val canAuthenticate = BiometricAuthenticator.canAuthenticate(activity)
+ if (!canAuthenticate) {
+ enabled = false
+ checked = false
+ summaryRes = R.string.pref_biometric_auth_summary_error
+ } else {
+ summaryRes = R.string.pref_biometric_auth_summary
+ onClick {
+ enabled = false
+ val isChecked = checked
+ activity.sharedPrefs.edit {
+ BiometricAuthenticator.authenticate(activity) { result ->
+ when (result) {
+ is BiometricAuthenticator.Result.Success -> {
+ // Apply the changes
+ putBoolean(PreferenceKeys.BIOMETRIC_AUTH, checked)
+ enabled = true
+ }
+ else -> {
+ // If any error occurs, revert back to the previous state. This
+ // catch-all clause includes the cancellation case.
+ putBoolean(PreferenceKeys.BIOMETRIC_AUTH, !checked)
+ checked = !isChecked
+ enabled = true
+ }
+ }
+ }
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ activity.getSystemService<ShortcutManager>()?.apply {
+ removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
+ }
+ }
+ false
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/MiscSettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/MiscSettings.kt
new file mode 100644
index 00000000..08da760c
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/MiscSettings.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.PreferenceScreen
+import de.Maxr1998.modernpreferences.helpers.checkBox
+import de.Maxr1998.modernpreferences.helpers.onClick
+import de.Maxr1998.modernpreferences.helpers.pref
+import dev.msfjarvis.aps.BuildConfig
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.services.PasswordExportService
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.documentfile.provider.DocumentFile
+import androidx.fragment.app.FragmentActivity
+
+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)
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ activity.startForegroundService(service)
+ } else {
+ activity.startService(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
+ }
+ }
+ checkBox(PreferenceKeys.CLEAR_CLIPBOARD_20X) {
+ defaultValue = false
+ titleRes = R.string.pref_clear_clipboard_title
+ summaryRes = R.string.pref_clear_clipboard_summary
+ }
+ checkBox(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/dev/msfjarvis/aps/ui/settings/PasswordSettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/PasswordSettings.kt
new file mode 100644
index 00000000..9b7eb01c
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/PasswordSettings.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.Preference
+import de.Maxr1998.modernpreferences.PreferenceScreen
+import de.Maxr1998.modernpreferences.helpers.categoryHeader
+import de.Maxr1998.modernpreferences.helpers.checkBox
+import de.Maxr1998.modernpreferences.helpers.editText
+import de.Maxr1998.modernpreferences.helpers.onCheckedChange
+import de.Maxr1998.modernpreferences.helpers.onClick
+import de.Maxr1998.modernpreferences.helpers.onSelectionChange
+import de.Maxr1998.modernpreferences.helpers.singleChoice
+import de.Maxr1998.modernpreferences.preferences.CheckBoxPreference
+import de.Maxr1998.modernpreferences.preferences.choice.SelectionItem
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.extensions.getString
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import dev.msfjarvis.aps.util.pwgenxkpwd.XkpwdDictionary
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.text.InputType
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.edit
+import androidx.fragment.app.FragmentActivity
+import java.io.File
+
+class PasswordSettings(private val activity: FragmentActivity) : SettingsProvider {
+
+ private val sharedPrefs by lazy(LazyThreadSafetyMode.NONE) { activity.sharedPrefs }
+ private val storeCustomXkpwdDictionaryAction = activity.registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
+ if (uri == null) return@registerForActivityResult
+
+ Toast.makeText(
+ activity,
+ activity.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
+ Toast.LENGTH_SHORT
+ ).show()
+
+ sharedPrefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
+
+ val inputStream = activity.contentResolver.openInputStream(uri)
+ val customDictFile = File(activity.filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE).outputStream()
+ inputStream?.copyTo(customDictFile, 1024)
+ inputStream?.close()
+ customDictFile.close()
+ }
+
+ override fun provideSettings(builder: PreferenceScreen.Builder) {
+ builder.apply {
+ val customDictPref = CheckBoxPreference(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT).apply {
+ titleRes = R.string.pref_xkpwgen_custom_wordlist_enabled_title
+ summaryRes = R.string.pref_xkpwgen_custom_dict_summary_off
+ summaryOnRes = R.string.pref_xkpwgen_custom_dict_summary_on
+ visible = sharedPrefs.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) == "xkpasswd"
+ onCheckedChange {
+ requestRebind()
+ true
+ }
+ }
+ val customDictPathPref = Preference(PreferenceKeys.PREF_KEY_CUSTOM_DICT).apply {
+ dependency = PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT
+ titleRes = R.string.pref_xkpwgen_custom_dict_picker_title
+ summary = sharedPrefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
+ ?: activity.resources.getString(R.string.pref_xkpwgen_custom_dict_picker_summary)
+ visible = sharedPrefs.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) == "xkpasswd"
+ onClick {
+ storeCustomXkpwdDictionaryAction.launch(arrayOf("*/*"))
+ true
+ }
+ }
+ 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 = "classic"
+ titleRes = R.string.pref_password_generator_type_title
+ onSelectionChange { selection ->
+ val xkpasswdEnabled = selection == "xkpasswd"
+ customDictPathPref.visible = xkpasswdEnabled
+ customDictPref.visible = xkpasswdEnabled
+ customDictPref.requestRebind()
+ customDictPathPref.requestRebind()
+ true
+ }
+ }
+ // We initialize them early and add them manually to be able to manually force a rebind
+ // when the password generator type is changed.
+ addPreferenceItem(customDictPref)
+ addPreferenceItem(customDictPathPref)
+ editText(PreferenceKeys.GENERAL_SHOW_TIME) {
+ titleRes = R.string.pref_clipboard_timeout_title
+ summaryProvider = { activity.getString(R.string.pref_clipboard_timeout_summary) }
+ textInputType = InputType.TYPE_CLASS_NUMBER
+ }
+ checkBox(PreferenceKeys.SHOW_PASSWORD) {
+ titleRes = R.string.show_password_pref_title
+ summaryRes = R.string.show_password_pref_summary
+ defaultValue = true
+ }
+ checkBox(PreferenceKeys.SHOW_EXTRA_CONTENT) {
+ titleRes = R.string.show_extra_content_pref_title
+ summaryRes = R.string.show_extra_content_pref_summary
+ defaultValue = true
+ }
+ checkBox(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/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt
new file mode 100644
index 00000000..32d04c9b
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.Preference
+import de.Maxr1998.modernpreferences.PreferenceScreen
+import de.Maxr1998.modernpreferences.helpers.checkBox
+import de.Maxr1998.modernpreferences.helpers.onCheckedChange
+import de.Maxr1998.modernpreferences.helpers.onClick
+import de.Maxr1998.modernpreferences.helpers.pref
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.data.repo.PasswordRepository
+import dev.msfjarvis.aps.ui.git.config.GitConfigActivity
+import dev.msfjarvis.aps.ui.git.config.GitServerConfigActivity
+import dev.msfjarvis.aps.ui.proxy.ProxySelectorActivity
+import dev.msfjarvis.aps.ui.sshkeygen.ShowSshKeyFragment
+import dev.msfjarvis.aps.ui.sshkeygen.SshKeyGenActivity
+import dev.msfjarvis.aps.ui.sshkeygen.SshKeyImportActivity
+import dev.msfjarvis.aps.util.extensions.getEncryptedGitPrefs
+import dev.msfjarvis.aps.util.extensions.getString
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import dev.msfjarvis.aps.util.extensions.snackbar
+import dev.msfjarvis.aps.util.settings.GitSettings
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import android.content.Intent
+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 com.github.michaelbull.result.onFailure
+import com.github.michaelbull.result.runCatching
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+
+class RepositorySettings(private val activity: FragmentActivity) : SettingsProvider {
+
+ private val encryptedPreferences by lazy(LazyThreadSafetyMode.NONE) { activity.getEncryptedGitPrefs() }
+
+ private fun <T : FragmentActivity> launchActivity(clazz: Class<T>) {
+ activity.startActivity(Intent(activity, clazz))
+ }
+
+ private fun selectExternalGitRepository() {
+ MaterialAlertDialogBuilder(activity)
+ .setTitle(activity.resources.getString(R.string.external_repository_dialog_title))
+ .setMessage(activity.resources.getString(R.string.external_repository_dialog_text))
+ .setPositiveButton(R.string.dialog_ok) { _, _ ->
+ launchActivity(DirectorySelectionActivity::class.java)
+ }
+ .setNegativeButton(R.string.dialog_cancel, null)
+ .show()
+ }
+
+ override fun provideSettings(builder: PreferenceScreen.Builder) {
+ builder.apply {
+ pref(PreferenceKeys.GIT_SERVER_INFO) {
+ titleRes = R.string.pref_edit_git_server_settings
+ visible = PasswordRepository.isGitRepo()
+ onClick {
+ launchActivity(GitServerConfigActivity::class.java)
+ true
+ }
+ }
+ pref(PreferenceKeys.PROXY_SETTINGS) {
+ titleRes = R.string.pref_edit_proxy_settings
+ visible = GitSettings.url?.startsWith("https") == true && PasswordRepository.isGitRepo()
+ onClick {
+ launchActivity(ProxySelectorActivity::class.java)
+ true
+ }
+ }
+ pref(PreferenceKeys.GIT_CONFIG) {
+ titleRes = R.string.pref_edit_git_config
+ visible = PasswordRepository.isGitRepo()
+ onClick {
+ launchActivity(GitConfigActivity::class.java)
+ true
+ }
+ }
+ pref(PreferenceKeys.SSH_KEY) {
+ titleRes = R.string.pref_import_ssh_key_title
+ visible = PasswordRepository.isGitRepo()
+ onClick {
+ launchActivity(SshKeyImportActivity::class.java)
+ true
+ }
+ }
+ pref(PreferenceKeys.SSH_KEYGEN) {
+ titleRes = R.string.pref_ssh_keygen_title
+ onClick {
+ launchActivity(SshKeyGenActivity::class.java)
+ true
+ }
+ }
+ pref(PreferenceKeys.SSH_SEE_KEY) {
+ titleRes = R.string.pref_ssh_see_key_title
+ visible = PasswordRepository.isGitRepo()
+ onClick {
+ ShowSshKeyFragment().show(activity.supportFragmentManager, "public_key")
+ 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
+ }
+ when {
+ httpsPass != null -> titleRes = R.string.clear_saved_passphrase_https
+ sshPass != null -> titleRes = 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
+ }
+ }
+ val deleteRepoPref = pref(PreferenceKeys.GIT_DELETE_REPO) {
+ titleRes = R.string.pref_git_delete_repo_title
+ summaryRes = R.string.pref_git_delete_repo_summary
+ visible = !activity.sharedPrefs.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
+ 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.getRepositoryDirectory().deleteRecursively()
+ PasswordRepository.closeRepository()
+ }.onFailure {
+ it.message?.let { message ->
+ activity.snackbar(message = message)
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ 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
+ }
+ }
+ checkBox(PreferenceKeys.GIT_EXTERNAL) {
+ titleRes = R.string.pref_external_repository_title
+ summaryRes = R.string.pref_external_repository_summary
+ onCheckedChange { checked ->
+ deleteRepoPref.visible = !checked
+ deleteRepoPref.requestRebind()
+ PasswordRepository.closeRepository()
+ activity.sharedPrefs.edit { putBoolean(PreferenceKeys.REPO_CHANGED, true) }
+ true
+ }
+ }
+ pref(PreferenceKeys.GIT_EXTERNAL_REPO) {
+ val externalRepo = activity.sharedPrefs.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
+ if (externalRepo != null) {
+ summary = externalRepo
+ } else {
+ summaryRes = R.string.pref_select_external_repository_summary_no_repo_selected
+ }
+ titleRes = R.string.pref_select_external_repository_title
+ dependency = PreferenceKeys.GIT_EXTERNAL
+ onClick {
+ selectExternalGitRepository()
+ true
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsActivity.kt
new file mode 100644
index 00000000..7db80023
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsActivity.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.settings
+
+import de.Maxr1998.modernpreferences.PreferencesAdapter
+import de.Maxr1998.modernpreferences.helpers.screen
+import de.Maxr1998.modernpreferences.helpers.subScreen
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.databinding.ActivityPreferenceRecyclerviewBinding
+import dev.msfjarvis.aps.util.extensions.viewBinding
+import android.os.Bundle
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+
+class SettingsActivity : AppCompatActivity() {
+
+ 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 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)
+ val screen = screen(this) {
+ subScreen {
+ titleRes = R.string.pref_category_general_title
+ iconRes = R.drawable.app_settings_alt_24px
+ generalSettings.provideSettings(this)
+ }
+ subScreen {
+ titleRes = R.string.pref_category_autofill_title
+ iconRes = R.drawable.ic_wysiwyg_24px
+ autofillSettings.provideSettings(this)
+ }
+ subScreen {
+ titleRes = R.string.pref_category_passwords_title
+ iconRes = R.drawable.ic_lock_open_24px
+ passwordSettings.provideSettings(this)
+ }
+ subScreen {
+ titleRes = R.string.pref_category_repository_title
+ iconRes = R.drawable.ic_call_merge_24px
+ repositorySettings.provideSettings(this)
+ }
+ subScreen {
+ titleRes = R.string.pref_category_misc_title
+ iconRes = R.drawable.ic_miscellaneous_services_24px
+ miscSettings.provideSettings(this)
+ }
+ }
+ val adapter = PreferencesAdapter(screen)
+ adapter.onScreenChangeListener = PreferencesAdapter.OnScreenChangeListener { subScreen, entering ->
+ supportActionBar?.title = if (!entering) {
+ getString(R.string.action_settings)
+ } else {
+ getString(subScreen.titleRes)
+ }
+ }
+ savedInstanceState?.getParcelable<PreferencesAdapter.SavedState>("adapter")
+ ?.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)
+ }
+ }
+
+ override fun onBackPressed() {
+ if (!preferencesAdapter.goBack())
+ super.onBackPressed()
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsProvider.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsProvider.kt
new file mode 100644
index 00000000..b8398b94
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/SettingsProvider.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.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/dev/msfjarvis/aps/ui/settings/UserPreference.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/UserPreference.kt
deleted file mode 100644
index 315ebbda..00000000
--- a/app/src/main/java/dev/msfjarvis/aps/ui/settings/UserPreference.kt
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-package dev.msfjarvis.aps.ui.settings
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.content.Intent
-import android.content.SharedPreferences
-import android.content.pm.ShortcutManager
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
-import android.os.Environment
-import android.provider.DocumentsContract
-import android.provider.Settings
-import android.text.TextUtils
-import android.view.MenuItem
-import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts.OpenDocument
-import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
-import androidx.annotation.RequiresApi
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.AppCompatTextView
-import androidx.core.content.edit
-import androidx.core.content.getSystemService
-import androidx.documentfile.provider.DocumentFile
-import androidx.preference.CheckBoxPreference
-import androidx.preference.EditTextPreference
-import androidx.preference.ListPreference
-import androidx.preference.Preference
-import androidx.preference.PreferenceFragmentCompat
-import androidx.preference.SwitchPreferenceCompat
-import com.github.ajalt.timberkt.Timber.tag
-import com.github.ajalt.timberkt.d
-import com.github.ajalt.timberkt.w
-import com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel
-import com.github.androidpasswordstore.autofillparser.getInstalledBrowsersWithAutofillSupportLevel
-import com.github.michaelbull.result.getOr
-import com.github.michaelbull.result.onFailure
-import com.github.michaelbull.result.runCatching
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import dev.msfjarvis.aps.BuildConfig
-import dev.msfjarvis.aps.util.services.PasswordExportService
-import dev.msfjarvis.aps.R
-import dev.msfjarvis.aps.ui.crypto.BasePgpActivity
-import dev.msfjarvis.aps.ui.git.config.GitConfigActivity
-import dev.msfjarvis.aps.ui.git.config.GitServerConfigActivity
-import dev.msfjarvis.aps.util.git.sshj.SshKey
-import dev.msfjarvis.aps.util.pwgenxkpwd.XkpwdDictionary
-import dev.msfjarvis.aps.ui.sshkeygen.ShowSshKeyFragment
-import dev.msfjarvis.aps.ui.sshkeygen.SshKeyGenActivity
-import dev.msfjarvis.aps.ui.proxy.ProxySelectorActivity
-import dev.msfjarvis.aps.util.auth.BiometricAuthenticator
-import dev.msfjarvis.aps.data.repo.PasswordRepository
-import dev.msfjarvis.aps.util.settings.PreferenceKeys
-import dev.msfjarvis.aps.util.extensions.autofillManager
-import dev.msfjarvis.aps.util.extensions.getEncryptedGitPrefs
-import dev.msfjarvis.aps.util.extensions.getString
-import dev.msfjarvis.aps.util.extensions.sharedPrefs
-import java.io.File
-
-typealias ClickListener = Preference.OnPreferenceClickListener
-typealias ChangeListener = Preference.OnPreferenceChangeListener
-
-class UserPreference : AppCompatActivity() {
-
- private lateinit var prefsFragment: PrefsFragment
- private var fromIntent = false
-
- @Suppress("DEPRECATION")
- private val directorySelectAction = registerForActivityResult(OpenDocumentTree()) { uri: Uri? ->
- if (uri == null) return@registerForActivityResult
-
- tag(TAG).d { "Selected repository URI is $uri" }
- // TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile
- val docId = DocumentsContract.getTreeDocumentId(uri)
- val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
- val path = if (split.size > 1) split[1] else split[0]
- val repoPath = "${Environment.getExternalStorageDirectory()}/$path"
- val prefs = sharedPrefs
-
- tag(TAG).d { "Selected repository path is $repoPath" }
-
- if (Environment.getExternalStorageDirectory().path == repoPath) {
- MaterialAlertDialogBuilder(this)
- .setTitle(getString(R.string.sdcard_root_warning_title))
- .setMessage(getString(R.string.sdcard_root_warning_message))
- .setPositiveButton("Remove everything") { _, _ ->
- prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri.path) }
- }
- .setNegativeButton(R.string.dialog_cancel, null)
- .show()
- }
- prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) }
- if (fromIntent) {
- setResult(RESULT_OK)
- finish()
- }
-
- }
-
- private val sshKeyImportAction = registerForActivityResult(OpenDocument()) { uri: Uri? ->
- if (uri == null) 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), null)
- .show()
- }
- }
-
- private val storeExportAction = registerForActivityResult(object : 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(applicationContext, uri)
-
- if (targetDirectory != null) {
- val service = Intent(applicationContext, PasswordExportService::class.java).apply {
- action = PasswordExportService.ACTION_EXPORT_PASSWORD
- putExtra("uri", uri)
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(service)
- } else {
- startService(service)
- }
- }
- }
-
- private val storeCustomXkpwdDictionaryAction = registerForActivityResult(OpenDocument()) { uri ->
- if (uri == null) return@registerForActivityResult
-
- Toast.makeText(
- this,
- this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
- Toast.LENGTH_SHORT
- ).show()
-
- sharedPrefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
-
- val customDictPref = prefsFragment.findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
- setCustomDictSummary(customDictPref, uri)
- // copy user selected file to internal storage
- val inputStream = contentResolver.openInputStream(uri)
- val customDictFile = File(filesDir.toString(), XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE).outputStream()
- inputStream?.copyTo(customDictFile, 1024)
- inputStream?.close()
- customDictFile.close()
-
- setResult(RESULT_OK)
- }
-
- class PrefsFragment : PreferenceFragmentCompat() {
-
- private var autoFillEnablePreference: SwitchPreferenceCompat? = null
- private var clearSavedPassPreference: Preference? = null
- private var viewSshKeyPreference: Preference? = null
- private lateinit var oreoAutofillDependencies: List<Preference>
- private lateinit var prefsActivity: UserPreference
- private lateinit var sharedPreferences: SharedPreferences
- private lateinit var encryptedPreferences: SharedPreferences
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- prefsActivity = requireActivity() as UserPreference
- val context = requireContext()
- sharedPreferences = preferenceManager.sharedPreferences
- encryptedPreferences = requireActivity().getEncryptedGitPrefs()
-
- addPreferencesFromResource(R.xml.preference)
-
- // Git preferences
- val gitServerPreference = findPreference<Preference>(PreferenceKeys.GIT_SERVER_INFO)
- val openkeystoreIdPreference = findPreference<Preference>(PreferenceKeys.SSH_OPENKEYSTORE_CLEAR_KEY_ID)
- val gitConfigPreference = findPreference<Preference>(PreferenceKeys.GIT_CONFIG)
- val sshKeyPreference = findPreference<Preference>(PreferenceKeys.SSH_KEY)
- val sshKeygenPreference = findPreference<Preference>(PreferenceKeys.SSH_KEYGEN)
- viewSshKeyPreference = findPreference(PreferenceKeys.SSH_SEE_KEY)
- clearSavedPassPreference = findPreference(PreferenceKeys.CLEAR_SAVED_PASS)
- val deleteRepoPreference = findPreference<Preference>(PreferenceKeys.GIT_DELETE_REPO)
- val externalGitRepositoryPreference = findPreference<Preference>(PreferenceKeys.GIT_EXTERNAL)
- val selectExternalGitRepositoryPreference = findPreference<Preference>(PreferenceKeys.PREF_SELECT_EXTERNAL)
-
- if (!PasswordRepository.isGitRepo()) {
- listOfNotNull(
- gitServerPreference,
- gitConfigPreference,
- sshKeyPreference,
- viewSshKeyPreference,
- clearSavedPassPreference,
- ).forEach {
- it.parent?.removePreference(it)
- }
- }
-
- // General preferences
- val showTimePreference = findPreference<Preference>(PreferenceKeys.GENERAL_SHOW_TIME)
- val clearClipboard20xPreference = findPreference<CheckBoxPreference>(PreferenceKeys.CLEAR_CLIPBOARD_20X)
-
- // Autofill preferences
- autoFillEnablePreference = findPreference(PreferenceKeys.AUTOFILL_ENABLE)
- val oreoAutofillDirectoryStructurePreference = findPreference<ListPreference>(PreferenceKeys.OREO_AUTOFILL_DIRECTORY_STRUCTURE)
- val oreoAutofillDefaultUsername = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME)
- val oreoAutofillCustomPublixSuffixes = findPreference<EditTextPreference>(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES)
- oreoAutofillDependencies = listOfNotNull(
- oreoAutofillDirectoryStructurePreference,
- oreoAutofillDefaultUsername,
- oreoAutofillCustomPublixSuffixes,
- )
- oreoAutofillCustomPublixSuffixes?.apply {
- setOnBindEditTextListener {
- it.isSingleLine = false
- it.setHint(R.string.preference_custom_public_suffixes_hint)
- }
- }
-
- // Misc preferences
- val appVersionPreference = findPreference<Preference>(PreferenceKeys.APP_VERSION)
-
- selectExternalGitRepositoryPreference?.summary = sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- ?: getString(R.string.no_repo_selected)
- deleteRepoPreference?.isVisible = !sharedPreferences.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
- clearClipboard20xPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.GENERAL_SHOW_TIME)?.toInt() != 0
- openkeystoreIdPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)?.isNotEmpty()
- ?: false
-
- updateAutofillSettings()
- updateClearSavedPassphrasePrefs()
-
- appVersionPreference?.summary = "Version: ${BuildConfig.VERSION_NAME}"
-
- sshKeyPreference?.onPreferenceClickListener = ClickListener {
- prefsActivity.getSshKey()
- true
- }
-
- sshKeygenPreference?.onPreferenceClickListener = ClickListener {
- prefsActivity.makeSshKey(true)
- true
- }
-
- viewSshKeyPreference?.onPreferenceClickListener = ClickListener {
- val df = ShowSshKeyFragment()
- df.show(parentFragmentManager, "public_key")
- true
- }
-
- clearSavedPassPreference?.onPreferenceClickListener = ClickListener {
- encryptedPreferences.edit {
- if (encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD) != null)
- remove(PreferenceKeys.HTTPS_PASSWORD)
- else if (encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) != null)
- remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE)
- }
- updateClearSavedPassphrasePrefs()
- true
- }
-
- openkeystoreIdPreference?.onPreferenceClickListener = ClickListener {
- sharedPreferences.edit { putString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID, null) }
- it.isVisible = false
- true
- }
-
- gitServerPreference?.onPreferenceClickListener = ClickListener {
- startActivity(Intent(prefsActivity, GitServerConfigActivity::class.java))
- true
- }
-
- gitConfigPreference?.onPreferenceClickListener = ClickListener {
- startActivity(Intent(prefsActivity, GitConfigActivity::class.java))
- true
- }
-
- deleteRepoPreference?.onPreferenceClickListener = ClickListener {
- val repoDir = PasswordRepository.getRepositoryDirectory()
- MaterialAlertDialogBuilder(prefsActivity)
- .setTitle(R.string.pref_dialog_delete_title)
- .setMessage(resources.getString(R.string.dialog_delete_msg, repoDir))
- .setCancelable(false)
- .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ ->
- runCatching {
- PasswordRepository.getRepositoryDirectory().deleteRecursively()
- PasswordRepository.closeRepository()
- }.onFailure {
- // TODO Handle the different cases of exceptions
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
- requireContext().getSystemService<ShortcutManager>()?.apply {
- removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
- }
- }
- sharedPreferences.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) }
- dialogInterface.cancel()
- prefsActivity.finish()
- }
- .setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } }
- .show()
-
- true
- }
-
- selectExternalGitRepositoryPreference?.summary =
- sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- ?: context.getString(R.string.no_repo_selected)
- selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener {
- prefsActivity.selectExternalGitRepository()
- true
- }
-
- val resetRepo = Preference.OnPreferenceChangeListener { _, o ->
- deleteRepoPreference?.isVisible = !(o as Boolean)
- PasswordRepository.closeRepository()
- sharedPreferences.edit { putBoolean(PreferenceKeys.REPO_CHANGED, true) }
- true
- }
-
- selectExternalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo
- externalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- autoFillEnablePreference?.onPreferenceClickListener = ClickListener {
- onEnableAutofillClick()
- true
- }
- }
-
- findPreference<Preference>(PreferenceKeys.EXPORT_PASSWORDS)?.apply {
- isVisible = sharedPreferences.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
- onPreferenceClickListener = Preference.OnPreferenceClickListener {
- prefsActivity.exportPasswords()
- true
- }
- }
-
- showTimePreference?.onPreferenceChangeListener = ChangeListener { _, newValue: Any? ->
- runCatching {
- val isEnabled = newValue.toString().toInt() != 0
- clearClipboard20xPreference?.isVisible = isEnabled
- true
- }.getOr(false)
- }
-
- showTimePreference?.summaryProvider = Preference.SummaryProvider<Preference> {
- getString(R.string.pref_clipboard_timeout_summary, sharedPreferences.getString
- (PreferenceKeys.GENERAL_SHOW_TIME, "45"))
- }
-
- findPreference<CheckBoxPreference>(PreferenceKeys.ENABLE_DEBUG_LOGGING)?.isVisible = !BuildConfig.ENABLE_DEBUG_FEATURES
-
- findPreference<CheckBoxPreference>(PreferenceKeys.BIOMETRIC_AUTH)?.apply {
- val canAuthenticate = BiometricAuthenticator.canAuthenticate(prefsActivity)
-
- if (!canAuthenticate) {
- isEnabled = false
- isChecked = false
- summary = getString(R.string.biometric_auth_summary_error)
- } else {
- setOnPreferenceClickListener {
- isEnabled = false
- sharedPreferences.edit {
- val checked = isChecked
- BiometricAuthenticator.authenticate(requireActivity()) { result ->
- when (result) {
- is BiometricAuthenticator.Result.Success -> {
- // Apply the changes
- putBoolean(PreferenceKeys.BIOMETRIC_AUTH, checked)
- isEnabled = true
- }
- else -> {
- // If any error occurs, revert back to the previous state. This
- // catch-all clause includes the cancellation case.
- putBoolean(PreferenceKeys.BIOMETRIC_AUTH, !checked)
- isChecked = !checked
- isEnabled = true
- }
- }
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
- requireContext().getSystemService<ShortcutManager>()?.apply {
- removeDynamicShortcuts(dynamicShortcuts.map { it.id }.toMutableList())
- }
- }
- }
- true
- }
- }
- }
-
- findPreference<Preference>(PreferenceKeys.PROXY_SETTINGS)?.onPreferenceClickListener = ClickListener {
- startActivity(Intent(requireContext(), ProxySelectorActivity::class.java))
- true
- }
-
- val prefCustomXkpwdDictionary = findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
- prefCustomXkpwdDictionary?.onPreferenceClickListener = ClickListener {
- prefsActivity.storeCustomDictionaryPath()
- true
- }
- val dictUri = sharedPreferences.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT) ?: ""
-
- if (!TextUtils.isEmpty(dictUri)) {
- setCustomDictSummary(prefCustomXkpwdDictionary, Uri.parse(dictUri))
- }
-
- val prefIsCustomDict = findPreference<CheckBoxPreference>(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT)
- val prefCustomDictPicker = findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
- val prefPwgenType = findPreference<ListPreference>(PreferenceKeys.PREF_KEY_PWGEN_TYPE)
- updateXkPasswdPrefsVisibility(prefPwgenType?.value, prefIsCustomDict, prefCustomDictPicker)
-
- prefPwgenType?.onPreferenceChangeListener = ChangeListener { _, newValue ->
- updateXkPasswdPrefsVisibility(newValue, prefIsCustomDict, prefCustomDictPicker)
- true
- }
-
- prefIsCustomDict?.onPreferenceChangeListener = ChangeListener { _, newValue ->
- if (!(newValue as Boolean)) {
- val customDictFile = File(context.filesDir, XkpwdDictionary.XKPWD_CUSTOM_DICT_FILE)
- if (customDictFile.exists() && !customDictFile.delete()) {
- w { "Failed to delete custom XkPassword dictionary: $customDictFile" }
- }
- prefCustomDictPicker?.setSummary(R.string.xkpwgen_pref_custom_dict_picker_summary)
- }
- true
- }
- }
-
- private fun updateXkPasswdPrefsVisibility(newValue: Any?, prefIsCustomDict: CheckBoxPreference?, prefCustomDictPicker: Preference?) {
- when (newValue as String) {
- BasePgpActivity.KEY_PWGEN_TYPE_CLASSIC -> {
- prefIsCustomDict?.isVisible = false
- prefCustomDictPicker?.isVisible = false
- }
- BasePgpActivity.KEY_PWGEN_TYPE_XKPASSWD -> {
- prefIsCustomDict?.isVisible = true
- prefCustomDictPicker?.isVisible = true
- }
- }
- }
-
- private fun updateAutofillSettings() {
- val isAutofillServiceEnabled = prefsActivity.isAutofillServiceEnabled
- val isAutofillSupported = prefsActivity.isAutofillServiceSupported
- if (!isAutofillSupported) {
- autoFillEnablePreference?.isVisible = false
- } else {
- autoFillEnablePreference?.isChecked = isAutofillServiceEnabled
- }
- oreoAutofillDependencies.forEach {
- it.isVisible = isAutofillServiceEnabled
- }
- }
-
- private fun updateClearSavedPassphrasePrefs() {
- clearSavedPassPreference?.apply {
- val sshPass = encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE)
- val httpsPass = encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD)
- if (sshPass == null && httpsPass == null) {
- isVisible = false
- return@apply
- }
- title = when {
- httpsPass != null -> getString(R.string.clear_saved_passphrase_https)
- sshPass != null -> getString(R.string.clear_saved_passphrase_ssh)
- else -> null
- }
- isVisible = true
- }
- }
-
- private fun updateViewSshPubkeyPref() {
- viewSshKeyPreference?.isVisible = SshKey.canShowSshPublicKey
- }
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun onEnableAutofillClick() {
- if (prefsActivity.isAutofillServiceEnabled) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- prefsActivity.autofillManager!!.disableAutofillServices()
- else
- throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O")
- } else {
- MaterialAlertDialogBuilder(prefsActivity).run {
- setTitle(R.string.pref_autofill_enable_title)
- @SuppressLint("InflateParams")
- val layout =
- 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 -> getString(R.string.oreo_autofill_no_support)
- BrowserAutofillSupportLevel.FlakyFill -> getString(R.string.oreo_autofill_flaky_fill_support)
- BrowserAutofillSupportLevel.PasswordFill -> getString(R.string.oreo_autofill_password_fill_support)
- BrowserAutofillSupportLevel.PasswordFillAndSaveIfNoAccessibility -> getString(R.string.oreo_autofill_password_fill_and_conditional_save_support)
- BrowserAutofillSupportLevel.GeneralFill -> getString(R.string.oreo_autofill_general_fill_support)
- BrowserAutofillSupportLevel.GeneralFillAndSave -> 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 = Uri.parse("package:${BuildConfig.APPLICATION_ID}")
- }
- startActivity(intent)
- }
- setNegativeButton(R.string.dialog_cancel, null)
- setOnDismissListener { updateAutofillSettings() }
- show()
- }
- }
- }
-
- override fun onResume() {
- super.onResume()
- updateAutofillSettings()
- updateClearSavedPassphrasePrefs()
- updateViewSshPubkeyPref()
- }
- }
-
- override fun onBackPressed() {
- super.onBackPressed()
- setResult(RESULT_OK)
- finish()
- }
-
- public override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- when (intent?.getStringExtra("operation")) {
- "get_ssh_key" -> getSshKey()
- "make_ssh_key" -> makeSshKey(false)
- "git_external" -> {
- fromIntent = true
- selectExternalGitRepository()
- }
- }
- prefsFragment = PrefsFragment()
-
- supportFragmentManager
- .beginTransaction()
- .replace(android.R.id.content, prefsFragment)
- .commit()
-
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- }
-
- @Suppress("Deprecation") // for Environment.getExternalStorageDirectory()
- fun selectExternalGitRepository() {
- MaterialAlertDialogBuilder(this)
- .setTitle(this.resources.getString(R.string.external_repository_dialog_title))
- .setMessage(this.resources.getString(R.string.external_repository_dialog_text))
- .setPositiveButton(R.string.dialog_ok) { _, _ ->
- directorySelectAction.launch(null)
- }
- .setNegativeButton(R.string.dialog_cancel, null)
- .show()
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- android.R.id.home -> {
- setResult(RESULT_OK)
- onBackPressed()
- true
- }
- else -> super.onOptionsItemSelected(item)
- }
- }
-
- private fun importSshKey() {
- sshKeyImportAction.launch(arrayOf("*/*"))
- }
-
- /**
- * Opens a file explorer to import the private key
- */
- private fun getSshKey() {
- 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) { _, _ -> }
- show()
- }
- } else {
- importSshKey()
- }
- }
-
- /**
- * Exports the passwords
- */
- private fun exportPasswords() {
- storeExportAction.launch(null)
- }
-
- /**
- * Opens a key generator to generate a public/private key pair
- */
- fun makeSshKey(fromPreferences: Boolean) {
- val intent = Intent(applicationContext, SshKeyGenActivity::class.java)
- startActivity(intent)
- if (!fromPreferences) {
- setResult(RESULT_OK)
- finish()
- }
- }
-
- /**
- * Pick custom xkpwd dictionary from sdcard
- */
- private fun storeCustomDictionaryPath() {
- storeCustomXkpwdDictionaryAction.launch(arrayOf("*/*"))
- }
-
- private val isAutofillServiceSupported: Boolean
- get() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
- return autofillManager?.isAutofillSupported != null
- }
-
- private val isAutofillServiceEnabled: Boolean
- get() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
- return autofillManager?.hasEnabledAutofillServices() == true
- }
-
- companion object {
-
- private const val TAG = "UserPreference"
-
- fun createDirectorySelectionIntent(context: Context): Intent {
- return Intent(context, UserPreference::class.java).run {
- putExtra("operation", "git_external")
- }
- }
-
- /**
- * Set custom dictionary summary
- */
- @JvmStatic
- private fun setCustomDictSummary(customDictPref: Preference?, uri: Uri) {
- val fileName = uri.path?.substring(uri.path?.lastIndexOf(":")!! + 1)
- customDictPref?.summary = "Selected dictionary: $fileName"
- }
- }
-}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyGenActivity.kt
index b260b77e..efcdd0f3 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyGenActivity.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyGenActivity.kt
@@ -144,6 +144,7 @@ class SshKeyGenActivity : AppCompatActivity() {
.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()
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyImportActivity.kt b/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyImportActivity.kt
new file mode 100644
index 00000000..1bb1056e
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/sshkeygen/SshKeyImportActivity.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package dev.msfjarvis.aps.ui.sshkeygen
+
+import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.util.git.sshj.SshKey
+import android.net.Uri
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+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/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt b/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
index aa70bacb..bcc5aac1 100644
--- a/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
@@ -9,8 +9,10 @@ import android.os.Build
import androidx.annotation.RequiresApi
import com.github.androidpasswordstore.autofillparser.Credentials
import dev.msfjarvis.aps.data.password.PasswordEntry
+import dev.msfjarvis.aps.util.extensions.getString
import dev.msfjarvis.aps.util.extensions.sharedPrefs
import dev.msfjarvis.aps.util.services.getDefaultUsername
+import dev.msfjarvis.aps.util.settings.PreferenceKeys
import java.io.File
import java.nio.file.Paths
@@ -110,8 +112,7 @@ enum class DirectoryStructure(val value: String) {
companion object {
- const val PREFERENCE = "oreo_autofill_directory_structure"
- private val DEFAULT = FileBased
+ val DEFAULT = FileBased
private val reverseMap = values().associateBy { it.value }
fun fromValue(value: String?) = if (value != null) reverseMap[value] ?: DEFAULT else DEFAULT
@@ -121,7 +122,7 @@ enum class DirectoryStructure(val value: String) {
object AutofillPreferences {
fun directoryStructure(context: Context): DirectoryStructure {
- val value = context.sharedPrefs.getString(DirectoryStructure.PREFERENCE, null)
+ val value = context.sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_DIRECTORY_STRUCTURE)
return DirectoryStructure.fromValue(value)
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/GitOperation.kt b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/GitOperation.kt
index 44292fc6..83c25a7e 100644
--- a/app/src/main/java/dev/msfjarvis/aps/util/git/operation/GitOperation.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/util/git/operation/GitOperation.kt
@@ -15,7 +15,6 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.msfjarvis.aps.R
-import dev.msfjarvis.aps.ui.settings.UserPreference
import dev.msfjarvis.aps.util.git.GitCommandExecutor
import dev.msfjarvis.aps.util.settings.AuthMode
import dev.msfjarvis.aps.util.settings.GitSettings
@@ -25,6 +24,8 @@ import dev.msfjarvis.aps.util.git.sshj.SshKey
import dev.msfjarvis.aps.util.git.sshj.SshjSessionFactory
import dev.msfjarvis.aps.util.auth.BiometricAuthenticator
import dev.msfjarvis.aps.data.repo.PasswordRepository
+import dev.msfjarvis.aps.ui.sshkeygen.SshKeyGenActivity
+import dev.msfjarvis.aps.ui.sshkeygen.SshKeyImportActivity
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
@@ -92,9 +93,11 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
private fun getSshKey(make: Boolean) {
runCatching {
- // Ask the UserPreference to provide us with the ssh-key
- val intent = Intent(callingActivity.applicationContext, UserPreference::class.java)
- intent.putExtra("operation", if (make) "make_ssh_key" else "get_ssh_key")
+ val intent = if (make) {
+ Intent(callingActivity.applicationContext, SshKeyGenActivity::class.java)
+ } else {
+ Intent(callingActivity.applicationContext, SshKeyImportActivity::class.java)
+ }
callingActivity.startActivity(intent)
}.onFailure { e ->
e(e)
diff --git a/app/src/main/res/drawable/app_settings_alt_24px.xml b/app/src/main/res/drawable/app_settings_alt_24px.xml
new file mode 100644
index 00000000..a6dce94c
--- /dev/null
+++ b/app/src/main/res/drawable/app_settings_alt_24px.xml
@@ -0,0 +1,14 @@
+<!--
+ ~ Copyright © 2014-2021 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:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.81,12.74l-0.82,-0.63v-0.22l0.8,-0.63c0.16,-0.12 0.2,-0.34 0.1,-0.51l-0.85,-1.48c-0.07,-0.13 -0.21,-0.2 -0.35,-0.2 -0.05,0 -0.1,0.01 -0.15,0.03l-0.95,0.38c-0.08,-0.05 -0.11,-0.07 -0.19,-0.11l-0.15,-1.01c-0.03,-0.21 -0.2,-0.36 -0.4,-0.36h-1.71c-0.2,0 -0.37,0.15 -0.4,0.34l-0.14,1.01c-0.03,0.02 -0.07,0.03 -0.1,0.05l-0.09,0.06 -0.95,-0.38c-0.05,-0.02 -0.1,-0.03 -0.15,-0.03 -0.14,0 -0.27,0.07 -0.35,0.2l-0.85,1.48c-0.1,0.17 -0.06,0.39 0.1,0.51l0.8,0.63v0.23l-0.8,0.63c-0.16,0.12 -0.2,0.34 -0.1,0.51l0.85,1.48c0.07,0.13 0.21,0.2 0.35,0.2 0.05,0 0.1,-0.01 0.15,-0.03l0.95,-0.37c0.08,0.05 0.12,0.07 0.2,0.11l0.15,1.01c0.03,0.2 0.2,0.34 0.4,0.34h1.71c0.2,0 0.37,-0.15 0.4,-0.34l0.15,-1.01c0.03,-0.02 0.07,-0.03 0.1,-0.05l0.09,-0.06 0.95,0.38c0.05,0.02 0.1,0.03 0.15,0.03 0.14,0 0.27,-0.07 0.35,-0.2l0.85,-1.48c0.1,-0.17 0.06,-0.39 -0.1,-0.51zM18,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM17,17h2v4c0,1.1 -0.9,2 -2,2H7c-1.1,0 -2,-0.9 -2,-2V3c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2v4h-2V6H7v12h10v-1z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_call_merge_24px.xml b/app/src/main/res/drawable/ic_call_merge_24px.xml
new file mode 100644
index 00000000..1ecd5dac
--- /dev/null
+++ b/app/src/main/res/drawable/ic_call_merge_24px.xml
@@ -0,0 +1,14 @@
+<!--
+ ~ Copyright © 2014-2020 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:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,20.41L18.41,19 15,15.59 13.59,17 17,20.41zM7.5,8H11v5.59L5.59,19 7,20.41l6,-6V8h3.5L12,3.5 7.5,8z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_miscellaneous_services_24px.xml b/app/src/main/res/drawable/ic_miscellaneous_services_24px.xml
new file mode 100644
index 00000000..3fc185c5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_miscellaneous_services_24px.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ Copyright © 2014-2020 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:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.17,13.71l1.4,-2.42c0.09,-0.15 0.05,-0.34 -0.08,-0.45l-1.48,-1.16c0.03,-0.22 0.05,-0.45 0.05,-0.68s-0.02,-0.46 -0.05,-0.69l1.48,-1.16c0.13,-0.11 0.17,-0.3 0.08,-0.45l-1.4,-2.42c-0.09,-0.15 -0.27,-0.21 -0.43,-0.15L12,4.83c-0.36,-0.28 -0.75,-0.51 -1.18,-0.69l-0.26,-1.85C10.53,2.13 10.38,2 10.21,2h-2.8C7.24,2 7.09,2.13 7.06,2.3L6.8,4.15C6.38,4.33 5.98,4.56 5.62,4.84l-1.74,-0.7c-0.16,-0.06 -0.34,0 -0.43,0.15l-1.4,2.42C1.96,6.86 2,7.05 2.13,7.16l1.48,1.16C3.58,8.54 3.56,8.77 3.56,9s0.02,0.46 0.05,0.69l-1.48,1.16C2,10.96 1.96,11.15 2.05,11.3l1.4,2.42c0.09,0.15 0.27,0.21 0.43,0.15l1.74,-0.7c0.36,0.28 0.75,0.51 1.18,0.69l0.26,1.85C7.09,15.87 7.24,16 7.41,16h2.8c0.17,0 0.32,-0.13 0.35,-0.3l0.26,-1.85c0.42,-0.18 0.82,-0.41 1.18,-0.69l1.74,0.7C13.9,13.92 14.08,13.86 14.17,13.71zM8.81,11c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C10.81,10.1 9.91,11 8.81,11z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M21.92,18.67l-0.96,-0.74c0.02,-0.14 0.04,-0.29 0.04,-0.44c0,-0.15 -0.01,-0.3 -0.04,-0.44l0.95,-0.74c0.08,-0.07 0.11,-0.19 0.05,-0.29l-0.9,-1.55c-0.05,-0.1 -0.17,-0.13 -0.28,-0.1l-1.11,0.45c-0.23,-0.18 -0.48,-0.33 -0.76,-0.44l-0.17,-1.18C18.73,13.08 18.63,13 18.53,13h-1.79c-0.11,0 -0.21,0.08 -0.22,0.19l-0.17,1.18c-0.27,0.12 -0.53,0.26 -0.76,0.44l-1.11,-0.45c-0.1,-0.04 -0.22,0 -0.28,0.1l-0.9,1.55c-0.05,0.1 -0.04,0.22 0.05,0.29l0.95,0.74c-0.02,0.14 -0.03,0.29 -0.03,0.44c0,0.15 0.01,0.3 0.03,0.44l-0.95,0.74c-0.08,0.07 -0.11,0.19 -0.05,0.29l0.9,1.55c0.05,0.1 0.17,0.13 0.28,0.1l1.11,-0.45c0.23,0.18 0.48,0.33 0.76,0.44l0.17,1.18c0.02,0.11 0.11,0.19 0.22,0.19h1.79c0.11,0 0.21,-0.08 0.22,-0.19l0.17,-1.18c0.27,-0.12 0.53,-0.26 0.75,-0.44l1.12,0.45c0.1,0.04 0.22,0 0.28,-0.1l0.9,-1.55C22.03,18.86 22,18.74 21.92,18.67zM17.63,18.83c-0.74,0 -1.35,-0.6 -1.35,-1.35s0.6,-1.35 1.35,-1.35s1.35,0.6 1.35,1.35S18.37,18.83 17.63,18.83z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_wysiwyg_24px.xml b/app/src/main/res/drawable/ic_wysiwyg_24px.xml
new file mode 100644
index 00000000..7549868b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wysiwyg_24px.xml
@@ -0,0 +1,14 @@
+<!--
+ ~ Copyright © 2014-2020 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:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3H5C3.89,3 3,3.9 3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.11,3 19,3zM19,19H5V7h14V19zM17,12H7v-2h10V12zM13,16H7v-2h6V16z"/>
+</vector>
diff --git a/app/src/main/res/layout/activity_preference_recyclerview.xml b/app/src/main/res/layout/activity_preference_recyclerview.xml
new file mode 100644
index 00000000..fe8cfa8e
--- /dev/null
+++ b/app/src/main/res/layout/activity_preference_recyclerview.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ ~ SPDX-License-Identifier: GPL-3.0-only
+ -->
+
+<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/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index df28d86c..50de1a02 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -89,43 +89,42 @@
<string name="share_as_plaintext">Als Klartext teilen</string>
<string name="last_changed">Zuletzt geändert %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Repository</string>
- <string name="pref_edit_server_info">Git-Server Einstellungen</string>
+ <string name="pref_category_repository_title">Repository</string>
+ <string name="pref_edit_git_server_settings">Git-Server Einstellungen</string>
<string name="pref_edit_git_config">Lokale Git Konfiguration &amp; Dienstprogramme</string>
- <string name="pref_ssh_title">Importiere SSH-Key</string>
+ <string name="pref_import_ssh_key_title">Importiere SSH-Key</string>
<string name="pref_ssh_keygen_title">Erstelle SSH-Schlüsselpaar</string>
<string name="pref_ssh_see_key_title">Zeige erstellten öffentlichen SSH-Key</string>
- <string name="pref_git_delete_repo">Repository löschen</string>
+ <string name="pref_git_delete_repo_title">Repository löschen</string>
<string name="pref_dialog_delete_title">Repository löschen</string>
<string name="pref_category_general_title">Allgemein</string>
- <string name="pref_category_title_passwords">Passwörter</string>
+ <string name="pref_category_passwords_title">Passwörter</string>
<string name="pref_clipboard_timeout_title">Timeout für das Kopieren des Passwortes</string>
<string name="pref_clipboard_timeout_summary">Legen Sie die Zeit (in Sekunden) fest, die das Passwort in der Zwischenablage liegen soll. 0 bedeutet für immer. Aktueller Wert: %1$s</string>
<string name="pref_copy_title">Kopiere Passwort automatisch</string>
- <string name="pref_copy_dialog_title">Kopiert das Passwort in die Zwischenablage, wenn der Eintrag entschlüsselt wurde.</string>
+ <string name="pref_copy_summary">Kopiert das Passwort in die Zwischenablage, wenn der Eintrag entschlüsselt wurde.</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">Die ausgewählte Datei scheint kein privater SSH-Schlüssel zu sein.</string>
<string name="ssh_key_success_dialog_title">SSH-Key importiert</string>
<string name="ssh_key_error_dialog_title">Schlüssel-Importfehler</string>
<string name="ssh_key_error_dialog_text">Nachricht : \n</string>
- <string name="pref_recursive_filter">Suche in Unterordnern</string>
- <string name="pref_recursive_filter_hint">Findet Passwörter auch in Unterordnern.</string>
+ <string name="pref_recursive_filter_title">Suche in Unterordnern</string>
+ <string name="pref_recursive_filter_summary">Findet Passwörter auch in Unterordnern.</string>
<string name="pref_sort_order_title">Passwortsortierung</string>
<string name="pref_folder_first_sort_order">Ordner zuerst</string>
<string name="pref_file_first_sort_order">Dateien zuerst</string>
<string name="pref_type_independent_sort_order">Typ unabhängig</string>
<string name="pref_recently_used_sort_order">Zuletzt verwendet</string>
- <string name="pref_autofill_title">Automatisch ausfüllen</string>
+ <string name="pref_category_autofill_title">Automatisch ausfüllen</string>
<string name="pref_autofill_enable_title">Autofill aktivieren</string>
- <string name="pref_misc_title">Verschiedenes</string>
+ <string name="pref_category_misc_title">Verschiedenes</string>
<string name="pref_clear_clipboard_title">Lösche die Zwischenablage 20-mal</string>
- <string name="pref_clear_clipboard_hint">Speichert Nonsense 20-mal anstatt 1-mal in der Zwischenablage. Nützlich bspw. auf Samsung-Geräten, die den Verlauf der Zwischenablage speichern.</string>
+ <string name="pref_clear_clipboard_summary">Speichert Nonsense 20-mal anstatt 1-mal in der Zwischenablage. Nützlich bspw. auf Samsung-Geräten, die den Verlauf der Zwischenablage speichern.</string>
<string name="pref_git_delete_repo_summary">Lösche das lokale (versteckte) Repository</string>
- <string name="pref_external_repository">Externes Repository</string>
+ <string name="pref_external_repository_title">Externes Repository</string>
<string name="pref_external_repository_summary">Nutze ein externes Repository</string>
- <string name="pref_select_external_repository">Wähle ein externes Repository</string>
+ <string name="pref_select_external_repository_title">Wähle ein externes Repository</string>
<string name="prefs_export_passwords_title">Passwörter exportieren</string>
<string name="prefs_export_passwords_summary">Exportiert die verschlüsselten Passwörter in ein externes Verzeichnis</string>
- <string name="prefs_version">Version</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Passwort generieren</string>
<string name="pwgen_generate">Generieren</string>
@@ -147,12 +146,12 @@
<string name="xkpwgen_custom_dict_imported">Eigene Wortliste: %1$s</string>
<string name="xkpwgen_builder_error">Das ausgewählte Wörterbuch enthält nicht genügend Wörter der angegebenen Länge %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Passwortgenerator</string>
- <string name="xkpwgen_pref_custom_dict_title">Eigene Wortliste</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Eigene Wordlist-Datei verwenden</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Integrierte Wortliste verwenden</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Eigene Wortliste</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Tippen Sie, um eine benutzerdefinierte Wordlist-Datei mit einem Wort pro Zeile auszuwählen</string>
+ <string name="pref_password_generator_type_title">Passwortgenerator</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Eigene Wortliste</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Eigene Wordlist-Datei verwenden</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Integrierte Wortliste verwenden</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Eigene Wortliste</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Tippen Sie, um eine benutzerdefinierte Wordlist-Datei mit einem Wort pro Zeile auszuwählen</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Passwort</string>
<string name="ssh_keygen_generate">Generieren</string>
@@ -192,7 +191,7 @@
<string name="show_extra_content_pref_summary">Soll weiterer Inhalt sichtbar sein?</string>
<string name="pwd_generate_button">Generieren</string>
<string name="refresh_list">Aktualisieren</string>
- <string name="no_repo_selected">Kein externes Repository ausgewählt</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Kein externes Repository ausgewählt</string>
<string name="send_plaintext_password_to">Passwort unverschlüsselt senden an…</string>
<string name="app_icon_hint">App Icon</string>
<!-- Oreo Autofill -->
@@ -238,10 +237,10 @@
<string name="biometric_prompt_title">Biometrische Abfrage</string>
<string name="biometric_auth_error">Authentifizierungsfehler</string>
<string name="biometric_auth_error_reason">Authentifizierungsfehler: %s</string>
- <string name="biometric_auth_title">Biometrische Authentifizierung aktivieren</string>
- <string name="biometric_auth_summary">Wenn aktiviert, werden Sie beim Starten der App nach Ihrem Fingerabdruck gefragt</string>
- <string name="biometric_auth_summary_error">Fingerabdrucksensor fehlt oder ist nicht ansprechbar</string>
- <string name="ssh_openkeystore_clear_keyid">Lösche gespeicherte OpenKeystore SSH-Schlüssel-ID</string>
+ <string name="pref_biometric_auth_title">Biometrische Authentifizierung aktivieren</string>
+ <string name="pref_biometric_auth_summary">Wenn 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="access_sdcard_text">Der Speicherort befindet sich in Ihrer SD-Karte oder im internen Speicher, aber die App hat nicht die Berechtigung, darauf zuzugreifen.</string>
<string name="your_public_key">Ihr öffentlicher Schlüssel</string>
<string name="error_generate_ssh_key">Fehler beim Generieren des SSH-Schlüssels</string>
@@ -254,16 +253,15 @@
<string name="message_error_destination_outside_repo">Ziel muss innerhalb des repository sein</string>
<string name="message_rename_folder">Ziel für %1$s angeben</string>
<string name="button_create">Erstellen</string>
- <string name="pref_search_on_start">Suchfeld beim Start öffnen</string>
- <string name="pref_search_on_start_hint">Suchleiste beim Start der App anzeigen</string>
- <string name="password_generator_category_title">Passwort Generator</string>
+ <string name="pref_search_on_start_title">Suchfeld beim Start öffnen</string>
+ <string name="pref_search_on_start_summary">Suchleiste beim Start der App anzeigen</string>
<string name="tap_clear_clipboard">Hier tippen, um die Zwischenablage zu löschen</string>
<string name="clone_git_repo">Das Repository muss geklont werden, bevor Änderungen synchronisert werden können.</string>
- <string name="theme_title">App Farbthema</string>
- <string name="theme_light">Hell</string>
- <string name="theme_dark">Dunkel</string>
- <string name="theme_battery_saver">Durch Energiesparmodus gesetzt</string>
- <string name="theme_follow_system">Systemstandard</string>
+ <string name="pref_app_theme_title">App Farbthema</string>
+ <string name="pref_app_theme_value_light">Hell</string>
+ <string name="pref_app_theme_value_dark">Dunkel</string>
+ <string name="pref_app_theme_value_battery_saver">Durch Energiesparmodus gesetzt</string>
+ <string name="pref_app_theme_value_follow_system">Systemstandard</string>
<string name="connection_mode_ssh_key">SSH-Schlüssel</string>
<string name="connection_mode_basic_authentication">Passwort</string>
<string name="git_server_config_save_success">Konfiguration erfolgreich gespeichert</string>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index d53cac7e..73008ecf 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -91,43 +91,42 @@
<string name="share_as_plaintext">Partager en clair</string>
<string name="last_changed">Dernière modification le %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Dépôt</string>
- <string name="pref_edit_server_info">Modifier les paramètres du serveur Git</string>
+ <string name="pref_category_repository_title">Dépôt</string>
+ <string name="pref_edit_git_server_settings">Modifier les paramètres du serveur Git</string>
<string name="pref_edit_git_config">Configuration locale de Git &amp; utilitaires</string>
- <string name="pref_ssh_title">Importer une clef SSH</string>
+ <string name="pref_import_ssh_key_title">Importer une clef SSH</string>
<string name="pref_ssh_keygen_title">Générer une paire de clefs SSH</string>
<string name="pref_ssh_see_key_title">Voir la clef publique SSH générée</string>
- <string name="pref_git_delete_repo">Supprimer le dépôt</string>
+ <string name="pref_git_delete_repo_title">Supprimer le dépôt</string>
<string name="pref_dialog_delete_title">Effacer le dépôt</string>
<string name="pref_category_general_title">Général</string>
- <string name="pref_category_title_passwords">Mot de passe</string>
+ <string name="pref_category_passwords_title">Mot de passe</string>
<string name="pref_clipboard_timeout_title">Délai imparti pour la copie</string>
<string name="pref_clipboard_timeout_summary">Définissez le temps (en secondes) durant lequel le mot de passe restera dans le presse-papiers. 0 pour une rétention illimitée. Valeur actuelle: %1$s</string>
<string name="pref_copy_title">Copie automatique du mot de passe</string>
- <string name="pref_copy_dialog_title">Copie automatiquement le mot de passe vers le presse-papier si le déchiffrement a réussi.</string>
+ <string name="pref_copy_summary">Copie automatiquement le mot de passe vers le presse-papier si le déchiffrement a réussi.</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">Le fichier sélectionné ne semble pas être une clé privée SSH.</string>
<string name="ssh_key_success_dialog_title">Clef SSH importée</string>
<string name="ssh_key_error_dialog_title">Erreur d\'importation de la clé</string>
<string name="ssh_key_error_dialog_text">Message : \n</string>
- <string name="pref_recursive_filter">Filtre récursif</string>
- <string name="pref_recursive_filter_hint">Cherche le mot de passe dans tous les sous-répertoires du répertoire actuel.</string>
+ <string name="pref_recursive_filter_title">Filtre récursif</string>
+ <string name="pref_recursive_filter_summary">Cherche le mot de passe dans tous les sous-répertoires du répertoire actuel.</string>
<string name="pref_sort_order_title">Ordre de tri des mots de passe</string>
<string name="pref_folder_first_sort_order">Dossiers en premier</string>
<string name="pref_file_first_sort_order">Fichiers en premier</string>
<string name="pref_type_independent_sort_order">Indifférent au type d\'entrée</string>
<string name="pref_recently_used_sort_order">Récemment utilisé</string>
- <string name="pref_autofill_title">Saisie automatique</string>
+ <string name="pref_category_autofill_title">Saisie automatique</string>
<string name="pref_autofill_enable_title">Saisie automatique</string>
- <string name="pref_misc_title">Divers</string>
+ <string name="pref_category_misc_title">Divers</string>
<string name="pref_clear_clipboard_title">Effacer le presse-papier 20 fois</string>
- <string name="pref_clear_clipboard_hint">Enregistre des informations absurdes dans le presse-papier 20 fois à la place d\'une seule. Utile sur les téléphones Samsung qui disposent d\'un historique du presse-papier.</string>
+ <string name="pref_clear_clipboard_summary">Enregistre des informations absurdes dans le presse-papier 20 fois à la place d\'une seule. Utile sur les téléphones Samsung qui disposent d\'un historique du presse-papier.</string>
<string name="pref_git_delete_repo_summary">Supprime le dépot local (caché)</string>
- <string name="pref_external_repository">Dépôt externe</string>
+ <string name="pref_external_repository_title">Dépôt externe</string>
<string name="pref_external_repository_summary">Utilise un dépôt externe pour les mots de passe</string>
- <string name="pref_select_external_repository">Choisissez un dépôt externe</string>
+ <string name="pref_select_external_repository_title">Choisissez un dépôt externe</string>
<string name="prefs_export_passwords_title">Exporter les mots de passe</string>
<string name="prefs_export_passwords_summary">Exporter les mots de passe (chiffrés) vers un répertoire externe</string>
- <string name="prefs_version">Version</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Générer un mot de passe</string>
<string name="pwgen_generate">Générer</string>
@@ -149,12 +148,12 @@
<string name="xkpwgen_custom_dict_imported">Liste de mots personnalisée : %1$s</string>
<string name="xkpwgen_builder_error">Le dictionnaire sélectionné ne contient pas assez de mots de la longueur %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Type de générateur de mot de passe</string>
- <string name="xkpwgen_pref_custom_dict_title">Liste de mots personnalisée</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Utiliser une liste de mots personnalisée</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Utilisation de la liste de mots intégrée</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Fichier personnalisé de liste de mots</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Touchez pour choisir un fichier de liste de mots personnalisés contenant un mot par ligne</string>
+ <string name="pref_password_generator_type_title">Type de générateur de mot de passe</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Liste de mots personnalisée</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Utiliser une liste de mots personnalisée</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Utilisation de la liste de mots intégrée</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Fichier personnalisé de liste de mots</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Touchez pour choisir un fichier de liste de mots personnalisés contenant un mot par ligne</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Mot de passe</string>
<string name="ssh_keygen_generate">Générer</string>
@@ -191,7 +190,7 @@
<string name="show_extra_content_pref_summary">Controller la visibilité du contenu supplémentaire une fois déchiffré</string>
<string name="pwd_generate_button">Générer</string>
<string name="refresh_list">Rafraichir la liste</string>
- <string name="no_repo_selected">Pas de dépôt externe séléctionné</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Pas de dépôt externe séléctionné</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 -->
@@ -224,9 +223,9 @@
<string name="git_operation_remember_passphrase">Se rappeler de la phrase secrète dans la configuration de l\'application (peu sûr)</string>
<string name="reset_to_remote">Réinitialisation dure de la branche distante</string>
<string name="biometric_prompt_title">Identification biométrique</string>
- <string name="biometric_auth_title">Authentification biométrique</string>
- <string name="biometric_auth_summary">Lorsque cette option est activée, Password Store vous demandera votre empreinte digitale au lancement</string>
- <string name="biometric_auth_summary_error">Lecteur d\'empreinte digitale non accessible ou manquant</string>
+ <string name="pref_biometric_auth_title">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="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>
@@ -235,14 +234,13 @@
<string name="title_rename_folder">Renommer le dossier</string>
<string name="message_rename_folder">Entrez le chemin pour %1$s</string>
<string name="button_create">Créer</string>
- <string name="pref_search_on_start">Ouvrir la recherche au démarrage</string>
- <string name="pref_search_on_start_hint">Ouvrir la barre de recherche au démarrage de l\'application</string>
- <string name="password_generator_category_title">Générateur de mot de passe</string>
+ <string name="pref_search_on_start_title">Ouvrir la recherche au démarrage</string>
+ <string name="pref_search_on_start_summary">Ouvrir la barre de recherche au démarrage de l\'application</string>
<string name="tap_clear_clipboard">Appuyez ici pour effacer le presse-papiers</string>
- <string name="theme_title">Thème de l\'application</string>
- <string name="theme_light">Clair</string>
- <string name="theme_dark">Sombre</string>
- <string name="theme_follow_system">Thème système</string>
+ <string name="pref_app_theme_title">Thème de l\'application</string>
+ <string name="pref_app_theme_value_light">Clair</string>
+ <string name="pref_app_theme_value_dark">Sombre</string>
+ <string name="pref_app_theme_value_follow_system">Thème système</string>
<string name="connection_mode_ssh_key">Clé SSH</string>
<string name="connection_mode_basic_authentication">Mot de passe</string>
<string name="git_server_config_save_error">L\'URL du dépôt fournie n\'est pas valide</string>
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 89b4da27..5de9af33 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -93,43 +93,42 @@
<string name="share_as_plaintext">Compartir como texto plano</string>
<string name="last_changed">Último cambio %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Repositorio</string>
- <string name="pref_edit_server_info">Editar axustes do servidor git</string>
+ <string name="pref_category_repository_title">Repositorio</string>
+ <string name="pref_edit_git_server_settings">Editar axustes do servidor git</string>
<string name="pref_edit_git_config">Utilidades Git</string>
- <string name="pref_ssh_title">Importar chave SSH</string>
+ <string name="pref_import_ssh_key_title">Importar chave SSH</string>
<string name="pref_ssh_keygen_title">Crear par de chaves SSH</string>
<string name="pref_ssh_see_key_title">Ver a chave pública SSH creada</string>
- <string name="pref_git_delete_repo">Eliminar repositorio</string>
+ <string name="pref_git_delete_repo_title">Eliminar repositorio</string>
<string name="pref_dialog_delete_title">Baleirar repositorio</string>
<string name="pref_category_general_title">Xeral</string>
- <string name="pref_category_title_passwords">Contrasinais</string>
+ <string name="pref_category_passwords_title">Contrasinais</string>
<string name="pref_clipboard_timeout_title">Caducidade do copiado do contrasinal</string>
<string name="pref_clipboard_timeout_summary">Establece os segundos que queres que o contrasinal permaneza copiado no portapapeis. 0 significa para sempre. Valor actual: %1$s</string>
<string name="pref_copy_title">Copiar contrasinal automáticamente</string>
- <string name="pref_copy_dialog_title">Copia automáticamente o contrasinal ao portapapeis se o descifra correctamente</string>
+ <string name="pref_copy_summary">Copia automáticamente o contrasinal ao portapapeis se o descifra correctamente</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">O ficheiro escollido non semella ser unha chave privada SSH.</string>
<string name="ssh_key_success_dialog_title">Chave-SSH importada</string>
<string name="ssh_key_error_dialog_title">Houbo un fallo ao importar a chave ssh</string>
<string name="ssh_key_error_dialog_text">Mensaxe : \n</string>
- <string name="pref_recursive_filter">Filtro recursivo</string>
- <string name="pref_recursive_filter_hint">Atopa as chaves de xeito recursivo no directorio actual.</string>
+ <string name="pref_recursive_filter_title">Filtro recursivo</string>
+ <string name="pref_recursive_filter_summary">Atopa as chaves de xeito recursivo no directorio actual.</string>
<string name="pref_sort_order_title">Orde para mostrar contrasinais</string>
<string name="pref_folder_first_sort_order">Primeiro cartafoles</string>
<string name="pref_file_first_sort_order">Primeiro ficheiros</string>
<string name="pref_type_independent_sort_order">Independentemente do tipo</string>
<string name="pref_recently_used_sort_order">Usadas recentemente</string>
- <string name="pref_autofill_title">Completado automático</string>
+ <string name="pref_category_autofill_title">Completado automático</string>
<string name="pref_autofill_enable_title">Activar completado automático</string>
- <string name="pref_misc_title">Varios</string>
+ <string name="pref_category_misc_title">Varios</string>
<string name="pref_clear_clipboard_title">Baleirar portapapeis 20 veces</string>
- <string name="pref_clear_clipboard_hint">Gardar números consecutivos no portapapeis 20 veces. Resulta útil nos móbiles Samsung que gardan historial no portapapeis.</string>
+ <string name="pref_clear_clipboard_summary">Gardar números consecutivos no portapapeis 20 veces. Resulta útil nos móbiles Samsung que gardan historial no portapapeis.</string>
<string name="pref_git_delete_repo_summary">Elimina repositorio local (oculto).</string>
- <string name="pref_external_repository">Repositorio externo</string>
+ <string name="pref_external_repository_title">Repositorio externo</string>
<string name="pref_external_repository_summary">Usar un repositorio externo de contrasinais</string>
- <string name="pref_select_external_repository">Selecciona repositorio externo</string>
+ <string name="pref_select_external_repository_title">Selecciona repositorio externo</string>
<string name="prefs_export_passwords_title">Exportar contrasinais</string>
<string name="prefs_export_passwords_summary">Exporta os contrasinais cifrados a un directorio externo</string>
- <string name="prefs_version">Versión</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Crear contrasinal</string>
<string name="pwgen_generate">Crear</string>
@@ -151,12 +150,12 @@
<string name="xkpwgen_custom_dict_imported">Lista persoal de palabras: %1$s</string>
<string name="xkpwgen_builder_error">O dicionario non contén palabras suficientes da lonxitude dada %1$d .. %2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Tipo de creador de contrasinais</string>
- <string name="xkpwgen_pref_custom_dict_title">Lista persoal de palabras</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Usar ficheiro con palabras personalizadas</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Usar lista de palabras incluída</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Ficheiro persoal de palabras</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Toca para escoller un ficheiro persoal con palabras que conteña unha palabra por liña</string>
+ <string name="pref_password_generator_type_title">Tipo de creador de contrasinais</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista persoal de palabras</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Usar ficheiro con palabras personalizadas</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Usar lista de palabras incluída</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Ficheiro persoal de palabras</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Toca para escoller un ficheiro persoal con palabras que conteña unha palabra por liña</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Frase de paso</string>
<string name="ssh_keygen_generate">Crear</string>
@@ -196,7 +195,7 @@
<string name="show_extra_content_pref_summary">Controla a visibilidade do contido extra unha vez descifrado</string>
<string name="pwd_generate_button">Crear</string>
<string name="refresh_list">Actualizar lista</string>
- <string name="no_repo_selected">Non hai seleccionado ningún repositorio externo</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Non hai seleccionado ningún repositorio externo</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 -->
@@ -249,10 +248,10 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid
<string name="biometric_prompt_title">Petición biométrica</string>
<string name="biometric_auth_error">Fallo de autenticación</string>
<string name="biometric_auth_error_reason">Fallou a autenticación: %s</string>
- <string name="biometric_auth_title">Activar autenticación biométrica</string>
- <string name="biometric_auth_summary">Ao activala, Password Store vaiche pedir a túa pegada dactilar ao iniciar a app</string>
- <string name="biometric_auth_summary_error">O hardware de pegada dixital non é accesible ou existente</string>
- <string name="ssh_openkeystore_clear_keyid">Eliminar o ID lembrado da chave SSH en OpenKeystore</string>
+ <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="access_sdcard_text">O almacenaxe está na tarxeta SD pero a app non ten permiso para acceder a el. Por favor concédelle permiso.</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>
@@ -265,16 +264,15 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid
<string name="message_error_destination_outside_repo">O destino debe estar dentro do repositorio</string>
<string name="message_rename_folder">Escribe o destino para %1$s</string>
<string name="button_create">Crear</string>
- <string name="pref_search_on_start">Abrir busca ao inicio</string>
- <string name="pref_search_on_start_hint">Abrir a barra de busca cando se inicia a app</string>
- <string name="password_generator_category_title">Creador de contrasinais</string>
+ <string name="pref_search_on_start_title">Abrir busca ao inicio</string>
+ <string name="pref_search_on_start_summary">Abrir a barra de busca cando se inicia a app</string>
<string name="tap_clear_clipboard">Toca aquí para baleirar o portapapeis</string>
<string name="clone_git_repo">Clonar un repositorio git para sincronizar os cambios</string>
- <string name="theme_title">Decorado da App</string>
- <string name="theme_light">Claro</string>
- <string name="theme_dark">Escuro</string>
- <string name="theme_battery_saver">Establecido polo Aforrador de enerxía</string>
- <string name="theme_follow_system">Por omisión do sistema</string>
+ <string name="pref_app_theme_title">Decorado da App</string>
+ <string name="pref_app_theme_value_light">Claro</string>
+ <string name="pref_app_theme_value_dark">Escuro</string>
+ <string name="pref_app_theme_value_battery_saver">Establecido polo Aforrador de enerxía</string>
+ <string name="pref_app_theme_value_follow_system">Por omisión do sistema</string>
<string name="connection_mode_ssh_key">Chave SSH</string>
<string name="connection_mode_basic_authentication">Contrasinal</string>
<string name="git_server_config_save_success">Configuración gardada</string>
@@ -354,7 +352,7 @@ a app desde unha fonte de confianza, como a Play Store, Amazon Appstore, F-Droid
<!-- Proxy configuration activity -->
<string name="proxy_hostname">Servidor proxy</string>
<string name="port">Porto</string>
- <string name="pref_proxy_settings">Axustes do proxy HTTP(S)</string>
+ <string name="pref_edit_proxy_settings">Axustes do proxy HTTP(S)</string>
<string name="invalid_proxy_url">URL non válido</string>
<string name="oreo_autofill_password_fill_and_conditional_save_support">Completa e garda contrasinais (gardar require que os servizos de accesibilidade non estén activados)</string>
<string name="clear_saved_host_key">Eliminar chave do host gardada</string>
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 5fcd812d..103283ca 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -93,43 +93,42 @@
<string name="share_as_plaintext">Condividi come testo semplice</string>
<string name="last_changed">Ultima modifica %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Repository</string>
- <string name="pref_edit_server_info">Modifica impostazioni del server di Git</string>
+ <string name="pref_category_repository_title">Repository</string>
+ <string name="pref_edit_git_server_settings">Modifica impostazioni del server di Git</string>
<string name="pref_edit_git_config">Configurazione &amp; utilità della configurazione di Git</string>
- <string name="pref_ssh_title">Importa chiave SSH</string>
+ <string name="pref_import_ssh_key_title">Importa chiave SSH</string>
<string name="pref_ssh_keygen_title">Genera coppia di chiavi SSH</string>
<string name="pref_ssh_see_key_title">Visualizza la chiave SSH pubblica generata</string>
- <string name="pref_git_delete_repo">Elimina repository</string>
+ <string name="pref_git_delete_repo_title">Elimina repository</string>
<string name="pref_dialog_delete_title">Cancella repository</string>
<string name="pref_category_general_title">Generale</string>
- <string name="pref_category_title_passwords">Password</string>
+ <string name="pref_category_passwords_title">Password</string>
<string name="pref_clipboard_timeout_title">Timeout copia della password</string>
<string name="pref_clipboard_timeout_summary">Imposta per quanto tempo (in secondi) vuoi che la password rimanga negli appunti. 0 significa per sempre. Valore corrente: %1$s</string>
<string name="pref_copy_title">Copia automaticamente la password</string>
- <string name="pref_copy_dialog_title">Copia automaticamente la password negli appunti dopo il successo della decifratura.</string>
+ <string name="pref_copy_summary">Copia automaticamente la password negli appunti dopo il successo della decifratura.</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">Il file selezionato non sembra essere una chiave privata SSH.</string>
<string name="ssh_key_success_dialog_title">Chiave SSH importata</string>
<string name="ssh_key_error_dialog_title">Errore di importazione della chiave</string>
<string name="ssh_key_error_dialog_text">Messaggio : \n</string>
- <string name="pref_recursive_filter">Filtro ricorsivo</string>
- <string name="pref_recursive_filter_hint">Trova ricorsivamente le password della directory corrente.</string>
+ <string name="pref_recursive_filter_title">Filtro ricorsivo</string>
+ <string name="pref_recursive_filter_summary">Trova ricorsivamente le password della directory corrente.</string>
<string name="pref_sort_order_title">Ordine password</string>
<string name="pref_folder_first_sort_order">Prima le cartelle</string>
<string name="pref_file_first_sort_order">Prima i file</string>
<string name="pref_type_independent_sort_order">Tipo indipendente</string>
<string name="pref_recently_used_sort_order">Usato di recente</string>
- <string name="pref_autofill_title">Auto-compilazione</string>
+ <string name="pref_category_autofill_title">Auto-compilazione</string>
<string name="pref_autofill_enable_title">Abilita Auto-Compilazione</string>
- <string name="pref_misc_title">Varie</string>
+ <string name="pref_category_misc_title">Varie</string>
<string name="pref_clear_clipboard_title">Cancella 20 volte gli appunti</string>
- <string name="pref_clear_clipboard_hint">Archivia i numeri consecutivi negli appunti per 20 volte. Utile sui telefoni Samsung che presentano la cronologia degli appunti.</string>
+ <string name="pref_clear_clipboard_summary">Archivia i numeri consecutivi negli appunti per 20 volte. Utile sui telefoni Samsung che presentano la cronologia degli appunti.</string>
<string name="pref_git_delete_repo_summary">Elimina repository locale (nascosta)</string>
- <string name="pref_external_repository">Repository Esterna</string>
+ <string name="pref_external_repository_title">Repository Esterna</string>
<string name="pref_external_repository_summary">Usa una repository di password esterna</string>
- <string name="pref_select_external_repository">Seleziona repository esterna</string>
+ <string name="pref_select_external_repository_title">Seleziona repository esterna</string>
<string name="prefs_export_passwords_title">Esporta password</string>
<string name="prefs_export_passwords_summary">Esporta le password crittografate ad una directory esterna</string>
- <string name="prefs_version">Versione</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Genera Password</string>
<string name="pwgen_generate">Genera</string>
@@ -151,12 +150,12 @@
<string name="xkpwgen_custom_dict_imported">Lista di parole personalizzata: %1$s</string>
<string name="xkpwgen_builder_error">Il dizionario selezionato non contiene abbastanza parole della data lunghezza %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Tipo di generatore di password</string>
- <string name="xkpwgen_pref_custom_dict_title">Lista di parole personalizzata</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Usando file di elenco di parole personalizzati</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Usando liste di parole integrate</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">File di elenco di parole personalizzato</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Tocca per selezionare un file di lista di parole personalizzato contenente una parola per riga</string>
+ <string name="pref_password_generator_type_title">Tipo di generatore di password</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista di parole personalizzata</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Usando file di elenco di parole personalizzati</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Usando liste di parole integrate</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">File di elenco di parole personalizzato</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Tocca per selezionare un file di lista di parole personalizzato contenente una parola per riga</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Frase Segreta</string>
<string name="ssh_keygen_generate">Genera</string>
@@ -196,7 +195,7 @@
<string name="show_extra_content_pref_summary">Controlla la visibilità del contenuto extra una volta decrittografato.</string>
<string name="pwd_generate_button">Genera</string>
<string name="refresh_list">Aggiorna elenco</string>
- <string name="no_repo_selected">Nessuna repository esterna selezionata</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Nessuna repository esterna selezionata</string>
<string name="send_plaintext_password_to">Invia password come testo semplice usando…</string>
<string name="app_icon_hint">Icona app</string>
<!-- Oreo Autofill -->
@@ -248,10 +247,10 @@
<string name="biometric_prompt_title">Richiesta Biometrica</string>
<string name="biometric_auth_error">Autenticazione non riuscita</string>
<string name="biometric_auth_error_reason">Autenticazione non riuscita: %s</string>
- <string name="biometric_auth_title">Abilita autenticazione biometrica</string>
- <string name="biometric_auth_summary">Quando abilitata, il Password Store ti chiederà la tua impronta digitale al lancio dell\'app</string>
- <string name="biometric_auth_summary_error">L\'hardware delle impronte digitali non è accessibile o mancante</string>
- <string name="ssh_openkeystore_clear_keyid">Elimina l\'ID della Chiave SSH di OpenKeystore memorizzato</string>
+ <string name="pref_biometric_auth_title">Abilita autenticazione biometrica</string>
+ <string name="pref_biometric_auth_summary">Quando abilitata, il Password Store ti chiederà la tua impronta digitale al lancio 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="access_sdcard_text">La posizione dell\'archiviazione nella tua Scheda SD o Archiviazione Interna, ma l\'app non ha i permessi per accedervi.</string>
<string name="your_public_key">La tua chiave pubblica</string>
<string name="error_generate_ssh_key">Errore provando a generare la chiave-ssh</string>
@@ -264,16 +263,15 @@
<string name="message_error_destination_outside_repo">La destinazione deve essere nella repository</string>
<string name="message_rename_folder">Inserire destinazione per %1$s</string>
<string name="button_create">Crea</string>
- <string name="pref_search_on_start">Apri ricerca all\'avvio</string>
- <string name="pref_search_on_start_hint">Apri la barra di ricerca al lancio dell\'app</string>
- <string name="password_generator_category_title">Generatore di Password</string>
+ <string name="pref_search_on_start_title">Apri ricerca all\'avvio</string>
+ <string name="pref_search_on_start_summary">Apri la barra di ricerca al lancio dell\'app</string>
<string name="tap_clear_clipboard">Tocca qui per cancellare gli appunti</string>
<string name="clone_git_repo">La repository deve essere clonata prima di sincronizzare le modifiche.</string>
- <string name="theme_title">Tema dell\'App</string>
- <string name="theme_light">Chiaro</string>
- <string name="theme_dark">Scuro</string>
- <string name="theme_battery_saver">Impostato dal Risparmio Energetico</string>
- <string name="theme_follow_system">Predefinito del sistema</string>
+ <string name="pref_app_theme_title">Tema dell\'App</string>
+ <string name="pref_app_theme_value_light">Chiaro</string>
+ <string name="pref_app_theme_value_dark">Scuro</string>
+ <string name="pref_app_theme_value_battery_saver">Impostato dal Risparmio Energetico</string>
+ <string name="pref_app_theme_value_follow_system">Predefinito del sistema</string>
<string name="connection_mode_ssh_key">Chiave SSH</string>
<string name="connection_mode_basic_authentication">Password</string>
<string name="git_server_config_save_success">Configurazione correttamente salvata</string>
@@ -353,7 +351,7 @@
<!-- Proxy configuration activity -->
<string name="proxy_hostname">Nome host del proxy</string>
<string name="port">Porta</string>
- <string name="pref_proxy_settings">Impostazioni proxy HTTP(S)</string>
+ <string name="pref_edit_proxy_settings">Impostazioni proxy HTTP(S)</string>
<string name="invalid_proxy_url">URL non valido</string>
<string name="oreo_autofill_password_fill_and_conditional_save_support">Compila e salva le password (il salvataggio necessita che nessun servizio di accessibilità sia abilitato)</string>
<string name="clear_saved_host_key">Cancella la chiave host salvata</string>
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index a26ca996..ff6b5fc5 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -93,43 +93,42 @@
<string name="share_as_plaintext">Compartilhar como texto</string>
<string name="last_changed">Última alteração %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Repositório</string>
- <string name="pref_edit_server_info">Editar configurações do servidor Git</string>
+ <string name="pref_category_repository_title">Repositório</string>
+ <string name="pref_edit_git_server_settings">Editar configurações do servidor Git</string>
<string name="pref_edit_git_config">Configuração local do Git &amp; utilitários</string>
- <string name="pref_ssh_title">Importar chave SSH</string>
+ <string name="pref_import_ssh_key_title">Importar chave SSH</string>
<string name="pref_ssh_keygen_title">Gerar um par de chave SSH</string>
<string name="pref_ssh_see_key_title">Ver a chave SSH pública gerada</string>
- <string name="pref_git_delete_repo">Excluir repositório</string>
+ <string name="pref_git_delete_repo_title">Excluir repositório</string>
<string name="pref_dialog_delete_title">Limpar repositório</string>
<string name="pref_category_general_title">Geral</string>
- <string name="pref_category_title_passwords">Senhas</string>
+ <string name="pref_category_passwords_title">Senhas</string>
<string name="pref_clipboard_timeout_title">Tempo limite de cópia da senha</string>
<string name="pref_clipboard_timeout_summary">Definir o tempo (em segundos) que a senha está na área de transferência. 0 significa para sempre. Valor atual: %1$s</string>
<string name="pref_copy_title">Copiar senha automaticamente</string>
- <string name="pref_copy_dialog_title">Automaticamente copie a senha para a área de transferência após a descriptografia ser bem sucedida.</string>
+ <string name="pref_copy_summary">Automaticamente copie a senha para a área de transferência após a descriptografia ser bem sucedida.</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">O arquivo selecionado não parece ser uma chave SSH privada.</string>
<string name="ssh_key_success_dialog_title">Chave SSH importada</string>
<string name="ssh_key_error_dialog_title">Erro ao importar chave</string>
<string name="ssh_key_error_dialog_text">Mensagem : \n</string>
- <string name="pref_recursive_filter">Filtragem recursiva</string>
- <string name="pref_recursive_filter_hint">Encontrar senhas do diretório corrente recursivamente.</string>
+ <string name="pref_recursive_filter_title">Filtragem recursiva</string>
+ <string name="pref_recursive_filter_summary">Encontrar senhas do diretório corrente recursivamente.</string>
<string name="pref_sort_order_title">Ordenação da Senha</string>
<string name="pref_folder_first_sort_order">Pastas primeiro</string>
<string name="pref_file_first_sort_order">Arquivos primeiro</string>
<string name="pref_type_independent_sort_order">Tipo independente</string>
<string name="pref_recently_used_sort_order">Usado recentemente</string>
- <string name="pref_autofill_title">Preenchimento Automático</string>
+ <string name="pref_category_autofill_title">Preenchimento Automático</string>
<string name="pref_autofill_enable_title">Ativar preenchimento automático</string>
- <string name="pref_misc_title">Outros</string>
+ <string name="pref_category_misc_title">Outros</string>
<string name="pref_clear_clipboard_title">Limpar área de transferência 20 vezes</string>
- <string name="pref_clear_clipboard_hint">Armazene números consecutivos na área de transferência 20 vezes. Útil em telefones Samsung que apresentam o histórico da área de transferência.</string>
+ <string name="pref_clear_clipboard_summary">Armazene números consecutivos na área de transferência 20 vezes. Útil em telefones Samsung que apresentam o histórico da área de transferência.</string>
<string name="pref_git_delete_repo_summary">Exclui o repositório local (oculto)</string>
- <string name="pref_external_repository">Repositório externo</string>
+ <string name="pref_external_repository_title">Repositório externo</string>
<string name="pref_external_repository_summary">Use um repositório de senha externo</string>
- <string name="pref_select_external_repository">Selecionar repositório externo</string>
+ <string name="pref_select_external_repository_title">Selecionar repositório externo</string>
<string name="prefs_export_passwords_title">Exportar senhas</string>
<string name="prefs_export_passwords_summary">Exporta as senhas criptografadas para um diretório externo</string>
- <string name="prefs_version">Versão</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Gerar Senha</string>
<string name="pwgen_generate">Gerar</string>
@@ -151,12 +150,12 @@
<string name="xkpwgen_custom_dict_imported">Lista de palavras personalizada: %1$s</string>
<string name="xkpwgen_builder_error">O dicionário selecionado não contém palavras suficientes de tamanho %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Tipo de gerador de senha</string>
- <string name="xkpwgen_pref_custom_dict_title">Lista de palavras personalizadas</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Usando um arquivo de Lista de Palavras</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Usando o arquivo de palavras embutido</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Lista de palavras personalizadas</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Toque para escolher um arquivo personalizado de lista de palavras contendo uma palavra por linha</string>
+ <string name="pref_password_generator_type_title">Tipo de gerador de senha</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Lista de palavras personalizadas</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Usando um arquivo de Lista de Palavras</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Usando o arquivo de palavras embutido</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Lista de palavras personalizadas</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Toque para escolher um arquivo personalizado de lista de palavras contendo uma palavra por linha</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Frase Secreta</string>
<string name="ssh_keygen_generate">Gerar</string>
@@ -196,7 +195,7 @@
<string name="show_extra_content_pref_summary">Controlar a visibilidade do conteúdo extra uma vez descriptografado.</string>
<string name="pwd_generate_button">Gerar</string>
<string name="refresh_list">Atualizar lista</string>
- <string name="no_repo_selected">Nenhum repositório externo selecionado</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Nenhum repositório externo selecionado</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 -->
@@ -247,10 +246,10 @@
<string name="biometric_prompt_title">Confirmação Biométrica</string>
<string name="biometric_auth_error">Falha de autenticação</string>
<string name="biometric_auth_error_reason">Falha de autenticação: %s</string>
- <string name="biometric_auth_title">Ativar autenticação biométrica</string>
- <string name="biometric_auth_summary">Quando ativado, o Password Store irá pedir a sua impressão digital ao iniciar o aplicativo</string>
- <string name="biometric_auth_summary_error">Hardware de impressão digital não acessível ou ausente</string>
- <string name="ssh_openkeystore_clear_keyid">Limpar ID de chave SSH lembrada do OpenKeystore</string>
+ <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="access_sdcard_text">O local do armazenamento está em seu cartão SD ou armazenamento interno, mas o aplicativo não tem permissão para acessá-lo.</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>
@@ -263,16 +262,15 @@
<string name="message_error_destination_outside_repo">O destino deve estar dentro do repositório</string>
<string name="message_rename_folder">Insira o destino para %1$s</string>
<string name="button_create">Criar</string>
- <string name="pref_search_on_start">Abrir pesquisa ao inicializar</string>
- <string name="pref_search_on_start_hint">Abrir barra de pesquisa quando o aplicativo for iniciado</string>
- <string name="password_generator_category_title">Gerador de Senha</string>
+ <string name="pref_search_on_start_title">Abrir pesquisa ao inicializar</string>
+ <string name="pref_search_on_start_summary">Abrir barra de pesquisa quando o aplicativo for iniciado</string>
<string name="tap_clear_clipboard">Toque aqui para limpar a área de transferência</string>
<string name="clone_git_repo">O repositório deve ser clonado antes de sincronizar as alterações.</string>
- <string name="theme_title">Tema do aplicativo</string>
- <string name="theme_light">Claro</string>
- <string name="theme_dark">Escuro</string>
- <string name="theme_battery_saver">Configurado pela Economia de bateria</string>
- <string name="theme_follow_system">Padrão do sistema</string>
+ <string name="pref_app_theme_title">Tema do aplicativo</string>
+ <string name="pref_app_theme_value_light">Claro</string>
+ <string name="pref_app_theme_value_dark">Escuro</string>
+ <string name="pref_app_theme_value_battery_saver">Configurado pela Economia de bateria</string>
+ <string name="pref_app_theme_value_follow_system">Padrão do sistema</string>
<string name="connection_mode_ssh_key">Chave SSH</string>
<string name="connection_mode_basic_authentication">Senha</string>
<string name="git_server_config_save_success">Configuração salva com sucesso</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e8ca9488..f0e3c6d4 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -97,43 +97,42 @@
<string name="share_as_plaintext">Поделиться в открытом виде</string>
<string name="last_changed">Последние изменение %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Репозиторий</string>
- <string name="pref_edit_server_info">Изменить настройки сервера Git</string>
+ <string name="pref_category_repository_title">Репозиторий</string>
+ <string name="pref_edit_git_server_settings">Изменить настройки сервера Git</string>
<string name="pref_edit_git_config">Конфигурация локального Git</string>
- <string name="pref_ssh_title">Импортировать SSH ключ</string>
+ <string name="pref_import_ssh_key_title">Импортировать SSH ключ</string>
<string name="pref_ssh_keygen_title">Сгенерировать пару SSH ключей</string>
<string name="pref_ssh_see_key_title">Просмотреть публичный SSH ключ</string>
- <string name="pref_git_delete_repo">Удалить репозиторий</string>
+ <string name="pref_git_delete_repo_title">Удалить репозиторий</string>
<string name="pref_dialog_delete_title">Очистить репозиторий</string>
<string name="pref_category_general_title">Общие</string>
- <string name="pref_category_title_passwords">Пароли</string>
+ <string name="pref_category_passwords_title">Пароли</string>
<string name="pref_clipboard_timeout_title">Срок хранения пароля в буфере обмена</string>
<string name="pref_clipboard_timeout_summary">Установите время (в секундах), которое вы хотите, чтобы пароль был в буфере обмена. 0 означает навсегда. Текущее значение: %1$s</string>
<string name="pref_copy_title">Автоматически копировать пароль</string>
- <string name="pref_copy_dialog_title">Автоматически копировать пароль в буфер обмена после успешного расшифрования</string>
+ <string name="pref_copy_summary">Автоматически копировать пароль в буфер обмена после успешного расшифрования</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">Выбранный файл не похож на приватный SSH-ключ.</string>
<string name="ssh_key_success_dialog_title">SSH ключ импортирован</string>
<string name="ssh_key_error_dialog_title">Ошибка импорта ключа</string>
<string name="ssh_key_error_dialog_text">Сообщение: \n</string>
- <string name="pref_recursive_filter">Рекурсивная фильтрация</string>
- <string name="pref_recursive_filter_hint">Рекурсивный поиск паролей в текущей директории</string>
+ <string name="pref_recursive_filter_title">Рекурсивная фильтрация</string>
+ <string name="pref_recursive_filter_summary">Рекурсивный поиск паролей в текущей директории</string>
<string name="pref_sort_order_title">Порядок сортировки паролей</string>
<string name="pref_folder_first_sort_order">Сначала папки</string>
<string name="pref_file_first_sort_order">Сначала файлы</string>
<string name="pref_type_independent_sort_order">Типонезависимый</string>
<string name="pref_recently_used_sort_order">Недавно использованные</string>
- <string name="pref_autofill_title">Автозаполнение</string>
+ <string name="pref_category_autofill_title">Автозаполнение</string>
<string name="pref_autofill_enable_title">Включить автозаполнение</string>
- <string name="pref_misc_title">Другое</string>
+ <string name="pref_category_misc_title">Другое</string>
<string name="pref_clear_clipboard_title">Очистить буфер 20 раз</string>
- <string name="pref_clear_clipboard_hint">Вставлять чепуху в буфер обмена 20 раз вместо одного. Полезно на некоторых моделях телефонов с запоминанием истории буфера обмена</string>
+ <string name="pref_clear_clipboard_summary">Вставлять чепуху в буфер обмена 20 раз вместо одного. Полезно на некоторых моделях телефонов с запоминанием истории буфера обмена</string>
<string name="pref_git_delete_repo_summary">Удалить локальный (скрытый) репозиторий</string>
- <string name="pref_external_repository">Внешний репозиторий</string>
+ <string name="pref_external_repository_title">Внешний репозиторий</string>
<string name="pref_external_repository_summary">Использовать внешний репозиторий</string>
- <string name="pref_select_external_repository">Выбрать внешний репозиторий</string>
+ <string name="pref_select_external_repository_title">Выбрать внешний репозиторий</string>
<string name="prefs_export_passwords_title">Экспортировать пароли</string>
<string name="prefs_export_passwords_summary">Экспортировать пароли в открытом виде во внешнее хранилище</string>
- <string name="prefs_version">Версия</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Сгенерировать пароль</string>
<string name="pwgen_generate">Сгенерировать</string>
@@ -155,12 +154,12 @@
<string name="xkpwgen_custom_dict_imported">Пользовательский список слов: %1$s</string>
<string name="xkpwgen_builder_error">Выбранный словарь не содержит достаточного количества слова заданной длинны %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Тип генератора паролей</string>
- <string name="xkpwgen_pref_custom_dict_title">Пользовательский список слов</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Использовать файл списка слов созданный пользователем</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Использовать встроенный список слов</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Файл пользовательского списка слов</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Нажмите чтобы выбрать файл пользовательского списка слов содержащий одно слово на строку</string>
+ <string name="pref_password_generator_type_title">Тип генератора паролей</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Пользовательский список слов</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Использовать файл списка слов созданный пользователем</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Использовать встроенный список слов</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Файл пользовательского списка слов</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Нажмите чтобы выбрать файл пользовательского списка слов содержащий одно слово на строку</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Пароль</string>
<string name="ssh_keygen_generate">Сгенерировать</string>
@@ -200,7 +199,7 @@
<string name="show_extra_content_pref_summary">Видимость поля дополнительной информации после расшифрования</string>
<string name="pwd_generate_button">Сгенерировать</string>
<string name="refresh_list">Обновить список</string>
- <string name="no_repo_selected">Внешний репозиторий не выбран</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">Внешний репозиторий не выбран</string>
<string name="send_plaintext_password_to">Поделиться паролем в открытом виде с помощью</string>
<string name="app_icon_hint">Иконка приложения</string>
<!-- Oreo Autofill -->
@@ -251,10 +250,10 @@
<string name="biometric_prompt_title">Запрос биометрии</string>
<string name="biometric_auth_error">Ошибка авторизации</string>
<string name="biometric_auth_error_reason">Ошибка аутентификации: %s</string>
- <string name="biometric_auth_title">Включить биометрическую аутентификацию</string>
- <string name="biometric_auth_summary">Когда ключено, Password Store будет запрашивать ваш опечаток пальца при каждом запуске приложения</string>
- <string name="biometric_auth_summary_error">Сенсор отпечатка пальца не доступен или отсутствует</string>
- <string name="ssh_openkeystore_clear_keyid">Очистить сохраненный SSH Key идентификатор OpenKystortore</string>
+ <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="access_sdcard_text">Хранилище расположено на вашей SD-карте или во внутреннем хранилище, но у приложения нет разрешения на доступ к нему.</string>
<string name="your_public_key">Ваш публичный ключ</string>
<string name="error_generate_ssh_key">Возникла ошибка при попытке генерации ssh ключа</string>
@@ -267,16 +266,15 @@
<string name="message_error_destination_outside_repo">Путь должен указывать на область внутри репозитория</string>
<string name="message_rename_folder">Введите адрес назначения для %1$s</string>
<string name="button_create">Создать</string>
- <string name="pref_search_on_start">Открыть поиск на старте</string>
- <string name="pref_search_on_start_hint">Открыть панель поиска при запуске приложения</string>
- <string name="password_generator_category_title">Генератор паролей</string>
+ <string name="pref_search_on_start_title">Открыть поиск на старте</string>
+ <string name="pref_search_on_start_summary">Открыть панель поиска при запуске приложения</string>
<string name="tap_clear_clipboard">Нажмите здесь чтобы очистить буфер обмена</string>
<string name="clone_git_repo">Для синхронизации изменений клонируйте git репозиторий</string>
- <string name="theme_title">Тема приложения</string>
- <string name="theme_light">Светлая</string>
- <string name="theme_dark">Темная</string>
- <string name="theme_battery_saver">Задается экономией батареи</string>
- <string name="theme_follow_system">Системная</string>
+ <string name="pref_app_theme_title">Тема приложения</string>
+ <string name="pref_app_theme_value_light">Светлая</string>
+ <string name="pref_app_theme_value_dark">Темная</string>
+ <string name="pref_app_theme_value_battery_saver">Задается экономией батареи</string>
+ <string name="pref_app_theme_value_follow_system">Системная</string>
<string name="connection_mode_ssh_key">SSH ключ</string>
<string name="connection_mode_basic_authentication">Пароль</string>
<string name="git_server_config_save_success">Конфигурация успешно сохранена</string>
diff --git a/app/src/main/res/values-v29/arrays.xml b/app/src/main/res/values-v29/arrays.xml
index 97443b9f..13dfa87e 100644
--- a/app/src/main/res/values-v29/arrays.xml
+++ b/app/src/main/res/values-v29/arrays.xml
@@ -5,9 +5,9 @@
<resources>
<string-array name="app_theme_options">
- <item>@string/theme_light</item>
- <item>@string/theme_dark</item>
- <item>@string/theme_follow_system</item>
+ <item>@string/pref_app_theme_value_light</item>
+ <item>@string/pref_app_theme_value_dark</item>
+ <item>@string/pref_app_theme_value_follow_system</item>
</string-array>
<string-array name="app_theme_values">
<item>light</item>
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 2b320f31..a55e37b2 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -41,9 +41,9 @@
<item>directory</item>
</string-array>
<string-array name="app_theme_options">
- <item>@string/theme_light</item>
- <item>@string/theme_dark</item>
- <item>@string/theme_battery_saver</item>
+ <item>@string/pref_app_theme_value_light</item>
+ <item>@string/pref_app_theme_value_dark</item>
+ <item>@string/pref_app_theme_value_battery_saver</item>
</string-array>
<string-array name="app_theme_values">
<item>light</item>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a370d230..46e85334 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -110,43 +110,43 @@
<string name="last_changed">Last changed %s</string>
<!-- Preferences -->
- <string name="pref_repository_title">Repository</string>
- <string name="pref_edit_server_info">Edit Git server settings</string>
+ <string name="pref_category_repository_title">Repository</string>
+ <string name="pref_edit_git_server_settings">Edit Git server settings</string>
<string name="pref_edit_git_config">Local Git config &amp; utilities</string>
- <string name="pref_ssh_title">Import SSH key</string>
+ <string name="pref_import_ssh_key_title">Import SSH key</string>
<string name="pref_ssh_keygen_title">Generate SSH key pair</string>
<string name="pref_ssh_see_key_title">View generated public SSH key</string>
- <string name="pref_git_delete_repo">Delete repository</string>
+ <string name="pref_git_delete_repo_title">Delete repository</string>
<string name="pref_dialog_delete_title">Clear repository</string>
<string name="pref_category_general_title">General</string>
- <string name="pref_category_title_passwords">Passwords</string>
+ <string name="pref_category_passwords_title">Passwords</string>
<string name="pref_clipboard_timeout_title">Password copy timeout</string>
<string name="pref_clipboard_timeout_summary">Set the time (in seconds) you want the password to be in clipboard. 0 means forever. Current value: %1$s</string>
<string name="pref_copy_title">Automatically copy password</string>
- <string name="pref_copy_dialog_title">Automatically copy the password to the clipboard after decryption was successful.</string>
+ <string name="pref_copy_summary">Automatically copy the password to the clipboard after decryption was successful.</string>
<string name="ssh_key_import_error_not_an_ssh_key_message">Selected file does not appear to be an SSH private key.</string>
<string name="ssh_key_success_dialog_title">SSH-key imported</string>
<string name="ssh_key_error_dialog_title">Key import error</string>
<string name="ssh_key_error_dialog_text">Message : \n</string>
- <string name="pref_recursive_filter">Recursive filtering</string>
- <string name="pref_recursive_filter_hint">Recursively find passwords of the current directory.</string>
+ <string name="pref_recursive_filter_title">Recursive filtering</string>
+ <string name="pref_recursive_filter_summary">Recursively find passwords of the current directory.</string>
<string name="pref_sort_order_title">Password sort order</string>
<string name="pref_folder_first_sort_order">Folders first</string>
<string name="pref_file_first_sort_order">Files first</string>
<string name="pref_type_independent_sort_order">Type independent</string>
<string name="pref_recently_used_sort_order">Recently used</string>
- <string name="pref_autofill_title">Autofill</string>
+ <string name="pref_category_autofill_title">Autofill</string>
<string name="pref_autofill_enable_title">Enable Autofill</string>
- <string name="pref_misc_title">Misc</string>
+ <string name="pref_category_misc_title">Misc</string>
<string name="pref_clear_clipboard_title">Clear clipboard 20 times</string>
- <string name="pref_clear_clipboard_hint">Store consecutive numbers in the clipboard 20 times. Useful on Samsung phones that feature clipboard history.</string>
+ <string name="pref_clear_clipboard_summary">Store consecutive numbers in the clipboard 20 times. Useful on Samsung phones that feature clipboard history.</string>
<string name="pref_git_delete_repo_summary">Deletes local (hidden) repository</string>
- <string name="pref_external_repository">External repository</string>
+ <string name="pref_external_repository_title">External repository</string>
<string name="pref_external_repository_summary">Use an external password repository</string>
- <string name="pref_select_external_repository">Select external repository</string>
+ <string name="pref_select_external_repository_title">Select external repository</string>
+ <string name="pref_select_external_repository_summary_no_repo_selected">No external repository selected</string>
<string name="prefs_export_passwords_title">Export passwords</string>
<string name="prefs_export_passwords_summary">Exports the encrypted passwords to an external directory</string>
- <string name="prefs_version">Version</string>
<!-- PasswordGenerator fragment -->
<string name="pwgen_title">Generate Password</string>
@@ -171,12 +171,12 @@
<string name="xkpwgen_builder_error">Selected dictionary does not contain enough words of given length %1$d..%2$d</string>
<!-- XKPWD prefs -->
- <string name="xkpwgen_pref_gentype_title">Password generator type</string>
- <string name="xkpwgen_pref_custom_dict_title">Custom wordlist</string>
- <string name="xkpwgen_pref_custom_dict_summary_on">Using custom wordlist file</string>
- <string name="xkpwgen_pref_custom_dict_summary_off">Using built-in wordlist</string>
- <string name="xkpwgen_pref_custom_dict_picker_title">Custom worldlist file</string>
- <string name="xkpwgen_pref_custom_dict_picker_summary">Tap to pick a custom wordlist file containing one word per line</string>
+ <string name="pref_password_generator_type_title">Password generator type</string>
+ <string name="pref_xkpwgen_custom_wordlist_enabled_title">Custom wordlist</string>
+ <string name="pref_xkpwgen_custom_dict_summary_on">Using custom wordlist file</string>
+ <string name="pref_xkpwgen_custom_dict_summary_off">Using built-in wordlist</string>
+ <string name="pref_xkpwgen_custom_dict_picker_title">Custom worldlist file</string>
+ <string name="pref_xkpwgen_custom_dict_picker_summary">Tap to pick a custom wordlist file containing one word per line</string>
<!-- ssh keygen fragment -->
<string name="ssh_keygen_passphrase">Passphrase</string>
@@ -220,7 +220,6 @@
<string name="show_extra_content_pref_summary">Control the visibility of the extra content once decrypted.</string>
<string name="pwd_generate_button">Generate</string>
<string name="refresh_list">Refresh list</string>
- <string name="no_repo_selected">No external repository selected</string>
<string name="send_plaintext_password_to">Send password as plaintext using…</string>
<string name="app_icon_hint">App icon</string>
@@ -271,14 +270,15 @@
<string name="git_head_missing">Unable to locate HEAD</string>
<string name="sdcard_root_warning_title">SD-Card root selected</string>
<string name="sdcard_root_warning_message">You have selected the root of your sdcard for the store. This is extremely dangerous and you will lose your data as its content will, eventually, be deleted</string>
+ <string name="sdcard_root_warning_remove_everything">Remove everything</string>
<string name="git_abort_and_push_title">Abort and Push</string>
<string name="biometric_prompt_title">Biometric Prompt</string>
<string name="biometric_auth_error">Authentication failure</string>
<string name="biometric_auth_error_reason">Authentication failure: %s</string>
- <string name="biometric_auth_title">Enable biometric authentication</string>
- <string name="biometric_auth_summary">When enabled, Password Store will prompt you for your fingerprint when launching the app</string>
- <string name="biometric_auth_summary_error">Fingerprint hardware not accessible or missing</string>
- <string name="ssh_openkeystore_clear_keyid">Clear remembered OpenKeystore SSH Key ID</string>
+ <string name="pref_biometric_auth_title">Enable biometric authentication</string>
+ <string name="pref_biometric_auth_summary">When enabled, Password Store 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_title_openkeystore_clear_keyid">Clear remembered OpenKeystore SSH Key ID</string>
<string name="access_sdcard_text">The store location is in your SD Card or Internal storage, but the app does not have permission to access it.</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>
@@ -291,16 +291,15 @@
<string name="message_error_destination_outside_repo">Destination must be within the repository</string>
<string name="message_rename_folder">Enter destination for %1$s</string>
<string name="button_create">Create</string>
- <string name="pref_search_on_start">Open search on start</string>
- <string name="pref_search_on_start_hint">Open search bar when app is launched</string>
- <string name="password_generator_category_title">Password Generator</string>
+ <string name="pref_search_on_start_title">Open search on start</string>
+ <string name="pref_search_on_start_summary">Open search bar when app is launched</string>
<string name="tap_clear_clipboard">Tap here to clear clipboard</string>
<string name="clone_git_repo">The repository must be cloned before syncing changes.</string>
- <string name="theme_title">App theme</string>
- <string name="theme_light">Light</string>
- <string name="theme_dark">Dark</string>
- <string name="theme_battery_saver">Set by Battery Saver</string>
- <string name="theme_follow_system">System default</string>
+ <string name="pref_app_theme_title">App theme</string>
+ <string name="pref_app_theme_value_light">Light</string>
+ <string name="pref_app_theme_value_dark">Dark</string>
+ <string name="pref_app_theme_value_battery_saver">Set by Battery Saver</string>
+ <string name="pref_app_theme_value_follow_system">System default</string>
<string name="connection_mode_ssh_key">SSH key</string>
<string name="connection_mode_basic_authentication">Password</string>
<string name="connection_mode_openkeychain" translatable="false">OpenKeychain</string>
@@ -389,7 +388,7 @@
<!-- Proxy configuration activity -->
<string name="proxy_hostname">Proxy hostname</string>
<string name="port">Port</string>
- <string name="pref_proxy_settings">HTTP(S) proxy settings</string>
+ <string name="pref_edit_proxy_settings">HTTP(S) proxy settings</string>
<string name="invalid_proxy_url">Invalid URL</string>
<string name="oreo_autofill_password_fill_and_conditional_save_support">Fill and save passwords (saving requires that no accessibility services are enabled)</string>
<string name="clear_saved_host_key">Clear saved host key</string>
diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml
deleted file mode 100644
index c0b3a105..00000000
--- a/app/src/main/res/xml/preference.xml
+++ /dev/null
@@ -1,174 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <PreferenceCategory app:title="@string/pref_autofill_title">
- <SwitchPreferenceCompat
- app:defaultValue="true"
- app:key="autofill_enable"
- app:title="@string/pref_autofill_enable_title" />
- <ListPreference
- app:defaultValue="file"
- app:entries="@array/oreo_autofill_directory_structure_entries"
- app:entryValues="@array/oreo_autofill_directory_structure_values"
- app:key="oreo_autofill_directory_structure"
- app:title="@string/oreo_autofill_preference_directory_structure"
- app:useSimpleSummaryProvider="true" />
- <EditTextPreference
- app:key="oreo_autofill_default_username"
- app:summary="@string/preference_default_username_summary"
- app:title="@string/preference_default_username_title" />
- <EditTextPreference
- app:key="oreo_autofill_custom_public_suffixes"
- app:summary="@string/preference_custom_public_suffixes_summary"
- app:title="@string/preference_custom_public_suffixes_title" />
- </PreferenceCategory>
-
- <PreferenceCategory app:title="@string/pref_repository_title">
- <Preference
- app:key="git_server_info"
- app:title="@string/pref_edit_server_info" />
- <Preference
- app:key="proxy_settings"
- app:title="@string/pref_proxy_settings" />
- <Preference
- app:key="git_config"
- app:title="@string/pref_edit_git_config" />
- <Preference
- app:key="ssh_key"
- app:title="@string/pref_ssh_title" />
- <Preference
- app:key="ssh_keygen"
- app:title="@string/pref_ssh_keygen_title" />
- <Preference app:key="clear_saved_pass" />
- <Preference
- app:key="ssh_openkeystore_clear_keyid"
- app:title="@string/ssh_openkeystore_clear_keyid" />
- <Preference
- app:key="ssh_see_key"
- app:title="@string/pref_ssh_see_key_title" />
- <Preference
- app:key="git_delete_repo"
- app:summary="@string/pref_git_delete_repo_summary"
- app:title="@string/pref_git_delete_repo" />
- <CheckBoxPreference
- app:key="git_external"
- app:summary="@string/pref_external_repository_summary"
- app:title="@string/pref_external_repository" />
- <Preference
- app:dependency="git_external"
- app:key="pref_select_external"
- app:title="@string/pref_select_external_repository" />
- </PreferenceCategory>
-
- <PreferenceCategory app:title="@string/password_generator_category_title">
- <ListPreference
- app:defaultValue="classic"
- app:entries="@array/pwgen_provider_labels"
- app:entryValues="@array/pwgen_provider_values"
- app:key="pref_key_pwgen_type"
- app:persistent="true"
- app:title="@string/xkpwgen_pref_gentype_title"
- app:useSimpleSummaryProvider="true" />
- <CheckBoxPreference
- app:key="pref_key_is_custom_dict"
- app:summaryOff="@string/xkpwgen_pref_custom_dict_summary_off"
- app:summaryOn="@string/xkpwgen_pref_custom_dict_summary_on"
- app:title="@string/xkpwgen_pref_custom_dict_title" />
- <Preference
- app:dependency="pref_key_is_custom_dict"
- app:key="pref_key_custom_dict"
- app:summary="@string/xkpwgen_pref_custom_dict_picker_summary"
- app:title="@string/xkpwgen_pref_custom_dict_picker_title" />
- </PreferenceCategory>
-
- <PreferenceCategory app:title="@string/pref_category_general_title">
- <ListPreference
- android:defaultValue="@string/app_theme_def"
- android:entries="@array/app_theme_options"
- android:entryValues="@array/app_theme_values"
- android:key="app_theme"
- android:summary="%s"
- android:title="@string/theme_title" />
- <ListPreference
- app:defaultValue="FOLDER_FIRST"
- app:entries="@array/sort_order_entries"
- app:entryValues="@array/sort_order_values"
- app:key="sort_order"
- app:persistent="true"
- app:summary="%s"
- app:title="@string/pref_sort_order_title" />
- <CheckBoxPreference
- app:defaultValue="true"
- app:key="filter_recursively"
- app:summary="@string/pref_recursive_filter_hint"
- app:title="@string/pref_recursive_filter" />
- <CheckBoxPreference
- app:defaultValue="false"
- app:key="search_on_start"
- app:summary="@string/pref_search_on_start_hint"
- app:title="@string/pref_search_on_start" />
- <CheckBoxPreference
- app:defaultValue="false"
- app:key="show_hidden_contents"
- app:persistent="true"
- app:summary="@string/pref_show_hidden_summary"
- app:title="@string/pref_show_hidden_title" />
- <CheckBoxPreference
- app:key="biometric_auth"
- app:summary="@string/biometric_auth_summary"
- app:title="@string/biometric_auth_title" />
- </PreferenceCategory>
- <PreferenceCategory app:title="@string/pref_category_title_passwords">
- <EditTextPreference
- android:inputType="number"
- app:defaultValue="45"
- app:key="general_show_time"
- app:summary="@string/pref_clipboard_timeout_summary"
- app:title="@string/pref_clipboard_timeout_title" />
- <CheckBoxPreference
- app:defaultValue="true"
- app:key="show_password"
- app:summary="@string/show_password_pref_summary"
- app:title="@string/show_password_pref_title" />
- <CheckBoxPreference
- app:defaultValue="true"
- app:key="show_extra_content"
- app:summary="@string/show_extra_content_pref_summary"
- app:title="@string/show_extra_content_pref_title" />
- <CheckBoxPreference
- app:defaultValue="false"
- app:dialogTitle="@string/pref_copy_dialog_title"
- app:key="copy_on_decrypt"
- app:summary="@string/pref_copy_dialog_title"
- app:title="@string/pref_copy_title" />
- </PreferenceCategory>
-
- <PreferenceCategory app:title="@string/pref_misc_title">
- <Preference
- app:key="export_passwords"
- app:summary="@string/prefs_export_passwords_summary"
- app:title="@string/prefs_export_passwords_title" />
-
- <CheckBoxPreference
- app:defaultValue="false"
- app:key="clear_clipboard_20x"
- app:summary="@string/pref_clear_clipboard_hint"
- app:title="@string/pref_clear_clipboard_title" />
- <CheckBoxPreference
- app:defaultValue="false"
- app:key="enable_debug_logging"
- app:summary="@string/pref_debug_logging_summary"
- app:title="@string/pref_debug_logging_title" />
- </PreferenceCategory>
-
- <PreferenceCategory>
- <Preference
- app:icon="@mipmap/ic_launcher"
- app:key="app_version"
- app:title="@string/prefs_version" />
- </PreferenceCategory>
-</PreferenceScreen>
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 32abf6ab..8cffd7c2 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -53,6 +53,7 @@ object Dependencies {
const val jgit = "org.eclipse.jgit:org.eclipse.jgit:3.7.1.201504261725-r"
const val kotlin_result = "com.michael-bull.kotlin-result:kotlin-result:1.1.9"
const val leakcanary = "com.squareup.leakcanary:leakcanary-android:2.5"
+ const val modern_android_prefs = "de.Maxr1998.android:modernpreferences:1.2.0-alpha1"
const val plumber = "com.squareup.leakcanary:plumber-android:2.5"
const val sshj = "com.hierynomus:sshj:0.30.0"
const val ssh_auth = "org.sufficientlysecure:sshauthentication-api:1.0"