diff options
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 & 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 & 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 & 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 & 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 & 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" |