Revamp onboarding logic (#1068)

This commit is contained in:
Harsh Shandilya 2020-09-04 12:06:55 +05:30 committed by GitHub
parent b7f58cfb6e
commit e731943437
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 233 additions and 194 deletions

View file

@ -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>

View file

@ -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"

View 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()
}
}

View file

@ -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) &&

View file

@ -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) }
}
}

View file

@ -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,
) )
} }

View file

@ -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

View file

@ -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
} }

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>