Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop' into feature/autofill_sms_otp
Browse files Browse the repository at this point in the history
* develop:
  Scroll to files and enter folders when created (#909)
  Run a treewide reformat (#908)
  Improve how secrets and stored and used (#907)
  Improve and refactor Autofill heuristics (#905)
  Use PreferenceKeys file to manage SharedPreferences keys. (#891)
  Bump version
  Prepare release 1.9.2
  update changelog
  Workaround to prevent crash on first run (#898)
  build: bump version
  Prepare release 1.9.1
  Backport Actions fixes (#894)
  Remove API 30 from pull request test matrix (#879)
  CHANGELOG: reword to better clarify fixes
  Prevent cached passwords from being wiped (#884)
  Use remembered credential even if it is empty (#880)
  Reset SSH passphrase after SSH key import (#885)
  Add relnotes for #871 (#872)
  Add org.gnu.icecat as a trusted multi-origin browser (#871)
  • Loading branch information
msfjarvis committed Jul 1, 2020
2 parents 26dc067 + 1c9f797 commit 8bcc2da
Show file tree
Hide file tree
Showing 39 changed files with 392 additions and 210 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ All notable changes to this project will be documented in this file.
- TOTP support is reintroduced by popular demand. HOTP continues to be unsupported and heavily discouraged.
- Initial support for detecting and filling OTP fields with Autofill
- Importing TOTP secrets using QR codes
- Navigate into newly created folders and scroll to newly created passwords

## [1.9.2] - 2020-06-30

### Fixed

- App crashes upon launching the app for the first time

## [1.9.1] - 2020-06-28

Expand Down Expand Up @@ -220,7 +227,9 @@ All notable changes to this project will be documented in this file.

- Fix elements overlapping.

[Unreleased]: https://github.com/android-password-store/Android-Password-Store/compare/1.9.1...HEAD
[Unreleased]: https://github.com/android-password-store/Android-Password-Store/compare/1.9.2...HEAD

[1.9.2]: https://github.com/android-password-store/Android-Password-Store/compare/1.9.1...1.9.2

[1.9.1]: https://github.com/android-password-store/Android-Password-Store/compare/1.9.0...1.9.1

Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ android {

defaultConfig {
applicationId 'dev.msfjarvis.aps'
versionCode 10911
versionCode 10921
versionName '1.10.0-SNAPSHOT'
}

Expand Down
8 changes: 5 additions & 3 deletions app/src/main/java/com/zeapo/pwdstore/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.DebugTree
import com.github.ajalt.timberkt.Timber.plant
import com.zeapo.pwdstore.git.config.setUpBouncyCastleForSshj
import com.zeapo.pwdstore.utils.PreferenceKeys

@Suppress("Unused")
class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener {
Expand All @@ -23,7 +24,8 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
override fun onCreate() {
super.onCreate()
prefs = PreferenceManager.getDefaultSharedPreferences(this)
if (BuildConfig.ENABLE_DEBUG_FEATURES || prefs?.getBoolean("enable_debug_logging", false) == true) {
if (BuildConfig.ENABLE_DEBUG_FEATURES || prefs?.getBoolean(PreferenceKeys.ENABLE_DEBUG_LOGGING, false) ==
true) {
plant(DebugTree())
}
prefs?.registerOnSharedPreferenceChangeListener(this)
Expand All @@ -37,13 +39,13 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
}

override fun onSharedPreferenceChanged(prefs: SharedPreferences, key: String) {
if (key == "app_theme") {
if (key == PreferenceKeys.APP_THEME) {
setNightMode()
}
}

private fun setNightMode() {
AppCompatDelegate.setDefaultNightMode(when (prefs?.getString("app_theme", getString(R.string.app_theme_def))) {
AppCompatDelegate.setDefaultNightMode(when (prefs?.getString(PreferenceKeys.APP_THEME, getString(R.string.app_theme_def))) {
"light" -> MODE_NIGHT_NO
"dark" -> MODE_NIGHT_YES
"follow_system" -> MODE_NIGHT_FOLLOW_SYSTEM
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/com/zeapo/pwdstore/ClipboardService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.d
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.clipboard
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -44,7 +45,7 @@ class ClipboardService : Service() {

ACTION_START -> {
val time = try {
Integer.parseInt(settings.getString("general_show_time", "45") as String)
Integer.parseInt(settings.getString(PreferenceKeys.GENERAL_SHOW_TIME, "45") as String)
} catch (e: NumberFormatException) {
45
}
Expand Down Expand Up @@ -82,7 +83,7 @@ class ClipboardService : Service() {
}

private fun clearClipboard() {
val deepClear = settings.getBoolean("clear_clipboard_20x", false)
val deepClear = settings.getBoolean(PreferenceKeys.CLEAR_CLIPBOARD_20X, false)
val clipboard = clipboard

if (clipboard != null) {
Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/com/zeapo/pwdstore/LaunchActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.zeapo.pwdstore.crypto.DecryptActivity
import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PreferenceKeys

class LaunchActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
if (prefs.getBoolean("biometric_auth", false)) {
if (prefs.getBoolean(PreferenceKeys.BIOMETRIC_AUTH, false)) {
BiometricAuthenticator.authenticate(this) {
when (it) {
is BiometricAuthenticator.Result.Success -> {
startTargetActivity(false)
}
is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
prefs.edit { remove("biometric_auth") }
prefs.edit { remove(PreferenceKeys.BIOMETRIC_AUTH) }
startTargetActivity(false)
}
is BiometricAuthenticator.Result.Failure, BiometricAuthenticator.Result.Cancelled -> {
Expand Down
30 changes: 23 additions & 7 deletions app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter
import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet
import com.zeapo.pwdstore.utils.PasswordItem
import com.zeapo.pwdstore.utils.PasswordRepository
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.viewBinding
import me.zhanghai.android.fastscroll.FastScrollerBuilder
import java.io.File
Expand All @@ -42,6 +43,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {

private var recyclerViewStateToRestore: Parcelable? = null
private var actionMode: ActionMode? = null
private var scrollTarget: File? = null

private val model: SearchableRepositoryViewModel by activityViewModels()
private val binding by viewBinding(PasswordRecyclerViewBinding::bind)
Expand Down Expand Up @@ -78,7 +80,8 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
} else {
// When authentication is set to ConnectionMode.None then the only git operation we
// can run is a pull, so automatically fallback to that.
val operationId = when (ConnectionMode.fromString(settings.getString("git_remote_auth", null))) {
val operationId = when (ConnectionMode.fromString(settings.getString
(PreferenceKeys.GIT_REMOTE_AUTH, null))) {
ConnectionMode.None -> BaseGitActivity.REQUEST_PULL
else -> BaseGitActivity.REQUEST_SYNC
}
Expand Down Expand Up @@ -130,6 +133,11 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
// When the result is filtered, we always scroll to the top since that is where
// the best fuzzy match appears.
recyclerView.scrollToPosition(0)
} else if (scrollTarget != null) {
scrollTarget?.let {
recyclerView.scrollToPosition(recyclerAdapter.getPositionForFile(it))
}
scrollTarget == null
} else {
// When the result is not filtered and there is a saved scroll position for it,
// we try to restore it.
Expand Down Expand Up @@ -221,12 +229,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {
listener = object : OnFragmentInteractionListener {
override fun onFragmentInteraction(item: PasswordItem) {
if (item.type == PasswordItem.TYPE_CATEGORY) {
requireStore().clearSearch()
model.navigateTo(
item.file,
recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState()
)
requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true)
navigateTo(item.file)
} else {
if (requireArguments().getBoolean("matchWith", false)) {
requireStore().matchPasswordWithApp(item)
Expand Down Expand Up @@ -270,6 +273,19 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) {

fun createPassword() = requireStore().createPassword()

fun navigateTo(file: File) {
requireStore().clearSearch()
model.navigateTo(
file,
recyclerViewState = binding.passRecycler.layoutManager!!.onSaveInstanceState()
)
requireStore().supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

fun scrollToOnNextRefresh(file: File) {
scrollTarget = file
}

interface OnFragmentInteractionListener {
fun onFragmentInteraction(item: PasswordItem)
}
Expand Down
58 changes: 34 additions & 24 deletions app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirect
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.initialize
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.isInitialized
import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.getSortOrder
import com.zeapo.pwdstore.utils.PreferenceKeys
import com.zeapo.pwdstore.utils.commitChange
import com.zeapo.pwdstore.utils.contains
import com.zeapo.pwdstore.utils.isInsideRepository
import com.zeapo.pwdstore.utils.listFilesRecursively
import com.zeapo.pwdstore.utils.requestInputFocusOnView
Expand Down Expand Up @@ -121,7 +123,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
// 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("git_external", false) ||
if (savedInstanceState != null && (!settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) ||
ContextCompat.checkSelfPermission(
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED)) {
Expand Down Expand Up @@ -187,12 +189,12 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
public override fun onResume() {
super.onResume()
// do not attempt to checkLocalRepository() if no storage permission: immediate crash
if (settings.getBoolean("git_external", false)) {
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
hasRequiredStoragePermissions(true)
} else {
checkLocalRepository()
}
if (settings.getBoolean("search_on_start", false) && ::searchItem.isInitialized) {
if (settings.getBoolean(PreferenceKeys.SEARCH_ON_START, false) && ::searchItem.isInitialized) {
if (!searchItem.isActionViewExpanded) {
searchItem.expandActionView()
}
Expand All @@ -211,7 +213,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val menuRes = when {
ConnectionMode.fromString(settings.getString("git_remote_auth", null))
ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH, null))
== ConnectionMode.None -> R.menu.main_menu_no_auth
PasswordRepository.isGitRepo() -> R.menu.main_menu_git
else -> R.menu.main_menu_non_git
Expand Down Expand Up @@ -261,7 +263,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
return true
}
})
if (settings.getBoolean("search_on_start", false)) {
if (settings.getBoolean(PreferenceKeys.SEARCH_ON_START, false)) {
searchItem.expandActionView()
}
return super.onPrepareOptionsMenu(menu)
Expand Down Expand Up @@ -346,7 +348,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
check(localDir.mkdir()) { "Failed to create directory!" }
createRepository(localDir)
if (File(localDir.absolutePath + "/.gpg-id").createNewFile()) {
settings.edit { putBoolean("repository_initialized", true) }
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
} else {
throw IllegalStateException("Failed to initialize repository state.")
}
Expand All @@ -361,8 +363,8 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}

private fun initializeRepositoryInfo() {
val externalRepo = settings.getBoolean("git_external", false)
val externalRepoPath = settings.getString("git_external_repo", null)
val externalRepo = settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
if (externalRepo && !hasRequiredStoragePermissions()) {
return
}
Expand All @@ -375,7 +377,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
return // if not empty, just show me the passwords!
}
}
val keyIds = settings.getStringSet("openpgp_key_ids_set", HashSet())
val keyIds = settings.getStringSet(PreferenceKeys.OPENPGP_KEY_IDS_SET, HashSet())
if (keyIds != null && keyIds.isEmpty()) {
MaterialAlertDialogBuilder(this)
.setMessage(resources.getString(R.string.key_dialog_text))
Expand Down Expand Up @@ -431,12 +433,12 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}

private fun checkLocalRepository(localDir: File?) {
if (localDir != null && settings.getBoolean("repository_initialized", false)) {
if (localDir != null && settings.getBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)) {
d { "Check, dir: ${localDir.absolutePath}" }
// do not push the fragment if we already have it
if (supportFragmentManager.findFragmentByTag("PasswordsList") == null ||
settings.getBoolean("repo_changed", false)) {
settings.edit { putBoolean("repo_changed", false) }
settings.getBoolean(PreferenceKeys.REPO_CHANGED, false)) {
settings.edit { putBoolean(PreferenceKeys.REPO_CHANGED, false) }
plist = PasswordFragment()
val args = Bundle()
args.putString(REQUEST_ARG_PATH, getRepositoryDirectory(applicationContext).absolutePath)
Expand Down Expand Up @@ -535,7 +537,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.show()
return false
}
if (settings.getStringSet("openpgp_key_ids_set", HashSet()).isNullOrEmpty()) {
if (settings.getStringSet(PreferenceKeys.OPENPGP_KEY_IDS_SET, HashSet()).isNullOrEmpty()) {
MaterialAlertDialogBuilder(this)
.setTitle(resources.getString(R.string.no_key_selected_dialog_title))
.setMessage(resources.getString(R.string.no_key_selected_dialog_text))
Expand Down Expand Up @@ -733,10 +735,17 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
/**
* Refreshes the password list by re-executing the last navigation or search action, preserving
* the navigation stack and scroll position. If the current directory no longer exists,
* navigation is reset to the repository root.
* navigation is reset to the repository root. If the optional [target] argument is provided,
* it will be entered if it is a directory or scrolled into view if it is a file (both inside
* the current directory).
*/
fun refreshPasswordList() {
if (model.currentDir.value?.isDirectory == true) {
fun refreshPasswordList(target: File? = null) {
if (target?.isDirectory == true && model.currentDir.value?.contains(target) == true) {
plist?.navigateTo(target)
} else if (target?.isFile == true && model.currentDir.value?.contains(target) == true) {
// Creating new passwords is handled by an activity, so we will refresh in onStart.
plist?.scrollToOnNextRefresh(target)
} else if (model.currentDir.value?.isDirectory == true) {
model.forceRefresh()
} else {
model.reset()
Expand All @@ -751,7 +760,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
// if we get here with a RESULT_OK then it's probably OK :)
BaseGitActivity.REQUEST_CLONE -> settings.edit { putBoolean("repository_initialized", true) }
BaseGitActivity.REQUEST_CLONE -> settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
// if went from decrypt->edit and user saved changes, we need to commitChange
REQUEST_CODE_DECRYPT_AND_VERIFY -> {
if (data != null && data.getBooleanExtra("needCommit", false)) {
Expand All @@ -763,16 +772,17 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
REQUEST_CODE_ENCRYPT -> {
commitChange(resources.getString(R.string.git_commit_add_text,
data!!.extras!!.getString("LONG_NAME")))
data!!.extras!!.getString(PasswordCreationActivity.RETURN_EXTRA_LONG_NAME)))
refreshPasswordList(File(data.extras!!.getString(PasswordCreationActivity.RETURN_EXTRA_CREATED_FILE)!!))
}
BaseGitActivity.REQUEST_INIT, NEW_REPO_BUTTON -> initializeRepositoryInfo()
BaseGitActivity.REQUEST_SYNC, BaseGitActivity.REQUEST_PULL -> refreshPasswordList()
HOME -> checkLocalRepository()
// duplicate code
CLONE_REPO_BUTTON -> {
if (settings.getBoolean("git_external", false) &&
settings.getString("git_external_repo", null) != null) {
val externalRepoPath = settings.getString("git_external_repo", null)
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) != null) {
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
val dir = externalRepoPath?.let { File(it) }
if (dir != null &&
dir.exists() &&
Expand Down Expand Up @@ -832,7 +842,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
.setTitle(resources.getString(R.string.location_dialog_title))
.setMessage(resources.getString(R.string.location_dialog_text))
.setPositiveButton(resources.getString(R.string.location_hidden)) { _, _ ->
settings.edit { putBoolean("git_external", false) }
settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, false) }
when (operation) {
NEW_REPO_BUTTON -> initializeRepositoryInfo()
CLONE_REPO_BUTTON -> {
Expand All @@ -843,8 +853,8 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) {
}
}
.setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ ->
settings.edit { putBoolean("git_external", true) }
val externalRepo = settings.getString("git_external_repo", null)
settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, true) }
val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null)
if (externalRepo == null) {
val intent = Intent(activity, UserPreference::class.java)
intent.putExtra("operation", "git_external")
Expand Down
Loading

0 comments on commit 8bcc2da

Please sign in to comment.