Revamp onboarding logic (#1068)
This commit is contained in:
parent
b7f58cfb6e
commit
e731943437
15 changed files with 233 additions and 194 deletions
|
@ -55,6 +55,7 @@
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="WRAP_ON_TYPING" value="0" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:label="@string/app_name" />
|
android:label="@string/app_name" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".OnboardingActivity"
|
||||||
|
android:configChanges="orientation|screenSize" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".LaunchActivity"
|
android:name=".LaunchActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
|
|
203
app/src/main/java/com/zeapo/pwdstore/OnboardingActivity.kt
Normal file
203
app/src/main/java/com/zeapo/pwdstore/OnboardingActivity.kt
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.zeapo.pwdstore
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.github.ajalt.timberkt.d
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.zeapo.pwdstore.databinding.ActivityOnboardingBinding
|
||||||
|
import com.zeapo.pwdstore.git.BaseGitActivity
|
||||||
|
import com.zeapo.pwdstore.git.GitServerConfigActivity
|
||||||
|
import com.zeapo.pwdstore.utils.PasswordRepository
|
||||||
|
import com.zeapo.pwdstore.utils.PreferenceKeys
|
||||||
|
import com.zeapo.pwdstore.utils.checkRuntimePermission
|
||||||
|
import com.zeapo.pwdstore.utils.getString
|
||||||
|
import com.zeapo.pwdstore.utils.listFilesRecursively
|
||||||
|
import com.zeapo.pwdstore.utils.sharedPrefs
|
||||||
|
import com.zeapo.pwdstore.utils.viewBinding
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class OnboardingActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private val binding by viewBinding(ActivityOnboardingBinding::inflate)
|
||||||
|
private val settings by lazy { applicationContext.sharedPrefs }
|
||||||
|
private val sortOrder: PasswordRepository.PasswordSortOrder
|
||||||
|
get() = PasswordRepository.PasswordSortOrder.getSortOrder(settings)
|
||||||
|
|
||||||
|
private val cloneAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||||
|
if (result.resultCode == RESULT_OK) {
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val repositoryInitAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||||
|
if (result.resultCode == RESULT_OK) {
|
||||||
|
initializeRepositoryInfo()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val externalDirectorySelectAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||||
|
if (result.resultCode == RESULT_OK) {
|
||||||
|
if (checkExternalDirectory()) {
|
||||||
|
finish()
|
||||||
|
} else {
|
||||||
|
createRepository()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
supportActionBar?.hide()
|
||||||
|
setContentView(binding.root)
|
||||||
|
binding.settingsButton.setOnClickListener {
|
||||||
|
startActivity(Intent(this, UserPreference::class.java))
|
||||||
|
}
|
||||||
|
binding.localDirectoryButton.setOnClickListener {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(resources.getString(R.string.location_dialog_title))
|
||||||
|
.setMessage(resources.getString(R.string.location_dialog_create_text))
|
||||||
|
.setPositiveButton(resources.getString(R.string.location_hidden)) { _, _ ->
|
||||||
|
createRepoInHiddenDir()
|
||||||
|
}
|
||||||
|
.setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ ->
|
||||||
|
createRepoFromExternalDir()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
binding.cloneFromServerButton.setOnClickListener {
|
||||||
|
cloneToHiddenDir()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a remote Git repository to the app's private directory
|
||||||
|
*/
|
||||||
|
private fun cloneToHiddenDir() {
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, false) }
|
||||||
|
cloneAction.launch(Intent(this, GitServerConfigActivity::class.java).apply {
|
||||||
|
putExtra(BaseGitActivity.REQUEST_ARG_OP, BaseGitActivity.REQUEST_CLONE)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an empty repository in the app's private directory
|
||||||
|
*/
|
||||||
|
private fun createRepoInHiddenDir() {
|
||||||
|
settings.edit {
|
||||||
|
putBoolean(PreferenceKeys.GIT_EXTERNAL, false)
|
||||||
|
remove(PreferenceKeys.GIT_EXTERNAL_REPO)
|
||||||
|
}
|
||||||
|
initializeRepositoryInfo()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an empty repository in a selected directory if one does not already exist
|
||||||
|
*/
|
||||||
|
private fun createRepoFromExternalDir() {
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, true) }
|
||||||
|
val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
||||||
|
if (externalRepo == null) {
|
||||||
|
if (!checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
registerForActivityResult(RequestPermission()) { granted ->
|
||||||
|
if (granted) {
|
||||||
|
externalDirectorySelectAction.launch(Intent(this, UserPreference::class.java).apply {
|
||||||
|
putExtra("operation", "git_external")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(resources.getString(R.string.directory_selected_title))
|
||||||
|
.setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
|
||||||
|
.setPositiveButton(resources.getString(R.string.use)) { _, _ ->
|
||||||
|
if (!checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
registerForActivityResult(RequestPermission()) { granted ->
|
||||||
|
if (granted) {
|
||||||
|
initializeRepositoryInfo()
|
||||||
|
}
|
||||||
|
}.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
} else {
|
||||||
|
initializeRepositoryInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(resources.getString(R.string.change)) { _, _ ->
|
||||||
|
if (!checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
registerForActivityResult(RequestPermission()) { granted ->
|
||||||
|
if (granted) {
|
||||||
|
repositoryInitAction.launch(Intent(this, UserPreference::class.java).apply {
|
||||||
|
putExtra("operation", "git_external")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkExternalDirectory(): Boolean {
|
||||||
|
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
|
||||||
|
settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) != null) {
|
||||||
|
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
||||||
|
val dir = externalRepoPath?.let { File(it) }
|
||||||
|
if (dir != null && // The directory could be opened
|
||||||
|
dir.exists() && // The directory exists
|
||||||
|
dir.isDirectory && // The directory, is really a directory
|
||||||
|
dir.listFilesRecursively().isNotEmpty() && // The directory contains files
|
||||||
|
// The directory contains a non-zero number of password files
|
||||||
|
PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(), sortOrder).isNotEmpty()
|
||||||
|
) {
|
||||||
|
PasswordRepository.closeRepository()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRepository() {
|
||||||
|
val localDir = PasswordRepository.getRepositoryDirectory()
|
||||||
|
try {
|
||||||
|
check(localDir.exists() || localDir.mkdir()) { "Failed to create directory!" }
|
||||||
|
PasswordRepository.createRepository(localDir)
|
||||||
|
if (!PasswordRepository.isInitialized) {
|
||||||
|
PasswordRepository.initialize()
|
||||||
|
}
|
||||||
|
if (File(localDir.absolutePath + "/.gpg-id").createNewFile()) {
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException("Failed to initialize repository state.")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
if (!localDir.delete()) {
|
||||||
|
d { "Failed to delete local repository: $localDir" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initializeRepositoryInfo() {
|
||||||
|
val externalRepo = settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
|
||||||
|
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
||||||
|
if (externalRepo && !checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (externalRepo && externalRepoPath != null) {
|
||||||
|
if (checkExternalDirectory()) return
|
||||||
|
}
|
||||||
|
createRepository()
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.pm.ShortcutInfo.Builder
|
import android.content.pm.ShortcutInfo.Builder
|
||||||
import android.content.pm.ShortcutManager
|
import android.content.pm.ShortcutManager
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
|
@ -26,7 +25,6 @@ import androidx.activity.viewModels
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
@ -54,16 +52,13 @@ import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment
|
import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment
|
||||||
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.PasswordRepository.Companion.closeRepository
|
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.createRepository
|
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getPasswords
|
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepository
|
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepository
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
|
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.getRepositoryDirectory
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.initialize
|
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.initialize
|
||||||
import com.zeapo.pwdstore.utils.PasswordRepository.Companion.isInitialized
|
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.PreferenceKeys
|
||||||
import com.zeapo.pwdstore.utils.base64
|
import com.zeapo.pwdstore.utils.base64
|
||||||
|
import com.zeapo.pwdstore.utils.checkRuntimePermission
|
||||||
import com.zeapo.pwdstore.utils.commitChange
|
import com.zeapo.pwdstore.utils.commitChange
|
||||||
import com.zeapo.pwdstore.utils.contains
|
import com.zeapo.pwdstore.utils.contains
|
||||||
import com.zeapo.pwdstore.utils.getString
|
import com.zeapo.pwdstore.utils.getString
|
||||||
|
@ -94,43 +89,9 @@ class PasswordStore : BaseGitActivity() {
|
||||||
ViewModelProvider.AndroidViewModelFactory(application)
|
ViewModelProvider.AndroidViewModelFactory(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cloneAction = registerForActivityResult(StartActivityForResult()) { result ->
|
private val listRefreshAction = registerForActivityResult(StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == RESULT_OK) {
|
if (result.resultCode == RESULT_OK) {
|
||||||
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
|
refreshPasswordList()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val repositoryInitAction = registerForActivityResult(StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
initializeRepositoryInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val directoryChangeAction = registerForActivityResult(StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) &&
|
|
||||||
settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) != null) {
|
|
||||||
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
|
||||||
val dir = externalRepoPath?.let { File(it) }
|
|
||||||
if (dir != null &&
|
|
||||||
dir.exists() &&
|
|
||||||
dir.isDirectory &&
|
|
||||||
dir.listFilesRecursively().isNotEmpty() &&
|
|
||||||
getPasswords(dir, getRepositoryDirectory(), sortOrder).isNotEmpty()) {
|
|
||||||
closeRepository()
|
|
||||||
checkLocalRepository()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkPermissionsAndCloneAction.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val checkPermissionsAndCloneAction = registerForActivityResult(RequestPermission()) { granted ->
|
|
||||||
if (granted) {
|
|
||||||
val intent = Intent(activity, GitServerConfigActivity::class.java)
|
|
||||||
intent.putExtra(REQUEST_ARG_OP, REQUEST_CLONE)
|
|
||||||
cloneAction.launch(intent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,9 +121,7 @@ class PasswordStore : BaseGitActivity() {
|
||||||
// prevent attempt to create password list fragment
|
// prevent attempt to create password list fragment
|
||||||
var savedInstance = savedInstanceState
|
var savedInstance = savedInstanceState
|
||||||
if (savedInstanceState != null && (!settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) ||
|
if (savedInstanceState != null && (!settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) ||
|
||||||
ContextCompat.checkSelfPermission(
|
!checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE))) {
|
||||||
activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
!= PackageManager.PERMISSION_GRANTED)) {
|
|
||||||
savedInstance = null
|
savedInstance = null
|
||||||
}
|
}
|
||||||
super.onCreate(savedInstance)
|
super.onCreate(savedInstance)
|
||||||
|
@ -229,7 +188,7 @@ class PasswordStore : BaseGitActivity() {
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
|
if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) {
|
||||||
hasRequiredStoragePermissions(true)
|
hasRequiredStoragePermissions()
|
||||||
} else {
|
} else {
|
||||||
checkLocalRepository()
|
checkLocalRepository()
|
||||||
}
|
}
|
||||||
|
@ -361,47 +320,6 @@ class PasswordStore : BaseGitActivity() {
|
||||||
searchItem.collapseActionView()
|
searchItem.collapseActionView()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRepository() {
|
|
||||||
if (!isInitialized) {
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
val localDir = getRepositoryDirectory()
|
|
||||||
try {
|
|
||||||
check(localDir.mkdir()) { "Failed to create directory!" }
|
|
||||||
createRepository(localDir)
|
|
||||||
if (File(localDir.absolutePath + "/.gpg-id").createNewFile()) {
|
|
||||||
settings.edit { putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true) }
|
|
||||||
} else {
|
|
||||||
throw IllegalStateException("Failed to initialize repository state.")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
if (!localDir.delete()) {
|
|
||||||
d { "Failed to delete local repository" }
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
checkLocalRepository()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeRepositoryInfo() {
|
|
||||||
val externalRepo = settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)
|
|
||||||
val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
|
||||||
if (externalRepo && !hasRequiredStoragePermissions()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (externalRepo && externalRepoPath != null) {
|
|
||||||
val dir = File(externalRepoPath)
|
|
||||||
if (dir.exists() && dir.isDirectory &&
|
|
||||||
getPasswords(dir, getRepositoryDirectory(), sortOrder).isNotEmpty()) {
|
|
||||||
closeRepository()
|
|
||||||
checkLocalRepository()
|
|
||||||
return // if not empty, just show me the passwords!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createRepository()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runGitOperation(operation: Int) = lifecycleScope.launch {
|
private fun runGitOperation(operation: Int) = lifecycleScope.launch {
|
||||||
launchGitOperation(operation).fold(
|
launchGitOperation(operation).fold(
|
||||||
success = { refreshPasswordList() },
|
success = { refreshPasswordList() },
|
||||||
|
@ -413,10 +331,8 @@ class PasswordStore : BaseGitActivity() {
|
||||||
* Validates if storage permission is granted, and requests for it if not. The return value
|
* Validates if storage permission is granted, and requests for it if not. The return value
|
||||||
* is true if the permission has been granted.
|
* is true if the permission has been granted.
|
||||||
*/
|
*/
|
||||||
private fun hasRequiredStoragePermissions(checkLocalRepo: Boolean = false): Boolean {
|
private fun hasRequiredStoragePermissions(): Boolean {
|
||||||
val cloning = supportFragmentManager.findFragmentByTag("ToCloneOrNot") != null
|
return if (!checkRuntimePermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
return if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
!= PackageManager.PERMISSION_GRANTED && !cloning) {
|
|
||||||
Snackbar.make(
|
Snackbar.make(
|
||||||
findViewById(R.id.main_layout),
|
findViewById(R.id.main_layout),
|
||||||
getString(R.string.access_sdcard_text),
|
getString(R.string.access_sdcard_text),
|
||||||
|
@ -432,7 +348,6 @@ class PasswordStore : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
if (checkLocalRepo)
|
|
||||||
checkLocalRepository()
|
checkLocalRepository()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -478,11 +393,7 @@ class PasswordStore : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
supportActionBar!!.hide()
|
startActivity(Intent(this, OnboardingActivity::class.java))
|
||||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
|
||||||
supportFragmentManager.commit {
|
|
||||||
replace(R.id.main_layout, ToCloneOrNot(), "ToCloneOrNot")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,60 +732,6 @@ class PasswordStore : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun initRepository(operation: Int) {
|
|
||||||
closeRepository()
|
|
||||||
MaterialAlertDialogBuilder(this)
|
|
||||||
.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(PreferenceKeys.GIT_EXTERNAL, false) }
|
|
||||||
when (operation) {
|
|
||||||
NEW_REPO_BUTTON -> initializeRepositoryInfo()
|
|
||||||
CLONE_REPO_BUTTON -> {
|
|
||||||
val intent = Intent(activity, GitServerConfigActivity::class.java)
|
|
||||||
intent.putExtra(REQUEST_ARG_OP, REQUEST_CLONE)
|
|
||||||
cloneAction.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ ->
|
|
||||||
settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, true) }
|
|
||||||
val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO)
|
|
||||||
if (externalRepo == null) {
|
|
||||||
val intent = Intent(activity, UserPreference::class.java)
|
|
||||||
intent.putExtra("operation", "git_external")
|
|
||||||
when (operation) {
|
|
||||||
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
|
|
||||||
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
.setTitle(resources.getString(R.string.directory_selected_title))
|
|
||||||
.setMessage(resources.getString(R.string.directory_selected_message, externalRepo))
|
|
||||||
.setPositiveButton(resources.getString(R.string.use)) { _, _ ->
|
|
||||||
when (operation) {
|
|
||||||
NEW_REPO_BUTTON -> initializeRepositoryInfo()
|
|
||||||
CLONE_REPO_BUTTON -> {
|
|
||||||
val intent = Intent(activity, GitServerConfigActivity::class.java)
|
|
||||||
intent.putExtra(REQUEST_ARG_OP, REQUEST_CLONE)
|
|
||||||
cloneAction.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(resources.getString(R.string.change)) { _, _ ->
|
|
||||||
val intent = Intent(activity, UserPreference::class.java)
|
|
||||||
intent.putExtra("operation", "git_external")
|
|
||||||
when (operation) {
|
|
||||||
NEW_REPO_BUTTON -> repositoryInitAction.launch(intent)
|
|
||||||
CLONE_REPO_BUTTON -> directoryChangeAction.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun matchPasswordWithApp(item: PasswordItem) {
|
fun matchPasswordWithApp(item: PasswordItem) {
|
||||||
val path = item.file
|
val path = item.file
|
||||||
.absolutePath
|
.absolutePath
|
||||||
|
@ -886,14 +743,9 @@ class PasswordStore : BaseGitActivity() {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sortOrder: PasswordRepository.PasswordSortOrder
|
|
||||||
get() = getSortOrder(settings)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val REQUEST_ARG_PATH = "PATH"
|
const val REQUEST_ARG_PATH = "PATH"
|
||||||
const val CLONE_REPO_BUTTON = 401
|
|
||||||
const val NEW_REPO_BUTTON = 402
|
|
||||||
private fun isPrintable(c: Char): Boolean {
|
private fun isPrintable(c: Char): Boolean {
|
||||||
val block = UnicodeBlock.of(c)
|
val block = UnicodeBlock.of(c)
|
||||||
return (!Character.isISOControl(c) &&
|
return (!Character.isISOControl(c) &&
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
*/
|
|
||||||
package com.zeapo.pwdstore
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.zeapo.pwdstore.databinding.FragmentToCloneOrNotBinding
|
|
||||||
import com.zeapo.pwdstore.utils.viewBinding
|
|
||||||
|
|
||||||
class ToCloneOrNot : Fragment(R.layout.fragment_to_clone_or_not) {
|
|
||||||
|
|
||||||
private val binding by viewBinding(FragmentToCloneOrNotBinding::bind)
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
binding.settingsButton.setOnClickListener { startActivity(Intent(requireContext(), UserPreference::class.java)) }
|
|
||||||
binding.localDirectoryButton.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.NEW_REPO_BUTTON) }
|
|
||||||
binding.cloneFromServerButton.setOnClickListener { (requireActivity() as PasswordStore).initRepository(PasswordStore.CLONE_REPO_BUTTON) }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -124,7 +124,10 @@ class GitServerConfigActivity : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
snackbar.dismiss()
|
snackbar.dismiss()
|
||||||
launchGitOperation(REQUEST_CLONE).fold(
|
launchGitOperation(REQUEST_CLONE).fold(
|
||||||
success = ::finishOnSuccessHandler,
|
success = {
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
finish()
|
||||||
|
},
|
||||||
failure = ::finishAfterPromptOnErrorHandler,
|
failure = ::finishAfterPromptOnErrorHandler,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -159,7 +162,10 @@ class GitServerConfigActivity : BaseGitActivity() {
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
launchGitOperation(REQUEST_CLONE).fold(
|
launchGitOperation(REQUEST_CLONE).fold(
|
||||||
success = ::finishOnSuccessHandler,
|
success = {
|
||||||
|
setResult(RESULT_OK)
|
||||||
|
finish()
|
||||||
|
},
|
||||||
failure = ::finishAfterPromptOnErrorHandler,
|
failure = ::finishAfterPromptOnErrorHandler,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.app.KeyguardManager
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
|
@ -17,6 +18,7 @@ import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
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.preference.PreferenceManager
|
||||||
|
@ -131,6 +133,10 @@ suspend fun FragmentActivity.commitChange(
|
||||||
}.execute()
|
}.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun FragmentActivity.checkRuntimePermission(permission: String): Boolean {
|
||||||
|
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension function for [AlertDialog] that requests focus for the
|
* Extension function for [AlertDialog] that requests focus for the
|
||||||
* view whose id is [id]. Solution based on a StackOverflow
|
* view whose id is [id]. Solution based on a StackOverflow
|
||||||
|
|
|
@ -20,7 +20,6 @@ import org.eclipse.jgit.transport.URIish
|
||||||
|
|
||||||
open class PasswordRepository protected constructor() {
|
open class PasswordRepository protected constructor() {
|
||||||
|
|
||||||
@Suppress("Unused")
|
|
||||||
enum class PasswordSortOrder(val comparator: Comparator<PasswordItem>) {
|
enum class PasswordSortOrder(val comparator: Comparator<PasswordItem>) {
|
||||||
|
|
||||||
FOLDER_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem ->
|
FOLDER_FIRST(Comparator { p1: PasswordItem, p2: PasswordItem ->
|
||||||
|
@ -93,10 +92,7 @@ open class PasswordRepository protected constructor() {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isGitRepo(): Boolean {
|
fun isGitRepo(): Boolean {
|
||||||
if (repository != null) {
|
if (repository != null) {
|
||||||
// Check if remote exists
|
return repository!!.objectDatabase.exists()
|
||||||
return repository!!.config.getSubsections("remote").isNotEmpty() &&
|
|
||||||
repository!!.objectDatabase.exists() &&
|
|
||||||
repository!!.allRefs.isNotEmpty()
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
<string name="clone">Klonovat ze serveru</string>
|
<string name="clone">Klonovat ze serveru</string>
|
||||||
<string name="initialize">Použít místní adresář</string>
|
<string name="initialize">Použít místní adresář</string>
|
||||||
<string name="location_dialog_title">Umístění úložiště</string>
|
<string name="location_dialog_title">Umístění úložiště</string>
|
||||||
<string name="location_dialog_text">Zvolte kde vytvořit, nebo kam klonovat vaše úložiště hesel.</string>
|
|
||||||
<string name="location_sdcard">SD-Karta</string>
|
<string name="location_sdcard">SD-Karta</string>
|
||||||
<string name="location_hidden">Skryté (Preferováno)</string>
|
<string name="location_hidden">Skryté (Preferováno)</string>
|
||||||
<string name="external_repository_dialog_title">Vyberte kam ukládat hesla</string>
|
<string name="external_repository_dialog_title">Vyberte kam ukládat hesla</string>
|
||||||
|
|
|
@ -46,7 +46,6 @@
|
||||||
<string name="clone">Clonar desde servidor</string>
|
<string name="clone">Clonar desde servidor</string>
|
||||||
<string name="initialize">Usar directorio local</string>
|
<string name="initialize">Usar directorio local</string>
|
||||||
<string name="location_dialog_title">Ubicación del repositorio</string>
|
<string name="location_dialog_title">Ubicación del repositorio</string>
|
||||||
<string name="location_dialog_text">Selecciona dónde crear o clonar tu repositorio de contraseñas.</string>
|
|
||||||
<string name="location_sdcard">Tarjeta SD</string>
|
<string name="location_sdcard">Tarjeta SD</string>
|
||||||
<string name="location_hidden">Oculto (Recomendado)</string>
|
<string name="location_hidden">Oculto (Recomendado)</string>
|
||||||
<string name="external_repository_dialog_title">Selecciona dónde almacenar tus contraseñas</string>
|
<string name="external_repository_dialog_title">Selecciona dónde almacenar tus contraseñas</string>
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
<string name="clone">Cloner depuis le serveur</string>
|
<string name="clone">Cloner depuis le serveur</string>
|
||||||
<string name="initialize">Utiliser un répertoire local</string>
|
<string name="initialize">Utiliser un répertoire local</string>
|
||||||
<string name="location_dialog_title">Location du répertoire</string>
|
<string name="location_dialog_title">Location du répertoire</string>
|
||||||
<string name="location_dialog_text">Sélectionnez l\'emplacement de création ou de clonage de votre répertoire de mots de passe.</string>
|
|
||||||
<string name="location_sdcard">Carte SD</string>
|
<string name="location_sdcard">Carte SD</string>
|
||||||
<string name="location_hidden">Caché (Préféré)</string>
|
<string name="location_hidden">Caché (Préféré)</string>
|
||||||
<string name="external_repository_dialog_title">Choisissez où sauvegarder les mots de passe</string>
|
<string name="external_repository_dialog_title">Choisissez où sauvegarder les mots de passe</string>
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
<string name="clone">Clonar do servidor</string>
|
<string name="clone">Clonar do servidor</string>
|
||||||
<string name="initialize">Usar diretório local</string>
|
<string name="initialize">Usar diretório local</string>
|
||||||
<string name="location_dialog_title">Local do repositório</string>
|
<string name="location_dialog_title">Local do repositório</string>
|
||||||
<string name="location_dialog_text">Selecione onde criar ou clonar o repositório da sua senha.</string>
|
|
||||||
<string name="location_sdcard">Cartão SD</string>
|
<string name="location_sdcard">Cartão SD</string>
|
||||||
<string name="location_hidden">Oculto (preferencial)</string>
|
<string name="location_hidden">Oculto (preferencial)</string>
|
||||||
<string name="external_repository_dialog_title">Escolha onde armazenar as senhas</string>
|
<string name="external_repository_dialog_title">Escolha onde armazenar as senhas</string>
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
<string name="clone">Клонировать с сервера</string>
|
<string name="clone">Клонировать с сервера</string>
|
||||||
<string name="initialize">Использовать локальную директорию</string>
|
<string name="initialize">Использовать локальную директорию</string>
|
||||||
<string name="location_dialog_title">Расположение репозитория</string>
|
<string name="location_dialog_title">Расположение репозитория</string>
|
||||||
<string name="location_dialog_text">Укажите где создать/клонироввать ваш репозиторий с паролями.</string>
|
|
||||||
<string name="location_sdcard">SD-Карта</string>
|
<string name="location_sdcard">SD-Карта</string>
|
||||||
<string name="location_hidden">Скрытый (Предпочтительно)</string>
|
<string name="location_hidden">Скрытый (Предпочтительно)</string>
|
||||||
<string name="external_repository_dialog_title">Выберете где хранить пароли</string>
|
<string name="external_repository_dialog_title">Выберете где хранить пароли</string>
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
<string name="clone">Clone from server</string>
|
<string name="clone">Clone from server</string>
|
||||||
<string name="initialize">Use local directory</string>
|
<string name="initialize">Use local directory</string>
|
||||||
<string name="location_dialog_title">Repository location</string>
|
<string name="location_dialog_title">Repository location</string>
|
||||||
<string name="location_dialog_text">Select where to create or clone your password repository.</string>
|
<string name="location_dialog_create_text">Select where to create your password repository</string>
|
||||||
<string name="location_sdcard">SD-Card</string>
|
<string name="location_sdcard">SD-Card</string>
|
||||||
<string name="location_hidden">Hidden (Preferred)</string>
|
<string name="location_hidden">Hidden (Preferred)</string>
|
||||||
<string name="external_repository_dialog_title">Choose where to store the passwords</string>
|
<string name="external_repository_dialog_title">Choose where to store the passwords</string>
|
||||||
|
|
Loading…
Reference in a new issue