summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml64
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/data/repo/PasswordRepository.kt8
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/CloneFragment.kt30
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt198
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/passwords/PasswordStore.kt49
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt59
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt93
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/settings/Migrations.kt16
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt7
-rw-r--r--app/src/main/res/layout/fragment_repo_location.xml95
-rw-r--r--app/src/main/res/xml/backup_content.xml2
-rw-r--r--app/src/main/res/xml/data_extraction_rules.xml5
12 files changed, 134 insertions, 492 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ce40be47..aed314aa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,10 +8,11 @@
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <!-- Required by Autofill to verify the certificate hashes of packages -->
+ <uses-permission
+ android:name="android.permission.QUERY_ALL_PACKAGES"
+ tools:ignore="QueryAllPackagesPermission" />
<uses-feature
android:name="android.hardware.touchscreen"
@@ -23,23 +24,28 @@
<application
android:name="dev.msfjarvis.aps.Application"
android:allowBackup="false"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_content"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/AppThemeM3"
- tools:ignore="GoogleAppIndexingWarning">
+ tools:ignore="GoogleAppIndexingWarning"
+ tools:targetApi="s">
<activity
android:name="dev.msfjarvis.aps.ui.passwords.PasswordStore"
- android:configChanges="orientation|screenSize" />
+ android:configChanges="orientation|screenSize"
+ android:exported="false" />
<activity
android:name="dev.msfjarvis.aps.ui.onboarding.activity.OnboardingActivity"
- android:configChanges="orientation|screenSize" />
+ android:configChanges="orientation|screenSize"
+ android:exported="false" />
<activity
android:name="dev.msfjarvis.aps.ui.proxy.ProxySelectorActivity"
+ android:exported="false"
android:windowSoftInputMode="adjustResize" />
<activity
@@ -60,6 +66,7 @@
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:clearTaskOnLaunch="true"
+ android:exported="false"
android:stateNotNeeded="true"
android:theme="@style/zxing_CaptureTheme"
android:windowSoftInputMode="stateAlwaysHidden"
@@ -67,50 +74,56 @@
<activity
android:name="dev.msfjarvis.aps.ui.git.config.GitServerConfigActivity"
+ android:exported="false"
android:label="@string/title_activity_git_clone"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.git.config.GitConfigActivity"
+ android:exported="false"
android:label="@string/title_activity_git_config"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.git.log.GitLogActivity"
+ android:exported="false"
android:label="@string/title_activity_git_log" />
<activity
android:name="dev.msfjarvis.aps.ui.settings.SettingsActivity"
+ android:exported="false"
android:label="@string/action_settings"
android:parentActivityName=".ui.passwords.PasswordStore" />
<activity
- android:name="dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity"
- android:theme="@style/NoBackgroundThemeM3" />
-
- <activity
android:name="dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity"
+ android:exported="false"
android:label="@string/new_password_title"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.crypto.PasswordCreationActivityV2"
+ android:exported="false"
android:label="@string/new_password_title"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.crypto.DecryptActivity"
+ android:exported="false"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.crypto.GetKeyIdsActivity"
+ android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />
<service
android:name="dev.msfjarvis.aps.util.services.ClipboardService"
+ android:exported="false"
android:process=":clipboard_service_process" />
<service
android:name="dev.msfjarvis.aps.util.services.PasswordExportService"
+ android:exported="false"
android:process=":password_export_service_process" />
<service
android:name="dev.msfjarvis.aps.util.services.OreoAutofillService"
@@ -124,41 +137,66 @@
android:resource="@xml/oreo_autofill_service" />
</service>
- <activity android:name="dev.msfjarvis.aps.ui.folderselect.SelectFolderActivity" />
+ <activity
+ android:name="dev.msfjarvis.aps.ui.folderselect.SelectFolderActivity"
+ android:exported="false" />
<activity
android:name="dev.msfjarvis.aps.ui.sshkeygen.SshKeyImportActivity"
+ android:exported="false"
android:theme="@style/NoBackgroundThemeM3"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.sshkeygen.SshKeyGenActivity"
+ android:exported="false"
android:label="@string/pref_ssh_keygen_title"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivity"
+ android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />
<activity
android:name="dev.msfjarvis.aps.ui.autofill.AutofillDecryptActivityV2"
+ android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />
<activity
android:name="dev.msfjarvis.aps.ui.autofill.AutofillFilterView"
android:configChanges="orientation|keyboardHidden"
+ android:exported="false"
android:theme="@style/DialogLikeThemeM3"
android:windowSoftInputMode="adjustNothing" />
<activity
android:name="dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity"
+ android:exported="false"
android:theme="@style/NoBackgroundThemeM3" />
<activity
android:name="dev.msfjarvis.aps.autofill.oreo.ui.AutofillSmsActivity"
android:configChanges="orientation"
+ android:exported="false"
android:theme="@style/DialogLikeThemeM3"
android:windowSoftInputMode="adjustNothing" />
<activity
android:name="dev.msfjarvis.aps.ui.autofill.AutofillPublisherChangedActivity"
android:configChanges="orientation|keyboardHidden"
+ android:exported="false"
android:theme="@style/DialogLikeThemeM3"
android:windowSoftInputMode="adjustNothing" />
- <activity android:name="dev.msfjarvis.aps.ui.pgp.PGPKeyImportActivity"
+ <activity
+ android:name="dev.msfjarvis.aps.ui.pgp.PGPKeyImportActivity"
android:theme="@style/NoBackgroundThemeM3" />
</application>
+ <queries>
+ <package android:name="org.sufficientlysecure.keychain" />
+
+ <intent>
+ <action android:name="android.intent.action.SEND" />
+ <data android:mimeType="text/plain" />
+ </intent>
+ <intent>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="https" />
+ </intent>
+ </queries>
+
</manifest>
diff --git a/app/src/main/java/dev/msfjarvis/aps/data/repo/PasswordRepository.kt b/app/src/main/java/dev/msfjarvis/aps/data/repo/PasswordRepository.kt
index a3cf2dda..9bf1bce6 100644
--- a/app/src/main/java/dev/msfjarvis/aps/data/repo/PasswordRepository.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/data/repo/PasswordRepository.kt
@@ -10,7 +10,6 @@ import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching
import dev.msfjarvis.aps.Application
import dev.msfjarvis.aps.data.password.PasswordItem
-import dev.msfjarvis.aps.util.extensions.getString
import dev.msfjarvis.aps.util.extensions.sharedPrefs
import dev.msfjarvis.aps.util.extensions.unsafeLazy
import dev.msfjarvis.aps.util.settings.PasswordSortOrder
@@ -106,12 +105,7 @@ object PasswordRepository {
}
fun getRepositoryDirectory(): File {
- return if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
- val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- if (externalRepo != null) File(externalRepo) else File(filesDir.toString(), "/store")
- } else {
- File(filesDir.toString(), "/store")
- }
+ return File(filesDir.toString(), "/store")
}
fun initialize(): Repository? {
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/CloneFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/CloneFragment.kt
index 603c016b..16c1a784 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/CloneFragment.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/CloneFragment.kt
@@ -11,7 +11,10 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.fragment.app.Fragment
+import com.github.michaelbull.result.onFailure
+import com.github.michaelbull.result.runCatching
import dev.msfjarvis.aps.R
+import dev.msfjarvis.aps.data.repo.PasswordRepository
import dev.msfjarvis.aps.databinding.FragmentCloneBinding
import dev.msfjarvis.aps.ui.git.config.GitServerConfigActivity
import dev.msfjarvis.aps.util.extensions.finish
@@ -20,6 +23,9 @@ import dev.msfjarvis.aps.util.extensions.sharedPrefs
import dev.msfjarvis.aps.util.extensions.unsafeLazy
import dev.msfjarvis.aps.util.extensions.viewBinding
import dev.msfjarvis.aps.util.settings.PreferenceKeys
+import logcat.LogPriority.ERROR
+import logcat.asLog
+import logcat.logcat
class CloneFragment : Fragment(R.layout.fragment_clone) {
@@ -38,17 +44,33 @@ class CloneFragment : Fragment(R.layout.fragment_clone) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.cloneRemote.setOnClickListener { cloneToHiddenDir() }
- binding.createLocal.setOnClickListener {
- parentFragmentManager.performTransactionWithBackStack(RepoLocationFragment.newInstance())
- }
+ binding.createLocal.setOnClickListener { createRepository() }
}
/** Clones a remote Git repository to the app's private directory */
private fun cloneToHiddenDir() {
- settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, false) }
cloneAction.launch(GitServerConfigActivity.createCloneIntent(requireContext()))
}
+ private fun createRepository() {
+ val localDir = PasswordRepository.getRepositoryDirectory()
+ runCatching {
+ check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
+ PasswordRepository.createRepository(localDir)
+ if (!PasswordRepository.isInitialized) {
+ PasswordRepository.initialize()
+ }
+ parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
+ }
+ .onFailure { e ->
+ logcat(ERROR) { e.asLog() }
+ if (!localDir.delete()) {
+ logcat { "Failed to delete local repository: $localDir" }
+ }
+ finish()
+ }
+ }
+
companion object {
fun newInstance(): CloneFragment = CloneFragment()
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt b/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt
deleted file mode 100644
index befe44f1..00000000
--- a/app/src/main/java/dev/msfjarvis/aps/ui/onboarding/fragments/RepoLocationFragment.kt
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
- * SPDX-License-Identifier: GPL-3.0-only
- */
-
-package dev.msfjarvis.aps.ui.onboarding.fragments
-
-import android.Manifest
-import android.content.Intent
-import android.os.Bundle
-import android.view.View
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.edit
-import androidx.fragment.app.Fragment
-import 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.data.repo.PasswordRepository
-import dev.msfjarvis.aps.databinding.FragmentRepoLocationBinding
-import dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity
-import dev.msfjarvis.aps.util.extensions.finish
-import dev.msfjarvis.aps.util.extensions.getString
-import dev.msfjarvis.aps.util.extensions.isPermissionGranted
-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.unsafeLazy
-import dev.msfjarvis.aps.util.extensions.viewBinding
-import dev.msfjarvis.aps.util.settings.PasswordSortOrder
-import dev.msfjarvis.aps.util.settings.PreferenceKeys
-import java.io.File
-import logcat.LogPriority.ERROR
-import logcat.asLog
-import logcat.logcat
-
-class RepoLocationFragment : Fragment(R.layout.fragment_repo_location) {
-
- private val settings by unsafeLazy { requireActivity().applicationContext.sharedPrefs }
- private val directorySelectIntent by unsafeLazy {
- Intent(requireContext(), DirectorySelectionActivity::class.java)
- }
- private val binding by viewBinding(FragmentRepoLocationBinding::bind)
- private val sortOrder: PasswordSortOrder
- get() = PasswordSortOrder.getSortOrder(settings)
-
- private val repositoryInitAction =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == AppCompatActivity.RESULT_OK) {
- initializeRepositoryInfo()
- }
- }
-
- private val externalDirectorySelectAction =
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == AppCompatActivity.RESULT_OK) {
- if (checkExternalDirectory()) {
- finish()
- } else {
- createRepository()
- }
- }
- }
-
- private val externalDirPermGrantedAction = createPermGrantedAction {
- externalDirectorySelectAction.launch(directorySelectIntent)
- }
-
- private val repositoryUsePermGrantedAction = createPermGrantedAction {
- initializeRepositoryInfo()
- }
-
- private val repositoryChangePermGrantedAction = createPermGrantedAction {
- repositoryInitAction.launch(directorySelectIntent)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding.hidden.setOnClickListener { createRepoInHiddenDir() }
-
- binding.sdcard.setOnClickListener { createRepoFromExternalDir() }
- }
-
- /** Initializes an empty repository in the app's private directory */
- private fun createRepoInHiddenDir() {
- settings.edit {
- putBoolean(PreferenceKeys.GIT_EXTERNAL, false)
- remove(PreferenceKeys.GIT_EXTERNAL_REPO)
- }
- initializeRepositoryInfo()
- }
-
- /** Initializes an empty repository in a selected directory if one does not already exist */
- private fun createRepoFromExternalDir() {
- settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, true) }
- val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- if (externalRepo == null) {
- if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- externalDirPermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- } else {
- // Unlikely we have storage permissions without user ever selecting a directory,
- // but let's not assume.
- externalDirectorySelectAction.launch(directorySelectIntent)
- }
- } else {
- MaterialAlertDialogBuilder(requireActivity())
- .setTitle(resources.getString(R.string.directory_selected_title))
- .setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
- .setPositiveButton(resources.getString(R.string.use)) { _, _ ->
- if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- repositoryUsePermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- } else {
- initializeRepositoryInfo()
- }
- }
- .setNegativeButton(resources.getString(R.string.change)) { _, _ ->
- if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- repositoryChangePermGrantedAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- } else {
- repositoryInitAction.launch(directorySelectIntent)
- }
- }
- .show()
- }
- }
-
- private fun checkExternalDirectory(): Boolean {
- if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
- settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) != null
- ) {
- val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- val dir = externalRepoPath?.let { File(it) }
- if (dir != null && // The directory could be opened
- dir.exists() && // The directory exists
- dir.isDirectory && // The directory, is really a directory
- dir.listFilesRecursively().isNotEmpty() && // The directory contains files
- // The directory contains a non-zero number of password files
- PasswordRepository.getPasswords(
- dir,
- PasswordRepository.getRepositoryDirectory(),
- sortOrder
- )
- .isNotEmpty()
- ) {
- PasswordRepository.closeRepository()
- return true
- }
- }
- return false
- }
-
- private fun createRepository() {
- val localDir = PasswordRepository.getRepositoryDirectory()
- runCatching {
- check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
- PasswordRepository.createRepository(localDir)
- if (!PasswordRepository.isInitialized) {
- PasswordRepository.initialize()
- }
- parentFragmentManager.performTransactionWithBackStack(KeySelectionFragment.newInstance())
- }
- .onFailure { e ->
- logcat(ERROR) { e.asLog() }
- if (!localDir.delete()) {
- logcat { "Failed to delete local repository: $localDir" }
- }
- finish()
- }
- }
-
- private fun initializeRepositoryInfo() {
- val externalRepo = settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
- val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
- if (externalRepo && !isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- return
- }
- if (externalRepo && externalRepoPath != null) {
- if (checkExternalDirectory()) {
- finish()
- return
- }
- }
- createRepository()
- }
-
- private fun createPermGrantedAction(block: () -> Unit) =
- registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
- if (granted) {
- block.invoke()
- }
- }
-
- companion object {
-
- fun newInstance(): RepoLocationFragment = RepoLocationFragment()
- }
-}
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 d80959b8..e4ec2359 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
@@ -4,7 +4,6 @@
*/
package dev.msfjarvis.aps.ui.passwords
-import android.Manifest
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
@@ -40,12 +39,10 @@ import dev.msfjarvis.aps.ui.crypto.DecryptActivity
import dev.msfjarvis.aps.ui.crypto.DecryptActivityV2
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivity
import dev.msfjarvis.aps.ui.crypto.PasswordCreationActivityV2
-import dev.msfjarvis.aps.ui.dialogs.BasicBottomSheet
import dev.msfjarvis.aps.ui.dialogs.FolderCreationDialogFragment
import dev.msfjarvis.aps.ui.folderselect.SelectFolderActivity
import dev.msfjarvis.aps.ui.git.base.BaseGitActivity
import dev.msfjarvis.aps.ui.onboarding.activity.OnboardingActivity
-import dev.msfjarvis.aps.ui.settings.DirectorySelectionActivity
import dev.msfjarvis.aps.ui.settings.SettingsActivity
import dev.msfjarvis.aps.util.autofill.AutofillMatcher
import dev.msfjarvis.aps.util.extensions.base64
@@ -53,7 +50,6 @@ import dev.msfjarvis.aps.util.extensions.commitChange
import dev.msfjarvis.aps.util.extensions.contains
import dev.msfjarvis.aps.util.extensions.getString
import dev.msfjarvis.aps.util.extensions.isInsideRepository
-import dev.msfjarvis.aps.util.extensions.isPermissionGranted
import dev.msfjarvis.aps.util.extensions.listFilesRecursively
import dev.msfjarvis.aps.util.extensions.sharedPrefs
import dev.msfjarvis.aps.util.features.Feature
@@ -206,16 +202,7 @@ class PasswordStore : BaseGitActivity() {
@SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) {
- // If user opens app with permission granted then revokes and returns,
- // prevent attempt to create password list fragment
- var savedInstance = savedInstanceState
- if (savedInstanceState != null &&
- (!settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) ||
- !isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE))
- ) {
- savedInstance = null
- }
- super.onCreate(savedInstance)
+ super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pwdstore)
model.currentDir.observe(this) { dir ->
@@ -233,11 +220,7 @@ class PasswordStore : BaseGitActivity() {
override fun onResume() {
super.onResume()
- if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
- hasRequiredStoragePermissions()
- } else {
- checkLocalRepository()
- }
+ checkLocalRepository()
if (settings.getBoolean(PreferenceKeys.SEARCH_ON_START, false) && ::searchItem.isInitialized) {
if (!searchItem.isActionViewExpanded) {
searchItem.expandActionView()
@@ -362,33 +345,9 @@ class PasswordStore : BaseGitActivity() {
)
}
- /**
- * Validates if storage permission is granted, and requests for it if not. The return value is
- * true if the permission has been granted.
- */
- private fun hasRequiredStoragePermissions(): Boolean {
- return if (!isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
- BasicBottomSheet.Builder(this)
- .setMessageRes(R.string.access_sdcard_text)
- .setPositiveButtonClickListener(getString(R.string.snackbar_action_grant)) {
- storagePermissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- }
- .build()
- .show(supportFragmentManager, "STORAGE_PERMISSION_MISSING")
- false
- } else {
- checkLocalRepository()
- true
- }
- }
-
private fun checkLocalRepository() {
- val repo = PasswordRepository.initialize()
- if (repo == null) {
- directorySelectAction.launch(Intent(this, DirectorySelectionActivity::class.java))
- } else {
- checkLocalRepository(PasswordRepository.getRepositoryDirectory())
- }
+ PasswordRepository.initialize()
+ checkLocalRepository(PasswordRepository.getRepositoryDirectory())
}
private fun checkLocalRepository(localDir: File?) {
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
deleted file mode 100644
index 1f8873c0..00000000
--- a/app/src/main/java/dev/msfjarvis/aps/ui/settings/DirectorySelectionActivity.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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 android.net.Uri
-import android.os.Bundle
-import android.os.Environment
-import android.provider.DocumentsContract
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.edit
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import dev.msfjarvis.aps.R
-import dev.msfjarvis.aps.util.extensions.sharedPrefs
-import dev.msfjarvis.aps.util.settings.PreferenceKeys
-import logcat.logcat
-
-class DirectorySelectionActivity : AppCompatActivity() {
-
- @Suppress("DEPRECATION")
- private val directorySelectAction =
- registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri: Uri? ->
- if (uri == null) return@registerForActivityResult
-
- logcat { "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
-
- logcat { "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/RepositorySettings.kt b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt
index cbd9c18f..1c43127a 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/settings/RepositorySettings.kt
@@ -23,7 +23,6 @@ import dagger.hilt.components.SingletonComponent
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
@@ -60,17 +59,6 @@ class RepositorySettings(private val activity: FragmentActivity) : SettingsProvi
private var showSshKeyPref: Preference? = null
- 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) { _, _ ->
- activity.launchActivity(DirectorySelectionActivity::class.java)
- }
- .setNegativeButton(R.string.dialog_cancel, null)
- .show()
- }
-
override fun provideSettings(builder: PreferenceScreen.Builder) {
val encryptedPreferences = hiltEntryPoint.encryptedPreferences()
val gitSettings = hiltEntryPoint.gitSettings()
@@ -162,64 +150,35 @@ class RepositorySettings(private val activity: FragmentActivity) : SettingsProvi
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) } }
+ pref(PreferenceKeys.GIT_DELETE_REPO) {
+ titleRes = R.string.pref_git_delete_repo_title
+ summaryRes = R.string.pref_git_delete_repo_summary
+ onClick {
+ val repoDir = PasswordRepository.getRepositoryDirectory()
+ MaterialAlertDialogBuilder(activity)
+ .setTitle(R.string.pref_dialog_delete_title)
+ .setMessage(activity.getString(R.string.dialog_delete_msg, repoDir))
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ ->
+ runCatching {
+ PasswordRepository.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())
- }
+ 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()
+ 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
}
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/settings/Migrations.kt b/app/src/main/java/dev/msfjarvis/aps/util/settings/Migrations.kt
index 5d61e75b..b90856b1 100644
--- a/app/src/main/java/dev/msfjarvis/aps/util/settings/Migrations.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/util/settings/Migrations.kt
@@ -26,6 +26,7 @@ fun runMigrations(filesDirPath: String, sharedPrefs: SharedPreferences, gitSetti
migrateToSshKey(filesDirPath, sharedPrefs)
migrateToClipboardHistory(sharedPrefs)
migrateToDiceware(sharedPrefs)
+ removeExternalStorageProperties(sharedPrefs)
}
private fun migrateToGitUrlBasedConfig(sharedPrefs: SharedPreferences, gitSettings: GitSettings) {
@@ -132,3 +133,18 @@ private fun migrateToDiceware(sharedPrefs: SharedPreferences) {
}
}
}
+
+private fun removeExternalStorageProperties(prefs: SharedPreferences) {
+ logcat(TAG, INFO) { "Removing preferences related to external storage" }
+ prefs.edit {
+ if (prefs.contains(PreferenceKeys.GIT_EXTERNAL)) {
+ if (prefs.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
+ putBoolean(PreferenceKeys.GIT_EXTERNAL_MIGRATED, true)
+ }
+ remove(PreferenceKeys.GIT_EXTERNAL)
+ }
+ if (prefs.contains(PreferenceKeys.GIT_EXTERNAL_REPO)) {
+ remove(PreferenceKeys.GIT_EXTERNAL_REPO)
+ }
+ }
+}
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt b/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt
index 6168cfee..152275d6 100644
--- a/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/util/settings/PreferenceKeys.kt
@@ -8,7 +8,6 @@ package dev.msfjarvis.aps.util.settings
object PreferenceKeys {
const val APP_THEME = "app_theme"
- const val APP_VERSION = "app_version"
const val AUTOFILL_ENABLE = "autofill_enable"
const val BIOMETRIC_AUTH = "biometric_auth"
@Deprecated(
@@ -26,8 +25,11 @@ object PreferenceKeys {
const val GIT_CONFIG = "git_config"
const val GIT_CONFIG_AUTHOR_EMAIL = "git_config_user_email"
const val GIT_CONFIG_AUTHOR_NAME = "git_config_user_name"
+ @Deprecated(message = "We're removing support for external storage")
const val GIT_EXTERNAL = "git_external"
+ @Deprecated(message = "We're removing support for external storage")
const val GIT_EXTERNAL_REPO = "git_external_repo"
+ const val GIT_EXTERNAL_MIGRATED = "git_external_migrated"
const val GIT_REMOTE_AUTH = "git_remote_auth"
const val GIT_REMOTE_KEY_TYPE = "git_remote_key_type"
@@ -50,10 +52,7 @@ object PreferenceKeys {
const val OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES = "oreo_autofill_custom_public_suffixes"
const val OREO_AUTOFILL_DEFAULT_USERNAME = "oreo_autofill_default_username"
const val OREO_AUTOFILL_DIRECTORY_STRUCTURE = "oreo_autofill_directory_structure"
- const val PREF_KEY_CUSTOM_DICT = "pref_key_custom_dict"
- const val PREF_KEY_IS_CUSTOM_DICT = "pref_key_is_custom_dict"
const val PREF_KEY_PWGEN_TYPE = "pref_key_pwgen_type"
- const val PREF_SELECT_EXTERNAL = "pref_select_external"
const val REPOSITORY_INITIALIZED = "repository_initialized"
const val REPO_CHANGED = "repo_changed"
const val SEARCH_ON_START = "search_on_start"
diff --git a/app/src/main/res/layout/fragment_repo_location.xml b/app/src/main/res/layout/fragment_repo_location.xml
deleted file mode 100644
index 0302eaff..00000000
--- a/app/src/main/res/layout/fragment_repo_location.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<!--
- ~ Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
- ~ SPDX-License-Identifier: GPL-3.0-only
- -->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/colorBackground">
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/app_icon"
- android:layout_width="64dp"
- android:layout_height="64dp"
- android:layout_marginStart="32dp"
- android:layout_marginTop="@dimen/onboarding_icon_margin_top"
- android:contentDescription="@string/app_icon_hint"
- android:src="@mipmap/ic_launcher"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <androidx.appcompat.widget.AppCompatTextView
- android:id="@+id/app_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_marginStart="@dimen/fab_compat_margin"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/app_name"
- android:textAppearance="?attr/textAppearanceTitleLarge"
- android:textStyle="bold"
- app:layout_constraintBottom_toBottomOf="@id/app_icon"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@id/app_icon"
- app:layout_constraintTop_toTopOf="@+id/app_icon" />
-
- <TextView
- android:id="@+id/repo_location"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="48dp"
- android:layout_marginEnd="@dimen/fab_compat_margin"
- android:text="@string/repository_n_location"
- android:textAppearance="?attr/textAppearanceHeadlineSmall"
- android:textStyle="bold"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@id/app_icon"
- app:layout_constraintTop_toBottomOf="@id/app_icon" />
-
- <TextView
- android:id="@+id/repo_location_text"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/onboarding_desc_margin_top"
- android:layout_marginEnd="16dp"
- android:text="@string/location_dialog_create_text"
- android:textAppearance="?attr/textAppearanceHeadlineSmall"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="@id/repo_location"
- app:layout_constraintTop_toBottomOf="@id/repo_location" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/hidden"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginTop="@dimen/onboarding_button_margin_top"
- android:layout_marginEnd="16dp"
- android:maxWidth="300dp"
- android:minWidth="100dp"
- android:text="@string/location_hidden"
- app:layout_constraintBottom_toTopOf="@id/sdcard"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/repo_location_text" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/sdcard"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginBottom="128dp"
- android:maxWidth="300dp"
- android:minWidth="100dp"
- android:text="@string/location_sdcard"
- app:layout_constraintEnd_toEndOf="@id/hidden"
- app:layout_constraintStart_toStartOf="@id/hidden"
- app:layout_constraintTop_toBottomOf="@id/hidden" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
-</ScrollView>
diff --git a/app/src/main/res/xml/backup_content.xml b/app/src/main/res/xml/backup_content.xml
new file mode 100644
index 00000000..1b0854f7
--- /dev/null
+++ b/app/src/main/res/xml/backup_content.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<full-backup-content />
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 00000000..2148d1dd
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-extraction-rules>
+ <cloud-backup />
+ <device-transfer />
+</data-extraction-rules>