Refactor uses of applicationContext and startActivityForResult (#997)

* Refactor uses of applicationContext and startActivityForResult

This commit applies three types of refactoring:

1. Remove context argument from PasswordRepository companion functions
   by relying on Application.instance.
2. Introduce a sharedPrefs extension function on Context that returns
   the default SharedPreferences for the applicationContext.
3. Use OpenDocument() and OpenDocumentTree() contracts.

* Drop toPasswordItem argument
This commit is contained in:
Fabian Henneke
2020-08-06 10:58:20 +02:00
committed by GitHub
parent 14c44bf584
commit 3dace243e4
25 changed files with 159 additions and 191 deletions

View File

@@ -15,27 +15,25 @@ import com.github.ajalt.timberkt.Timber.DebugTree
import com.github.ajalt.timberkt.Timber.plant import com.github.ajalt.timberkt.Timber.plant
import com.zeapo.pwdstore.git.config.setUpBouncyCastleForSshj import com.zeapo.pwdstore.git.config.setUpBouncyCastleForSshj
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
@Suppress("Unused") @Suppress("Unused")
class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener { class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener {
private var prefs: SharedPreferences? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
instance = this instance = this
prefs = PreferenceManager.getDefaultSharedPreferences(this) if (BuildConfig.ENABLE_DEBUG_FEATURES ||
if (BuildConfig.ENABLE_DEBUG_FEATURES || prefs?.getBoolean(PreferenceKeys.ENABLE_DEBUG_LOGGING, false) == sharedPrefs.getBoolean(PreferenceKeys.ENABLE_DEBUG_LOGGING, false)) {
true) {
plant(DebugTree()) plant(DebugTree())
} }
prefs?.registerOnSharedPreferenceChangeListener(this) sharedPrefs.registerOnSharedPreferenceChangeListener(this)
setNightMode() setNightMode()
setUpBouncyCastleForSshj() setUpBouncyCastleForSshj()
} }
override fun onTerminate() { override fun onTerminate() {
prefs?.unregisterOnSharedPreferenceChangeListener(this) sharedPrefs.unregisterOnSharedPreferenceChangeListener(this)
super.onTerminate() super.onTerminate()
} }
@@ -46,7 +44,7 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
} }
private fun setNightMode() { private fun setNightMode() {
AppCompatDelegate.setDefaultNightMode(when (prefs?.getString(PreferenceKeys.APP_THEME, getString(R.string.app_theme_def))) { AppCompatDelegate.setDefaultNightMode(when (sharedPrefs.getString(PreferenceKeys.APP_THEME, getString(R.string.app_theme_def))) {
"light" -> MODE_NIGHT_NO "light" -> MODE_NIGHT_NO
"dark" -> MODE_NIGHT_YES "dark" -> MODE_NIGHT_YES
"follow_system" -> MODE_NIGHT_FOLLOW_SYSTEM "follow_system" -> MODE_NIGHT_FOLLOW_SYSTEM

View File

@@ -19,6 +19,7 @@ import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.d
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.clipboard import com.zeapo.pwdstore.utils.clipboard
import com.zeapo.pwdstore.utils.sharedPrefs
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -31,7 +32,7 @@ import kotlinx.coroutines.withContext
class ClipboardService : Service() { class ClipboardService : Service() {
private val scope = CoroutineScope(Job() + Dispatchers.Main) private val scope = CoroutineScope(Job() + Dispatchers.Main)
private val settings: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) } private val settings: SharedPreferences by lazy { sharedPrefs }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) { if (intent != null) {

View File

@@ -13,12 +13,13 @@ import androidx.preference.PreferenceManager
import com.zeapo.pwdstore.crypto.DecryptActivity import com.zeapo.pwdstore.crypto.DecryptActivity
import com.zeapo.pwdstore.utils.BiometricAuthenticator import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
class LaunchActivity : AppCompatActivity() { class LaunchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = sharedPrefs
if (prefs.getBoolean(PreferenceKeys.BIOMETRIC_AUTH, false)) { if (prefs.getBoolean(PreferenceKeys.BIOMETRIC_AUTH, false)) {
BiometricAuthenticator.authenticate(this) { BiometricAuthenticator.authenticate(this) {
when (it) { when (it) {

View File

@@ -58,7 +58,7 @@ class PasswordExportService : Service() {
*/ */
private fun exportPasswords(targetDirectory: DocumentFile) { private fun exportPasswords(targetDirectory: DocumentFile) {
val repositoryDirectory = requireNotNull(PasswordRepository.getRepositoryDirectory(applicationContext)) val repositoryDirectory = requireNotNull(PasswordRepository.getRepositoryDirectory())
val sourcePassDir = DocumentFile.fromFile(repositoryDirectory) val sourcePassDir = DocumentFile.fromFile(repositoryDirectory)
d { "Copying ${repositoryDirectory.path} to $targetDirectory" } d { "Copying ${repositoryDirectory.path} to $targetDirectory" }

View File

@@ -18,7 +18,6 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@@ -33,6 +32,7 @@ import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import java.io.File import java.io.File
import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.FastScrollerBuilder
@@ -59,7 +59,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
settings = PreferenceManager.getDefaultSharedPreferences(requireContext()) settings = requireContext().sharedPrefs
initializePasswordList() initializePasswordList()
binding.fab.setOnClickListener { binding.fab.setOnClickListener {
ItemCreationBottomSheet().show(childFragmentManager, "BOTTOM_SHEET") ItemCreationBottomSheet().show(childFragmentManager, "BOTTOM_SHEET")
@@ -73,7 +73,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
} }
private fun initializePasswordList() { private fun initializePasswordList() {
val gitDir = File(PasswordRepository.getRepositoryDirectory(requireContext()), ".git") val gitDir = File(PasswordRepository.getRepositoryDirectory(), ".git")
val hasGitDir = gitDir.exists() && gitDir.isDirectory && (gitDir.listFiles()?.isNotEmpty() == true) val hasGitDir = gitDir.exists() && gitDir.isDirectory && (gitDir.listFiles()?.isNotEmpty() == true)
binding.swipeRefresher.setOnRefreshListener { binding.swipeRefresher.setOnRefreshListener {
if (!hasGitDir) { if (!hasGitDir) {
@@ -180,7 +180,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
// may be called multiple times if the mode is invalidated. // may be called multiple times if the mode is invalidated.
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
menu.findItem(R.id.menu_edit_password).isVisible = menu.findItem(R.id.menu_edit_password).isVisible =
recyclerAdapter.getSelectedItems(requireContext()) recyclerAdapter.getSelectedItems()
.all { it.type == PasswordItem.TYPE_CATEGORY } .all { it.type == PasswordItem.TYPE_CATEGORY }
return true return true
} }
@@ -189,17 +189,17 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.menu_delete_password -> { R.id.menu_delete_password -> {
requireStore().deletePasswords(recyclerAdapter.getSelectedItems(requireContext())) requireStore().deletePasswords(recyclerAdapter.getSelectedItems())
// Action picked, so close the CAB // Action picked, so close the CAB
mode.finish() mode.finish()
true true
} }
R.id.menu_move_password -> { R.id.menu_move_password -> {
requireStore().movePasswords(recyclerAdapter.getSelectedItems(requireContext())) requireStore().movePasswords(recyclerAdapter.getSelectedItems())
false false
} }
R.id.menu_edit_password -> { R.id.menu_edit_password -> {
requireStore().renameCategory(recyclerAdapter.getSelectedItems(requireContext())) requireStore().renameCategory(recyclerAdapter.getSelectedItems())
mode.finish() mode.finish()
false false
} }

View File

@@ -69,6 +69,7 @@ import com.zeapo.pwdstore.utils.contains
import com.zeapo.pwdstore.utils.isInsideRepository import com.zeapo.pwdstore.utils.isInsideRepository
import com.zeapo.pwdstore.utils.listFilesRecursively import com.zeapo.pwdstore.utils.listFilesRecursively
import com.zeapo.pwdstore.utils.requestInputFocusOnView import com.zeapo.pwdstore.utils.requestInputFocusOnView
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
import java.lang.Character.UnicodeBlock import java.lang.Character.UnicodeBlock
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -83,10 +84,11 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
private lateinit var activity: PasswordStore private lateinit var activity: PasswordStore
private lateinit var searchItem: MenuItem private lateinit var searchItem: MenuItem
private lateinit var searchView: SearchView private lateinit var searchView: SearchView
private lateinit var settings: SharedPreferences
private var plist: PasswordFragment? = null private var plist: PasswordFragment? = null
private var shortcutManager: ShortcutManager? = null private var shortcutManager: ShortcutManager? = null
private val settings by lazy { sharedPrefs }
private val model: SearchableRepositoryViewModel by viewModels { private val model: SearchableRepositoryViewModel by viewModels {
ViewModelProvider.AndroidViewModelFactory(application) ViewModelProvider.AndroidViewModelFactory(application)
} }
@@ -119,7 +121,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
dir.exists() && dir.exists() &&
dir.isDirectory && dir.isDirectory &&
dir.listFilesRecursively().isNotEmpty() && dir.listFilesRecursively().isNotEmpty() &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) { getPasswords(dir, getRepositoryDirectory(), sortOrder).isNotEmpty()) {
closeRepository() closeRepository()
checkLocalRepository() checkLocalRepository()
return@registerForActivityResult return@registerForActivityResult
@@ -153,7 +155,6 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
@SuppressLint("NewApi") @SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
activity = this activity = this
settings = PreferenceManager.getDefaultSharedPreferences(this.applicationContext)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
shortcutManager = getSystemService() shortcutManager = getSystemService()
} }
@@ -209,7 +210,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
model.currentDir.observe(this) { dir -> model.currentDir.observe(this) { dir ->
val basePath = getRepositoryDirectory(applicationContext).absoluteFile val basePath = getRepositoryDirectory().absoluteFile
supportActionBar!!.apply { supportActionBar!!.apply {
if (dir != basePath) if (dir != basePath)
title = dir.name title = dir.name
@@ -379,9 +380,9 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
private fun createRepository() { private fun createRepository() {
if (!isInitialized) { if (!isInitialized) {
initialize(this) initialize()
} }
val localDir = getRepositoryDirectory(applicationContext) val localDir = getRepositoryDirectory()
try { try {
check(localDir.mkdir()) { "Failed to create directory!" } check(localDir.mkdir()) { "Failed to create directory!" }
createRepository(localDir) createRepository(localDir)
@@ -409,7 +410,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
if (externalRepo && externalRepoPath != null) { if (externalRepo && externalRepoPath != null) {
val dir = File(externalRepoPath) val dir = File(externalRepoPath)
if (dir.exists() && dir.isDirectory && if (dir.exists() && dir.isDirectory &&
getPasswords(dir, getRepositoryDirectory(this), sortOrder).isNotEmpty()) { getPasswords(dir, getRepositoryDirectory(), sortOrder).isNotEmpty()) {
closeRepository() closeRepository()
checkLocalRepository() checkLocalRepository()
return // if not empty, just show me the passwords! return // if not empty, just show me the passwords!
@@ -449,7 +450,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
private fun checkLocalRepository() { private fun checkLocalRepository() {
val repo = initialize(this) val repo = initialize()
if (repo == null) { if (repo == null) {
val intent = Intent(activity, UserPreference::class.java) val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external") intent.putExtra("operation", "git_external")
@@ -459,7 +460,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
}.launch(intent) }.launch(intent)
} else { } else {
checkLocalRepository(getRepositoryDirectory(applicationContext)) checkLocalRepository(getRepositoryDirectory())
} }
} }
@@ -472,7 +473,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
settings.edit { putBoolean(PreferenceKeys.REPO_CHANGED, false) } settings.edit { putBoolean(PreferenceKeys.REPO_CHANGED, false) }
plist = PasswordFragment() plist = PasswordFragment()
val args = Bundle() val args = Bundle()
args.putString(REQUEST_ARG_PATH, getRepositoryDirectory(applicationContext).absolutePath) args.putString(REQUEST_ARG_PATH, getRepositoryDirectory().absolutePath)
// if the activity was started from the autofill settings, the // if the activity was started from the autofill settings, the
// intent is to match a clicked pwd with app. pass this to fragment // intent is to match a clicked pwd with app. pass this to fragment
@@ -506,7 +507,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
private fun getLastChangedTimestamp(fullPath: String): Long { private fun getLastChangedTimestamp(fullPath: String): Long {
val repoPath = getRepositoryDirectory(this) val repoPath = getRepositoryDirectory()
val repository = getRepository(repoPath) val repository = getRepository(repoPath)
if (repository == null) { if (repository == null) {
d { "getLastChangedTimestamp: No git repository" } d { "getLastChangedTimestamp: No git repository" }
@@ -534,7 +535,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
for (intent in arrayOf(decryptIntent, authDecryptIntent)) { for (intent in arrayOf(decryptIntent, authDecryptIntent)) {
intent.putExtra("NAME", item.toString()) intent.putExtra("NAME", item.toString())
intent.putExtra("FILE_PATH", item.file.absolutePath) intent.putExtra("FILE_PATH", item.file.absolutePath)
intent.putExtra("REPO_PATH", getRepositoryDirectory(applicationContext).absolutePath) intent.putExtra("REPO_PATH", getRepositoryDirectory().absolutePath)
intent.putExtra("LAST_CHANGED_TIMESTAMP", getLastChangedTimestamp(item.file.absolutePath)) intent.putExtra("LAST_CHANGED_TIMESTAMP", getLastChangedTimestamp(item.file.absolutePath))
} }
// Needs an action to be a shortcut intent // Needs an action to be a shortcut intent
@@ -577,7 +578,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
i { "Adding file to : ${currentDir.absolutePath}" } i { "Adding file to : ${currentDir.absolutePath}" }
val intent = Intent(this, PasswordCreationActivity::class.java) val intent = Intent(this, PasswordCreationActivity::class.java)
intent.putExtra("FILE_PATH", currentDir.absolutePath) intent.putExtra("FILE_PATH", currentDir.absolutePath)
intent.putExtra("REPO_PATH", getRepositoryDirectory(applicationContext).absolutePath) intent.putExtra("REPO_PATH", getRepositoryDirectory().absolutePath)
registerForActivityResult(StartActivityForResult()) { result -> registerForActivityResult(StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
lifecycleScope.launch { lifecycleScope.launch {
@@ -623,7 +624,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
refreshPasswordList() refreshPasswordList()
AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete) AutofillMatcher.updateMatches(applicationContext, delete = filesToDelete)
val fmt = selectedItems.joinToString(separator = ", ") { item -> val fmt = selectedItems.joinToString(separator = ", ") { item ->
item.file.toRelativeString(getRepositoryDirectory(this@PasswordStore)) item.file.toRelativeString(getRepositoryDirectory())
} }
lifecycleScope.launch { lifecycleScope.launch {
commitChange( commitChange(
@@ -645,7 +646,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
val intentData = result.data ?: return@registerForActivityResult val intentData = result.data ?: return@registerForActivityResult
val filesToMove = requireNotNull(intentData.getStringArrayExtra("Files")) val filesToMove = requireNotNull(intentData.getStringArrayExtra("Files"))
val target = File(requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH"))) val target = File(requireNotNull(intentData.getStringExtra("SELECTED_FOLDER_PATH")))
val repositoryPath = getRepositoryDirectory(applicationContext).absolutePath val repositoryPath = getRepositoryDirectory().absolutePath
if (!target.isDirectory) { if (!target.isDirectory) {
e { "Tried moving passwords to a non-existing folder." } e { "Tried moving passwords to a non-existing folder." }
return@registerForActivityResult return@registerForActivityResult
@@ -703,7 +704,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
} }
else -> { else -> {
val repoDir = getRepositoryDirectory(applicationContext).absolutePath val repoDir = getRepositoryDirectory().absolutePath
val relativePath = getRelativePath("${target.absolutePath}/", repoDir) val relativePath = getRelativePath("${target.absolutePath}/", repoDir)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
commitChange( commitChange(
@@ -756,7 +757,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
when { when {
newCategoryEditText.text.isNullOrBlank() -> renameCategory(oldCategory, CategoryRenameError.EmptyField) newCategoryEditText.text.isNullOrBlank() -> renameCategory(oldCategory, CategoryRenameError.EmptyField)
newCategory.exists() -> renameCategory(oldCategory, CategoryRenameError.CategoryExists) newCategory.exists() -> renameCategory(oldCategory, CategoryRenameError.CategoryExists)
!isInsideRepository(newCategory) -> renameCategory(oldCategory, CategoryRenameError.DestinationOutsideRepo) !newCategory.isInsideRepository() -> renameCategory(oldCategory, CategoryRenameError.DestinationOutsideRepo)
else -> lifecycleScope.launch(Dispatchers.IO) { else -> lifecycleScope.launch(Dispatchers.IO) {
moveFile(oldCategory.file, newCategory) moveFile(oldCategory.file, newCategory)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
@@ -803,7 +804,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
} }
private val currentDir: File private val currentDir: File
get() = plist?.currentDir ?: getRepositoryDirectory(applicationContext) get() = plist?.currentDir ?: getRepositoryDirectory()
private suspend fun moveFile(source: File, destinationFile: File) { private suspend fun moveFile(source: File, destinationFile: File) {
val sourceDestinationMap = if (source.isDirectory) { val sourceDestinationMap = if (source.isDirectory) {
@@ -887,7 +888,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
fun matchPasswordWithApp(item: PasswordItem) { fun matchPasswordWithApp(item: PasswordItem) {
val path = item.file val path = item.file
.absolutePath .absolutePath
.replace(getRepositoryDirectory(applicationContext).toString() + "/", "") .replace(getRepositoryDirectory().toString() + "/", "")
.replace(".gpg", "") .replace(".gpg", "")
val data = Intent() val data = Intent()
data.putExtra("path", path) data.putExtra("path", path)

View File

@@ -5,7 +5,6 @@
package com.zeapo.pwdstore package com.zeapo.pwdstore
import android.app.Application import android.app.Application
import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -31,6 +30,7 @@ import com.zeapo.pwdstore.autofill.oreo.DirectoryStructure
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
import java.text.Collator import java.text.Collator
import java.util.Locale import java.util.Locale
@@ -50,10 +50,10 @@ import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
private fun File.toPasswordItem(root: File) = if (isFile) private fun File.toPasswordItem() = if (isFile)
PasswordItem.newPassword(name, this, root) PasswordItem.newPassword(name, this, PasswordRepository.getRepositoryDirectory())
else else
PasswordItem.newCategory(name, this, root) PasswordItem.newCategory(name, this, PasswordRepository.getRepositoryDirectory())
private fun PasswordItem.fuzzyMatch(filter: String): Int { private fun PasswordItem.fuzzyMatch(filter: String): Int {
var i = 0 var i = 0
@@ -138,8 +138,8 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
} }
private val root private val root
get() = PasswordRepository.getRepositoryDirectory(getApplication()) get() = PasswordRepository.getRepositoryDirectory()
private val settings = PreferenceManager.getDefaultSharedPreferences(getApplication()) private val settings by lazy { application.sharedPrefs }
private val showHiddenDirs private val showHiddenDirs
get() = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false) get() = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
private val defaultSearchMode private val defaultSearchMode
@@ -216,7 +216,7 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
val passwordList = when (filterModeToUse) { val passwordList = when (filterModeToUse) {
FilterMode.NoFilter -> { FilterMode.NoFilter -> {
prefilteredResultFlow prefilteredResultFlow
.map { it.toPasswordItem(root) } .map { it.toPasswordItem() }
.toList() .toList()
.sortedWith(itemComparator) .sortedWith(itemComparator)
} }
@@ -228,7 +228,7 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
.filter { absoluteFile -> .filter { absoluteFile ->
regex.containsMatchIn(absoluteFile.relativeTo(root).path) regex.containsMatchIn(absoluteFile.relativeTo(root).path)
} }
.map { it.toPasswordItem(root) } .map { it.toPasswordItem() }
.toList() .toList()
.sortedWith(itemComparator) .sortedWith(itemComparator)
} else { } else {
@@ -238,7 +238,7 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
FilterMode.Fuzzy -> { FilterMode.Fuzzy -> {
prefilteredResultFlow prefilteredResultFlow
.map { .map {
val item = it.toPasswordItem(root) val item = it.toPasswordItem()
Pair(item.fuzzyMatch(searchAction.filter), item) Pair(item.fuzzyMatch(searchAction.filter), item)
} }
.filter { it.first > 0 } .filter { it.first > 0 }
@@ -443,10 +443,7 @@ open class SearchableRepositoryAdapter<T : RecyclerView.ViewHolder>(
private val selectedFiles private val selectedFiles
get() = requireSelectionTracker().selection.map { File(it) } get() = requireSelectionTracker().selection.map { File(it) }
fun getSelectedItems(context: Context): List<PasswordItem> { fun getSelectedItems() = selectedFiles.map { it.toPasswordItem() }
val root = PasswordRepository.getRepositoryDirectory(context)
return selectedFiles.map { it.toPasswordItem(root) }
}
fun getPositionForFile(file: File) = itemKeyProvider.getPosition(file.absolutePath) fun getPositionForFile(file: File) = itemKeyProvider.getPosition(file.absolutePath)

View File

@@ -22,7 +22,7 @@ class SelectFolderActivity : AppCompatActivity(R.layout.select_folder_layout) {
passwordList = SelectFolderFragment() passwordList = SelectFolderFragment()
val args = Bundle() val args = Bundle()
args.putString(PasswordStore.REQUEST_ARG_PATH, PasswordRepository.getRepositoryDirectory(applicationContext).absolutePath) args.putString(PasswordStore.REQUEST_ARG_PATH, PasswordRepository.getRepositoryDirectory().absolutePath)
passwordList.arguments = args passwordList.arguments = args

View File

@@ -6,6 +6,7 @@ package com.zeapo.pwdstore
import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.AccessibilityServiceInfo
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.pm.ShortcutManager import android.content.pm.ShortcutManager
@@ -20,8 +21,8 @@ import android.text.TextUtils
import android.view.MenuItem import android.view.MenuItem
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts.OpenDocument
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.activity.result.contract.ActivityResultContracts.OpenDocumentTree
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
@@ -53,6 +54,7 @@ import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.autofillManager import com.zeapo.pwdstore.utils.autofillManager
import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.getEncryptedPrefs
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@@ -69,15 +71,15 @@ class UserPreference : AppCompatActivity() {
private var clearSavedPassPreference: Preference? = null private var clearSavedPassPreference: Preference? = null
private lateinit var autofillDependencies: List<Preference> private lateinit var autofillDependencies: List<Preference>
private lateinit var oreoAutofillDependencies: List<Preference> private lateinit var oreoAutofillDependencies: List<Preference>
private lateinit var callingActivity: UserPreference private lateinit var prefsActivity: UserPreference
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var encryptedPreferences: SharedPreferences private lateinit var encryptedPreferences: SharedPreferences
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
callingActivity = requireActivity() as UserPreference prefsActivity = requireActivity() as UserPreference
val context = requireContext() val context = requireContext()
sharedPreferences = preferenceManager.sharedPreferences sharedPreferences = preferenceManager.sharedPreferences
encryptedPreferences = requireActivity().applicationContext.getEncryptedPrefs("git_operation") encryptedPreferences = requireActivity().getEncryptedPrefs("git_operation")
addPreferencesFromResource(R.xml.preference) addPreferencesFromResource(R.xml.preference)
@@ -152,12 +154,12 @@ class UserPreference : AppCompatActivity() {
appVersionPreference?.summary = "Version: ${BuildConfig.VERSION_NAME}" appVersionPreference?.summary = "Version: ${BuildConfig.VERSION_NAME}"
sshKeyPreference?.onPreferenceClickListener = ClickListener { sshKeyPreference?.onPreferenceClickListener = ClickListener {
callingActivity.getSshKey() prefsActivity.getSshKey()
true true
} }
sshKeygenPreference?.onPreferenceClickListener = ClickListener { sshKeygenPreference?.onPreferenceClickListener = ClickListener {
callingActivity.makeSshKey(true) prefsActivity.makeSshKey(true)
true true
} }
@@ -185,24 +187,24 @@ class UserPreference : AppCompatActivity() {
} }
gitServerPreference?.onPreferenceClickListener = ClickListener { gitServerPreference?.onPreferenceClickListener = ClickListener {
startActivity(Intent(callingActivity, GitServerConfigActivity::class.java)) startActivity(Intent(prefsActivity, GitServerConfigActivity::class.java))
true true
} }
gitConfigPreference?.onPreferenceClickListener = ClickListener { gitConfigPreference?.onPreferenceClickListener = ClickListener {
startActivity(Intent(callingActivity, GitConfigActivity::class.java)) startActivity(Intent(prefsActivity, GitConfigActivity::class.java))
true true
} }
deleteRepoPreference?.onPreferenceClickListener = ClickListener { deleteRepoPreference?.onPreferenceClickListener = ClickListener {
val repoDir = PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext) val repoDir = PasswordRepository.getRepositoryDirectory()
MaterialAlertDialogBuilder(callingActivity) MaterialAlertDialogBuilder(prefsActivity)
.setTitle(R.string.pref_dialog_delete_title) .setTitle(R.string.pref_dialog_delete_title)
.setMessage(resources.getString(R.string.dialog_delete_msg, repoDir)) .setMessage(resources.getString(R.string.dialog_delete_msg, repoDir))
.setCancelable(false) .setCancelable(false)
.setPositiveButton(R.string.dialog_delete) { dialogInterface, _ -> .setPositiveButton(R.string.dialog_delete) { dialogInterface, _ ->
try { try {
PasswordRepository.getRepositoryDirectory(callingActivity.applicationContext).deleteRecursively() PasswordRepository.getRepositoryDirectory().deleteRecursively()
PasswordRepository.closeRepository() PasswordRepository.closeRepository()
} catch (ignored: Exception) { } catch (ignored: Exception) {
// TODO Handle the different cases of exceptions // TODO Handle the different cases of exceptions
@@ -215,7 +217,7 @@ class UserPreference : AppCompatActivity() {
} }
sharedPreferences.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) } sharedPreferences.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) }
dialogInterface.cancel() dialogInterface.cancel()
callingActivity.finish() prefsActivity.finish()
} }
.setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } } .setNegativeButton(R.string.dialog_do_not_delete) { dialogInterface, _ -> run { dialogInterface.cancel() } }
.show() .show()
@@ -226,7 +228,7 @@ class UserPreference : AppCompatActivity() {
selectExternalGitRepositoryPreference?.summary = selectExternalGitRepositoryPreference?.summary =
sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO, context.getString(R.string.no_repo_selected)) sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO, context.getString(R.string.no_repo_selected))
selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener { selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener {
callingActivity.selectExternalGitRepository() prefsActivity.selectExternalGitRepository()
true true
} }
@@ -241,7 +243,7 @@ class UserPreference : AppCompatActivity() {
externalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo externalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo
autoFillAppsPreference?.onPreferenceClickListener = ClickListener { autoFillAppsPreference?.onPreferenceClickListener = ClickListener {
val intent = Intent(callingActivity, AutofillPreferenceActivity::class.java) val intent = Intent(prefsActivity, AutofillPreferenceActivity::class.java)
startActivity(intent) startActivity(intent)
true true
} }
@@ -254,7 +256,7 @@ class UserPreference : AppCompatActivity() {
findPreference<Preference>(PreferenceKeys.EXPORT_PASSWORDS)?.apply { findPreference<Preference>(PreferenceKeys.EXPORT_PASSWORDS)?.apply {
isVisible = sharedPreferences.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false) isVisible = sharedPreferences.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
onPreferenceClickListener = Preference.OnPreferenceClickListener { onPreferenceClickListener = Preference.OnPreferenceClickListener {
callingActivity.exportPasswords() prefsActivity.exportPasswords()
true true
} }
} }
@@ -316,7 +318,7 @@ class UserPreference : AppCompatActivity() {
val prefCustomXkpwdDictionary = findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT) val prefCustomXkpwdDictionary = findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
prefCustomXkpwdDictionary?.onPreferenceClickListener = ClickListener { prefCustomXkpwdDictionary?.onPreferenceClickListener = ClickListener {
callingActivity.storeCustomDictionaryPath() prefsActivity.storeCustomDictionaryPath()
true true
} }
val dictUri = sharedPreferences.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "") val dictUri = sharedPreferences.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "")
@@ -361,8 +363,8 @@ class UserPreference : AppCompatActivity() {
} }
private fun updateAutofillSettings() { private fun updateAutofillSettings() {
val isAccessibilityServiceEnabled = callingActivity.isAccessibilityServiceEnabled val isAccessibilityServiceEnabled = prefsActivity.isAccessibilityServiceEnabled
val isAutofillServiceEnabled = callingActivity.isAutofillServiceEnabled val isAutofillServiceEnabled = prefsActivity.isAutofillServiceEnabled
autoFillEnablePreference?.isChecked = autoFillEnablePreference?.isChecked =
isAccessibilityServiceEnabled || isAutofillServiceEnabled isAccessibilityServiceEnabled || isAutofillServiceEnabled
autofillDependencies.forEach { autofillDependencies.forEach {
@@ -391,16 +393,16 @@ class UserPreference : AppCompatActivity() {
} }
private fun onEnableAutofillClick() { private fun onEnableAutofillClick() {
if (callingActivity.isAccessibilityServiceEnabled) { if (prefsActivity.isAccessibilityServiceEnabled) {
startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
} else if (callingActivity.isAutofillServiceEnabled) { } else if (prefsActivity.isAutofillServiceEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
callingActivity.autofillManager!!.disableAutofillServices() prefsActivity.autofillManager!!.disableAutofillServices()
else else
throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O") throw IllegalStateException("isAutofillServiceEnabled == true, but Build.VERSION.SDK_INT < Build.VERSION_CODES.O")
} else { } else {
val enableOreoAutofill = callingActivity.isAutofillServiceSupported val enableOreoAutofill = prefsActivity.isAutofillServiceSupported
MaterialAlertDialogBuilder(callingActivity).run { MaterialAlertDialogBuilder(prefsActivity).run {
setTitle(R.string.pref_autofill_enable_title) setTitle(R.string.pref_autofill_enable_title)
if (enableOreoAutofill && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (enableOreoAutofill && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@@ -480,10 +482,8 @@ class UserPreference : AppCompatActivity() {
.setTitle(this.resources.getString(R.string.external_repository_dialog_title)) .setTitle(this.resources.getString(R.string.external_repository_dialog_title))
.setMessage(this.resources.getString(R.string.external_repository_dialog_text)) .setMessage(this.resources.getString(R.string.external_repository_dialog_text))
.setPositiveButton(R.string.dialog_ok) { _, _ -> .setPositiveButton(R.string.dialog_ok) { _, _ ->
val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) registerForActivityResult(OpenDocumentTree()) { uri: Uri? ->
registerForActivityResult(StartActivityForResult()) { result -> if (uri == null) return@registerForActivityResult
if (!validateResult(result)) return@registerForActivityResult
val uri = result.data?.data
tag(TAG).d { "Selected repository URI is $uri" } tag(TAG).d { "Selected repository URI is $uri" }
// TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile // TODO: This is fragile. Workaround until PasswordItem is backed by DocumentFile
@@ -491,7 +491,7 @@ class UserPreference : AppCompatActivity() {
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val path = if (split.size > 1) split[1] else split[0] val path = if (split.size > 1) split[1] else split[0]
val repoPath = "${Environment.getExternalStorageDirectory()}/$path" val repoPath = "${Environment.getExternalStorageDirectory()}/$path"
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) val prefs = sharedPrefs
tag(TAG).d { "Selected repository path is $repoPath" } tag(TAG).d { "Selected repository path is $repoPath" }
@@ -500,14 +500,14 @@ class UserPreference : AppCompatActivity() {
.setTitle(getString(R.string.sdcard_root_warning_title)) .setTitle(getString(R.string.sdcard_root_warning_title))
.setMessage(getString(R.string.sdcard_root_warning_message)) .setMessage(getString(R.string.sdcard_root_warning_message))
.setPositiveButton("Remove everything") { _, _ -> .setPositiveButton("Remove everything") { _, _ ->
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri?.path) } prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, uri.path) }
} }
.setNegativeButton(R.string.dialog_cancel, null) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
} }
prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) } prefs.edit { putString(PreferenceKeys.GIT_EXTERNAL_REPO, repoPath) }
}.launch(Intent.createChooser(i, "Choose Directory")) }.launch(null)
} }
.setNegativeButton(R.string.dialog_cancel, null) .setNegativeButton(R.string.dialog_cancel, null)
.show() .show()
@@ -527,29 +527,13 @@ class UserPreference : AppCompatActivity() {
} }
} }
/**
* Given a [ActivityResult], validates that the result is usable.
*/
private fun validateResult(result: ActivityResult): Boolean {
if (result.resultCode != RESULT_OK) {
return false
}
if (result.data == null) {
setResult(RESULT_CANCELED)
return false
}
return true
}
/** /**
* Opens a file explorer to import the private key * Opens a file explorer to import the private key
*/ */
private fun getSshKey() { private fun getSshKey() {
registerForActivityResult(StartActivityForResult()) { result -> registerForActivityResult(OpenDocument()) { uri: Uri? ->
if (!validateResult(result)) return@registerForActivityResult if (uri == null) return@registerForActivityResult
try { try {
val uri: Uri = result.data?.data ?: throw IOException("Unable to open file")
copySshKey(uri) copySshKey(uri)
Toast.makeText( Toast.makeText(
@@ -557,7 +541,7 @@ class UserPreference : AppCompatActivity() {
this.resources.getString(R.string.ssh_key_success_dialog_title), this.resources.getString(R.string.ssh_key_success_dialog_title),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) val prefs = sharedPrefs
prefs.edit { putBoolean(PreferenceKeys.USE_GENERATED_KEY, false) } prefs.edit { putBoolean(PreferenceKeys.USE_GENERATED_KEY, false) }
getEncryptedPrefs("git_operation").edit { remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) } getEncryptedPrefs("git_operation").edit { remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) }
@@ -574,44 +558,39 @@ class UserPreference : AppCompatActivity() {
.setPositiveButton(resources.getString(R.string.dialog_ok), null) .setPositiveButton(resources.getString(R.string.dialog_ok), null)
.show() .show()
} }
}.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply { }.launch(arrayOf("*/*"))
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
})
} }
/** /**
* Exports the passwords * Exports the passwords
*/ */
private fun exportPasswords() { private fun exportPasswords() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { registerForActivityResult(object : OpenDocumentTree() {
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or override fun createIntent(context: Context, input: Uri?): Intent {
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or return super.createIntent(context, input).apply {
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
} Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
registerForActivityResult(StartActivityForResult()) { result ->
if (!validateResult(result)) return@registerForActivityResult
val uri = result.data?.data
if (uri != null) {
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)
}
} }
} }
}.launch(intent) }) { 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)
}
}
}.launch(null)
} }
/** /**
@@ -630,18 +609,16 @@ class UserPreference : AppCompatActivity() {
* Pick custom xkpwd dictionary from sdcard * Pick custom xkpwd dictionary from sdcard
*/ */
private fun storeCustomDictionaryPath() { private fun storeCustomDictionaryPath() {
registerForActivityResult(StartActivityForResult()) { result -> registerForActivityResult(OpenDocument()) { uri ->
if (!validateResult(result)) return@registerForActivityResult if (uri == null) return@registerForActivityResult
val uri: Uri = result.data?.data ?: throw IOException("Unable to open file")
Toast.makeText( Toast.makeText(
this, this,
this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path), this.resources.getString(R.string.xkpwgen_custom_dict_imported, uri.path),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
prefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) } sharedPrefs.edit { putString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, uri.toString()) }
val customDictPref = prefsFragment.findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT) val customDictPref = prefsFragment.findPreference<Preference>(PreferenceKeys.PREF_KEY_CUSTOM_DICT)
setCustomDictSummary(customDictPref, uri) setCustomDictSummary(customDictPref, uri)
@@ -653,10 +630,7 @@ class UserPreference : AppCompatActivity() {
customDictFile.close() customDictFile.close()
setResult(RESULT_OK) setResult(RESULT_OK)
}.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply { }.launch(arrayOf("*/*"))
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
})
} }
@Throws(IllegalArgumentException::class, IOException::class) @Throws(IllegalArgumentException::class, IOException::class)

View File

@@ -76,7 +76,7 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
} }
fun setPickedPassword(path: String) { fun setPickedPassword(path: String) {
items.add(File("${PasswordRepository.getRepositoryDirectory(applicationContext)}/$path.gpg")) items.add(File("${PasswordRepository.getRepositoryDirectory()}/$path.gpg"))
bindDecryptAndVerify() bindDecryptAndVerify()
} }
@@ -296,9 +296,9 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
when (preference) { when (preference) {
"/first" -> { "/first" -> {
if (!PasswordRepository.isInitialized) { if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize(this) PasswordRepository.initialize()
} }
items = searchPasswords(PasswordRepository.getRepositoryDirectory(this), webViewTitle) items = searchPasswords(PasswordRepository.getRepositoryDirectory(), webViewTitle)
} }
"/never" -> items = ArrayList() "/never" -> items = ArrayList()
else -> getPreferredPasswords(preference) else -> getPreferredPasswords(preference)
@@ -318,9 +318,9 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
when (preference) { when (preference) {
"/first" -> { "/first" -> {
if (!PasswordRepository.isInitialized) { if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize(this) PasswordRepository.initialize()
} }
items = searchPasswords(PasswordRepository.getRepositoryDirectory(this), appName) items = searchPasswords(PasswordRepository.getRepositoryDirectory(), appName)
} }
"/never" -> items = ArrayList() "/never" -> items = ArrayList()
else -> getPreferredPasswords(preference) else -> getPreferredPasswords(preference)
@@ -331,12 +331,12 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
// file into the items list. // file into the items list.
private fun getPreferredPasswords(preference: String) { private fun getPreferredPasswords(preference: String) {
if (!PasswordRepository.isInitialized) { if (!PasswordRepository.isInitialized) {
PasswordRepository.initialize(this) PasswordRepository.initialize()
} }
val preferredPasswords = preference.splitLines() val preferredPasswords = preference.splitLines()
items = ArrayList() items = ArrayList()
for (password in preferredPasswords) { for (password in preferredPasswords) {
val path = PasswordRepository.getRepositoryDirectory(applicationContext).toString() + "/" + password + ".gpg" val path = PasswordRepository.getRepositoryDirectory().toString() + "/" + password + ".gpg"
if (File(path).exists()) { if (File(path).exists()) {
items.add(File(path)) items.add(File(path))
} }
@@ -417,7 +417,7 @@ class AutofillService : AccessibilityService(), CoroutineScope by CoroutineScope
// populate the dialog items, always with pick + pick and match. Could // populate the dialog items, always with pick + pick and match. Could
// make it optional (or make height a setting for the same effect) // make it optional (or make height a setting for the same effect)
val itemNames = arrayOfNulls<CharSequence>(items.size + 2) val itemNames = arrayOfNulls<CharSequence>(items.size + 2)
val passwordDirectory = PasswordRepository.getRepositoryDirectory(applicationContext).toString() val passwordDirectory = PasswordRepository.getRepositoryDirectory().toString()
val autofillFullPath = settings!!.getBoolean(PreferenceKeys.AUTOFILL_FULL_PATH, false) val autofillFullPath = settings!!.getBoolean(PreferenceKeys.AUTOFILL_FULL_PATH, false)
for (i in items.indices) { for (i in items.indices) {
if (autofillFullPath) { if (autofillFullPath) {

View File

@@ -121,7 +121,7 @@ private fun makeRemoteView(
fun makeFillMatchRemoteView(context: Context, file: File, formOrigin: FormOrigin): RemoteViews { fun makeFillMatchRemoteView(context: Context, file: File, formOrigin: FormOrigin): RemoteViews {
val title = formOrigin.getPrettyIdentifier(context, untrusted = false) val title = formOrigin.getPrettyIdentifier(context, untrusted = false)
val directoryStructure = AutofillPreferences.directoryStructure(context) val directoryStructure = AutofillPreferences.directoryStructure(context)
val relativeFile = file.relativeTo(PasswordRepository.getRepositoryDirectory(context)) val relativeFile = file.relativeTo(PasswordRepository.getRepositoryDirectory())
val summary = directoryStructure.getUsernameFor(relativeFile) val summary = directoryStructure.getUsernameFor(relativeFile)
?: directoryStructure.getPathToIdentifierFor(relativeFile) ?: "" ?: directoryStructure.getPathToIdentifierFor(relativeFile) ?: ""
val iconRes = R.drawable.ic_person_black_24dp val iconRes = R.drawable.ic_person_black_24dp

View File

@@ -9,12 +9,10 @@ import android.content.SharedPreferences
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
import java.nio.file.Paths import java.nio.file.Paths
private val Context.defaultSharedPreferences: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(this)
enum class DirectoryStructure(val value: String) { enum class DirectoryStructure(val value: String) {
EncryptedUsername("encrypted_username"), EncryptedUsername("encrypted_username"),
FileBased("file"), FileBased("file"),
@@ -122,8 +120,7 @@ enum class DirectoryStructure(val value: String) {
object AutofillPreferences { object AutofillPreferences {
fun directoryStructure(context: Context): DirectoryStructure { fun directoryStructure(context: Context): DirectoryStructure {
val value = val value = context.sharedPrefs.getString(DirectoryStructure.PREFERENCE, null)
context.defaultSharedPreferences.getString(DirectoryStructure.PREFERENCE, null)
return DirectoryStructure.fromValue(value) return DirectoryStructure.fromValue(value)
} }
} }

View File

@@ -8,6 +8,7 @@ import android.content.Context
import android.util.Patterns import android.util.Patterns
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.publicsuffixlist.PublicSuffixList
@@ -68,8 +69,7 @@ fun getSuffixPlusUpToOne(domain: String, suffix: String): String? {
} }
fun getCustomSuffixes(context: Context): Sequence<String> { fun getCustomSuffixes(context: Context): Sequence<String> {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) return context.sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES, "")!!
return prefs.getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES, "")!!
.splitToSequence('\n') .splitToSequence('\n')
.filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' } .filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' }
} }

View File

@@ -100,7 +100,7 @@ class AutofillSaveActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val repo = PasswordRepository.getRepositoryDirectory(applicationContext) val repo = PasswordRepository.getRepositoryDirectory()
val saveIntent = Intent(this, PasswordCreationActivity::class.java).apply { val saveIntent = Intent(this, PasswordCreationActivity::class.java).apply {
putExtras( putExtras(
bundleOf( bundleOf(

View File

@@ -27,6 +27,7 @@ import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.OPENPGP_PROVIDER import com.zeapo.pwdstore.utils.OPENPGP_PROVIDER
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.clipboard import com.zeapo.pwdstore.utils.clipboard
import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.snackbar import com.zeapo.pwdstore.utils.snackbar
import java.io.File import java.io.File
import me.msfjarvis.openpgpktx.util.OpenPgpApi import me.msfjarvis.openpgpktx.util.OpenPgpApi
@@ -69,7 +70,7 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou
/** /**
* [SharedPreferences] instance used by subclasses to persist settings * [SharedPreferences] instance used by subclasses to persist settings
*/ */
val settings: SharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) } val settings: SharedPreferences by lazy { sharedPrefs }
/** /**
* Handle to the [OpenPgpApi] instance that is used by subclasses to interface with OpenKeychain. * Handle to the [OpenPgpApi] instance that is used by subclasses to interface with OpenKeychain.

View File

@@ -304,7 +304,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
data.action = OpenPgpApi.ACTION_ENCRYPT data.action = OpenPgpApi.ACTION_ENCRYPT
// pass enters the key ID into `.gpg-id`. // pass enters the key ID into `.gpg-id`.
val repoRoot = PasswordRepository.getRepositoryDirectory(applicationContext) val repoRoot = PasswordRepository.getRepositoryDirectory()
val gpgIdentifierFile = File(repoRoot, directory.text.toString()).findTillRoot(".gpg-id", repoRoot) val gpgIdentifierFile = File(repoRoot, directory.text.toString()).findTillRoot(".gpg-id", repoRoot)
if (gpgIdentifierFile == null) { if (gpgIdentifierFile == null) {
snackbar(message = resources.getString(R.string.failed_to_find_key_id)) snackbar(message = resources.getString(R.string.failed_to_find_key_id))
@@ -391,7 +391,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
return@executeApiAsync return@executeApiAsync
} }
if (!isInsideRepository(file)) { if (!file.isInsideRepository()) {
snackbar(message = getString(R.string.message_error_destination_outside_repo)) snackbar(message = getString(R.string.message_error_destination_outside_repo))
return@executeApiAsync return@executeApiAsync
} }

View File

@@ -13,7 +13,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.text.isDigitsOnly import androidx.core.text.isDigitsOnly
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.tag import com.github.ajalt.timberkt.Timber.tag
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -31,6 +30,7 @@ import com.zeapo.pwdstore.git.operation.SyncOperation
import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.getEncryptedPrefs
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
import java.net.URI import java.net.URI
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -61,7 +61,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
settings = PreferenceManager.getDefaultSharedPreferences(this) settings = sharedPrefs
encryptedSettings = getEncryptedPrefs("git_operation") encryptedSettings = getEncryptedPrefs("git_operation")
protocol = Protocol.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_PROTOCOL, null)) protocol = Protocol.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_PROTOCOL, null))
connectionMode = ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH, null)) connectionMode = ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH, null))
@@ -197,7 +197,7 @@ abstract class BaseGitActivity : AppCompatActivity() {
return return
} }
val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory(this)) val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory())
val op = when (operation) { val op = when (operation) {
REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(localDir, url!!, this) REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(localDir, url!!, this)
REQUEST_PULL -> PullOperation(localDir, this) REQUEST_PULL -> PullOperation(localDir, this)

View File

@@ -34,7 +34,7 @@ class GitConfigActivity : BaseGitActivity() {
else else
binding.gitUserName.setText(username) binding.gitUserName.setText(username)
binding.gitUserEmail.setText(email) binding.gitUserEmail.setText(email)
val repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory(this)) val repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory())
if (repo != null) { if (repo != null) {
try { try {
val objectId = repo.resolve(Constants.HEAD) val objectId = repo.resolve(Constants.HEAD)

View File

@@ -113,7 +113,7 @@ class GitServerConfigActivity : BaseGitActivity() {
binding.saveButton.setOnClickListener { binding.saveButton.setOnClickListener {
if (isClone && PasswordRepository.getRepository(null) == null) if (isClone && PasswordRepository.getRepository(null) == null)
PasswordRepository.initialize(this) PasswordRepository.initialize()
when (val result = updateUrl()) { when (val result = updateUrl()) {
GitUpdateUrlResult.Ok -> { GitUpdateUrlResult.Ok -> {
settings.edit { settings.edit {
@@ -161,7 +161,7 @@ class GitServerConfigActivity : BaseGitActivity() {
* Clones the repository, the directory exists, deletes it * Clones the repository, the directory exists, deletes it
*/ */
private fun cloneRepository() { private fun cloneRepository() {
val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory(this)) val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory())
val localDirFiles = localDir.listFiles() ?: emptyArray() val localDirFiles = localDir.listFiles() ?: emptyArray()
// Warn if non-empty folder unless it's a just-initialized store that has just a .git folder // Warn if non-empty folder unless it's a just-initialized store that has just a .git folder
if (localDir.exists() && localDirFiles.isNotEmpty() && if (localDir.exists() && localDirFiles.isNotEmpty() &&

View File

@@ -158,9 +158,7 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) } .edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }
} }
is SshjSessionFactory -> { is SshjSessionFactory -> {
callingActivity.applicationContext callingActivity.getEncryptedPrefs("git_operation").edit {
.getEncryptedPrefs("git_operation")
.edit {
remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE)
remove(PreferenceKeys.HTTPS_PASSWORD) remove(PreferenceKeys.HTTPS_PASSWORD)
} }

View File

@@ -8,6 +8,7 @@ import android.content.Context
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
class XkpwdDictionary(context: Context) { class XkpwdDictionary(context: Context) {
@@ -15,7 +16,7 @@ class XkpwdDictionary(context: Context) {
val words: Map<Int, List<String>> val words: Map<Int, List<String>>
init { init {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = context.sharedPrefs
val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "")!! val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "")!!
val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE) val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE)

View File

@@ -19,6 +19,7 @@ import com.jcraft.jsch.KeyPair
import com.zeapo.pwdstore.R import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding import com.zeapo.pwdstore.databinding.ActivitySshKeygenBinding
import com.zeapo.pwdstore.utils.getEncryptedPrefs import com.zeapo.pwdstore.utils.getEncryptedPrefs
import com.zeapo.pwdstore.utils.sharedPrefs
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@@ -91,8 +92,7 @@ class SshKeyGenActivity : AppCompatActivity() {
if (e == null) { if (e == null) {
val df = ShowSshKeyFragment() val df = ShowSshKeyFragment()
df.show(supportFragmentManager, "public_key") df.show(supportFragmentManager, "public_key")
val prefs = PreferenceManager.getDefaultSharedPreferences(this) sharedPrefs.edit { putBoolean("use_generated_key", true) }
prefs.edit { putBoolean("use_generated_key", true) }
} else { } else {
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.error_generate_ssh_key)) .setTitle(getString(R.string.error_generate_ssh_key))

View File

@@ -19,6 +19,7 @@ import com.zeapo.pwdstore.SearchableRepositoryAdapter
import com.zeapo.pwdstore.stableId import com.zeapo.pwdstore.stableId
import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.sharedPrefs
import java.io.File import java.io.File
open class PasswordItemRecyclerAdapter : open class PasswordItemRecyclerAdapter :
@@ -49,8 +50,7 @@ open class PasswordItemRecyclerAdapter :
lateinit var itemDetails: ItemDetailsLookup.ItemDetails<String> lateinit var itemDetails: ItemDetailsLookup.ItemDetails<String>
fun bind(item: PasswordItem) { fun bind(item: PasswordItem) {
val settings = val settings = itemView.context.sharedPrefs
PreferenceManager.getDefaultSharedPreferences(itemView.context.applicationContext)
val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false) val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "") val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
val source = if (parentPath.isNotEmpty()) { val source = if (parentPath.isNotEmpty()) {

View File

@@ -18,6 +18,7 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceManager
import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey import androidx.security.crypto.MasterKey
import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.d
@@ -97,6 +98,9 @@ fun Context.getEncryptedPrefs(fileName: String): SharedPreferences {
) )
} }
val Context.sharedPrefs: SharedPreferences
get() = PreferenceManager.getDefaultSharedPreferences(applicationContext)
suspend fun FragmentActivity.commitChange( suspend fun FragmentActivity.commitChange(
message: String, message: String,
finishWithResultOnEnd: Intent? = null, finishWithResultOnEnd: Intent? = null,
@@ -109,7 +113,7 @@ suspend fun FragmentActivity.commitChange(
} }
return return
} }
object : GitOperation(getRepositoryDirectory(this@commitChange), this@commitChange) { object : GitOperation(getRepositoryDirectory(), this@commitChange) {
override val commands = arrayOf( override val commands = arrayOf(
git.add().addFilepattern("."), git.add().addFilepattern("."),
git.status(), git.status(),
@@ -151,6 +155,6 @@ val Context.autofillManager: AutofillManager?
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
get() = getSystemService() get() = getSystemService()
fun FragmentActivity.isInsideRepository(file: File): Boolean { fun File.isInsideRepository(): Boolean {
return file.canonicalPath.contains(getRepositoryDirectory(this).canonicalPath) return canonicalPath.contains(getRepositoryDirectory().canonicalPath)
} }

View File

@@ -4,10 +4,9 @@
*/ */
package com.zeapo.pwdstore.utils package com.zeapo.pwdstore.utils
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import com.zeapo.pwdstore.Application
import java.io.File import java.io.File
import java.io.FileFilter import java.io.FileFilter
import java.util.Comparator import java.util.Comparator
@@ -49,7 +48,9 @@ open class PasswordRepository protected constructor() {
companion object { companion object {
private var repository: Repository? = null private var repository: Repository? = null
private lateinit var settings: SharedPreferences private val settings by lazy { Application.instance.sharedPrefs }
private val filesDir
get() = Application.instance.filesDir
/** /**
* Returns the git repository * Returns the git repository
@@ -152,27 +153,21 @@ open class PasswordRepository protected constructor() {
} }
@JvmStatic @JvmStatic
fun getRepositoryDirectory(context: Context): File { fun getRepositoryDirectory(): File {
if (!::settings.isInitialized) {
settings = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
}
return if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) { return if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
if (externalRepo != null) if (externalRepo != null)
File(externalRepo) File(externalRepo)
else else
File(context.filesDir.toString(), "/store") File(filesDir.toString(), "/store")
} else { } else {
File(context.filesDir.toString(), "/store") File(filesDir.toString(), "/store")
} }
} }
@JvmStatic @JvmStatic
fun initialize(context: Context): Repository? { fun initialize(): Repository? {
if (!::settings.isInitialized) { val dir = getRepositoryDirectory()
settings = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
}
val dir = getRepositoryDirectory(context)
// uninitialize the repo if the dir does not exist or is absolutely empty // uninitialize the repo if the dir does not exist or is absolutely empty
settings.edit { settings.edit {
if (!dir.exists() || !dir.isDirectory || dir.listFiles()!!.isEmpty()) { if (!dir.exists() || !dir.isDirectory || dir.listFiles()!!.isEmpty()) {