Expand show hidden folders to also cover files (#1059)
* PasswordItem: only strip .gpg suffixes Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Add preference key and migration for showing all hidden contents Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Allow showing both hidden files and directories Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Add tests for hidden folder setting migration Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Add changelog entry Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Slightly improve migration logic Skip migration if old key is not found and always delete the previous key even if its set to false. Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Tweak wording Suggested-by: Fabian Henneke <fabian@henneke.me> Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Assert previous key's removal in tests Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
8ec3320df7
commit
1ce3ef4ea3
12 changed files with 60 additions and 21 deletions
|
@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
- A descriptive error message is shown if no username is specified in the Git server settings
|
||||
- Remove explicit protocol choice from Git server settings, it is now inferred from your URL
|
||||
- 'Show hidden folders' is now 'Show hidden files and folders'
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.zeapo.pwdstore
|
||||
|
||||
import android.content.Context
|
||||
|
@ -13,10 +14,10 @@ import com.zeapo.pwdstore.git.config.Protocol
|
|||
import com.zeapo.pwdstore.utils.PreferenceKeys
|
||||
import com.zeapo.pwdstore.utils.getString
|
||||
import com.zeapo.pwdstore.utils.sharedPrefs
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
class MigrationsTest {
|
||||
|
||||
private fun checkOldKeysAreRemoved(context: Context) = with(context.sharedPrefs) {
|
||||
|
@ -84,4 +85,25 @@ class MigrationsTest {
|
|||
"https://github.com/Android-Password-Store/pass-test"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyHiddenFoldersMigrationIfDisabled() {
|
||||
val context = Application.instance.applicationContext
|
||||
context.sharedPrefs.edit { clear() }
|
||||
runMigrations(context)
|
||||
assertEquals(true, context.sharedPrefs.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, true))
|
||||
assertEquals(false, context.sharedPrefs.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyHiddenFoldersMigrationIfEnabled() {
|
||||
val context = Application.instance.applicationContext
|
||||
context.sharedPrefs.edit {
|
||||
clear()
|
||||
putBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, true)
|
||||
}
|
||||
runMigrations(context)
|
||||
assertEquals(false, context.sharedPrefs.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false))
|
||||
assertEquals(true, context.sharedPrefs.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.net.URI
|
|||
|
||||
fun runMigrations(context: Context) {
|
||||
migrateToGitUrlBasedConfig(context)
|
||||
migrateToHideAll(context)
|
||||
}
|
||||
|
||||
private fun migrateToGitUrlBasedConfig(context: Context) {
|
||||
|
@ -84,3 +85,12 @@ private fun migrateToGitUrlBasedConfig(context: Context) {
|
|||
e { "Failed to migrate to URL-based Git config, generated URL is invalid" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateToHideAll(context: Context) {
|
||||
context.sharedPrefs.all[PreferenceKeys.SHOW_HIDDEN_FOLDERS] ?: return
|
||||
val isHidden = context.sharedPrefs.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
|
||||
context.sharedPrefs.edit {
|
||||
remove(PreferenceKeys.SHOW_HIDDEN_FOLDERS)
|
||||
putBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, isHidden)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ private fun PasswordItem.Companion.makeComparator(
|
|||
PasswordRepository.PasswordSortOrder.FOLDER_FIRST -> compareBy { it.type }
|
||||
// In order to let INDEPENDENT not distinguish between items based on their type, we simply
|
||||
// declare them all equal at this stage.
|
||||
PasswordRepository.PasswordSortOrder.INDEPENDENT -> Comparator<PasswordItem> { _, _ -> 0 }
|
||||
PasswordRepository.PasswordSortOrder.INDEPENDENT -> Comparator { _, _ -> 0 }
|
||||
PasswordRepository.PasswordSortOrder.FILE_FIRST -> compareByDescending { it.type }
|
||||
PasswordRepository.PasswordSortOrder.RECENTLY_USED -> PasswordRepository.PasswordSortOrder.RECENTLY_USED.comparator
|
||||
}
|
||||
|
@ -140,8 +140,8 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
|
|||
private val root
|
||||
get() = PasswordRepository.getRepositoryDirectory()
|
||||
private val settings by lazy { application.sharedPrefs }
|
||||
private val showHiddenDirs
|
||||
get() = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
|
||||
private val showHiddenContents
|
||||
get() = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false)
|
||||
private val defaultSearchMode
|
||||
get() = if (settings.getBoolean(PreferenceKeys.FILTER_RECURSIVELY, true)) {
|
||||
SearchMode.RecursivelyInSubdirectories
|
||||
|
@ -254,8 +254,9 @@ class SearchableRepositoryViewModel(application: Application) : AndroidViewModel
|
|||
}.asLiveData(Dispatchers.IO)
|
||||
|
||||
private fun shouldTake(file: File) = with(file) {
|
||||
if (showHiddenContents) return true
|
||||
if (isDirectory) {
|
||||
!isHidden || showHiddenDirs
|
||||
!isHidden
|
||||
} else {
|
||||
!isHidden && file.extension == "gpg"
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ open class PasswordItemRecyclerAdapter :
|
|||
|
||||
fun bind(item: PasswordItem) {
|
||||
val settings = itemView.context.sharedPrefs
|
||||
val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
|
||||
val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false)
|
||||
val parentPath = item.fullPathToParent.replace("(^/)|(/$)".toRegex(), "")
|
||||
val source = if (parentPath.isNotEmpty()) {
|
||||
"$parentPath\n$item"
|
||||
|
@ -62,8 +62,8 @@ open class PasswordItemRecyclerAdapter :
|
|||
name.text = spannable
|
||||
if (item.type == PasswordItem.TYPE_CATEGORY) {
|
||||
folderIndicator.visibility = View.VISIBLE
|
||||
val children = item.file.listFiles { pathname ->
|
||||
!(!showHidden && (pathname.isDirectory && pathname.isHidden))
|
||||
val children = with(item.file) {
|
||||
if (showHidden) listFiles() else listFiles { pathname -> pathname.isDirectory && !pathname.isHidden }
|
||||
} ?: emptyArray<File>()
|
||||
val count = children.size
|
||||
childCount.visibility = if (count > 0) View.VISIBLE else View.GONE
|
||||
|
|
|
@ -33,7 +33,7 @@ data class PasswordItem(
|
|||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name.replace(".gpg", "")
|
||||
return name.replace("\\.gpg$".toRegex(), "")
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
@ -226,12 +226,10 @@ open class PasswordRepository protected constructor() {
|
|||
// We need to recover the passwords then parse the files
|
||||
val passList = getFilesList(path).also { it.sortBy { f -> f.name } }
|
||||
val passwordList = ArrayList<PasswordItem>()
|
||||
val showHiddenDirs = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_FOLDERS, false)
|
||||
val showHidden = settings.getBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, false)
|
||||
|
||||
if (passList.size == 0) return passwordList
|
||||
if (showHiddenDirs) {
|
||||
passList.filter { !(it.isFile && it.isHidden) }.toCollection(passList.apply { clear() })
|
||||
} else {
|
||||
if (!showHidden) {
|
||||
passList.filter { !it.isHidden }.toCollection(passList.apply { clear() })
|
||||
}
|
||||
passList.forEach { file ->
|
||||
|
|
|
@ -28,16 +28,21 @@ object PreferenceKeys {
|
|||
const val GIT_EXTERNAL = "git_external"
|
||||
const val GIT_EXTERNAL_REPO = "git_external_repo"
|
||||
const val GIT_REMOTE_AUTH = "git_remote_auth"
|
||||
|
||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||
const val GIT_REMOTE_LOCATION = "git_remote_location"
|
||||
|
||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||
const val GIT_REMOTE_PORT = "git_remote_port"
|
||||
|
||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||
const val GIT_REMOTE_PROTOCOL = "git_remote_protocol"
|
||||
const val GIT_DELETE_REPO = "git_delete_repo"
|
||||
|
||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||
const val GIT_REMOTE_SERVER = "git_remote_server"
|
||||
const val GIT_REMOTE_URL = "git_remote_url"
|
||||
|
||||
@Deprecated("Use GIT_REMOTE_URL instead")
|
||||
const val GIT_REMOTE_USERNAME = "git_remote_username"
|
||||
const val GIT_SERVER_INFO = "git_server_info"
|
||||
|
@ -55,7 +60,13 @@ object PreferenceKeys {
|
|||
const val REPO_CHANGED = "repo_changed"
|
||||
const val SEARCH_ON_START = "search_on_start"
|
||||
const val SHOW_EXTRA_CONTENT = "show_extra_content"
|
||||
|
||||
@Deprecated(
|
||||
message = "Use SHOW_HIDDEN_CONTENTS instead",
|
||||
replaceWith = ReplaceWith("PreferenceKeys.SHOW_HIDDEN_CONTENTS")
|
||||
)
|
||||
const val SHOW_HIDDEN_FOLDERS = "show_hidden_folders"
|
||||
const val SHOW_HIDDEN_CONTENTS = "show_hidden_contents"
|
||||
const val SORT_ORDER = "sort_order"
|
||||
const val SHOW_PASSWORD = "show_password"
|
||||
const val SSH_KEY = "ssh_key"
|
||||
|
|
|
@ -269,8 +269,6 @@
|
|||
<string name="access_sdcard_text">O local do armazenamento está em seu cartão SD ou armazenamento interno, mas o aplicativo não tem permissão para acessá-lo.</string>
|
||||
<string name="your_public_key">Sua chave pública</string>
|
||||
<string name="error_generate_ssh_key">Erro ao tentar gerar a chave SSH</string>
|
||||
<string name="pref_show_hidden_title">Mostrar pastas ocultas</string>
|
||||
<string name="pref_show_hidden_summary">Incluir diretórios ocultos na lista de senhas</string>
|
||||
<string name="title_create_folder">Criar pasta</string>
|
||||
<string name="title_rename_folder">Renomear pasta</string>
|
||||
<string name="message_category_error_empty_field">O nome da categoria não pode ser vazio</string>
|
||||
|
|
|
@ -251,8 +251,6 @@
|
|||
<string name="ssh_openkeystore_clear_keyid">Очистить сохраненный SSH Key идентификатор OpenKystortore</string>
|
||||
<string name="your_public_key">Ваш публичный ключ</string>
|
||||
<string name="error_generate_ssh_key">Возникла ошибка при попытке генерации ssh ключа</string>
|
||||
<string name="pref_show_hidden_title">Показать скрытые папки</string>
|
||||
<string name="pref_show_hidden_summary">Включить скрытые директории в список паролей</string>
|
||||
<string name="title_create_folder">Создать папку</string>
|
||||
<string name="button_create">Создать</string>
|
||||
<string name="pref_search_on_start">Открыть поиск на старте</string>
|
||||
|
|
|
@ -302,8 +302,8 @@
|
|||
<string name="access_sdcard_text">The store location is in your SD Card or Internal storage, but the app does not have permission to access it.</string>
|
||||
<string name="your_public_key">Your public key</string>
|
||||
<string name="error_generate_ssh_key">Error while trying to generate the ssh-key</string>
|
||||
<string name="pref_show_hidden_title">Show hidden folders</string>
|
||||
<string name="pref_show_hidden_summary">Include hidden directories in the password list</string>
|
||||
<string name="pref_show_hidden_title">Show all files and folders</string>
|
||||
<string name="pref_show_hidden_summary">Include non-password files and directories in the password list</string>
|
||||
<string name="title_create_folder">Create folder</string>
|
||||
<string name="title_rename_folder">Rename folder</string>
|
||||
<string name="message_category_error_empty_field">Category name can\'t be empty</string>
|
||||
|
|
|
@ -127,7 +127,7 @@
|
|||
app:title="@string/pref_search_on_start" />
|
||||
<CheckBoxPreference
|
||||
app:defaultValue="false"
|
||||
app:key="show_hidden_folders"
|
||||
app:key="show_hidden_contents"
|
||||
app:persistent="true"
|
||||
app:summary="@string/pref_show_hidden_summary"
|
||||
app:title="@string/pref_show_hidden_title" />
|
||||
|
|
Loading…
Reference in a new issue