mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Better UriUtil methods
This commit is contained in:
@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.activities
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
@@ -348,7 +347,7 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
{
|
{
|
||||||
// Open Keepass doc to create field references
|
// Open Keepass doc to create field references
|
||||||
startActivity(Intent(Intent.ACTION_VIEW,
|
startActivity(Intent(Intent.ACTION_VIEW,
|
||||||
Uri.parse(getString(R.string.field_references_url))))
|
UriUtil.parse(getString(R.string.field_references_url))))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,13 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
if (fileName.isEmpty())
|
if (fileName.isEmpty())
|
||||||
fileName = it
|
fileName = it
|
||||||
}
|
}
|
||||||
launchPasswordActivityWithPath(fileName)
|
UriUtil.parse(fileName)?.let { fileNameUri ->
|
||||||
|
launchPasswordActivityWithPath(fileNameUri)
|
||||||
|
} ?: run {
|
||||||
|
Log.e(TAG, "Unable to open the database link")
|
||||||
|
Snackbar.make(activity_file_selection_coordinator_layout, getString(R.string.error_can_not_handle_uri), Snackbar.LENGTH_LONG).asError().show()
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create button
|
// Create button
|
||||||
@@ -147,10 +153,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
mOpenFileHelper = OpenFileHelper(this)
|
mOpenFileHelper = OpenFileHelper(this)
|
||||||
browseButtonView = findViewById(R.id.browse_button)
|
browseButtonView = findViewById(R.id.browse_button)
|
||||||
browseButtonView?.setOnClickListener(mOpenFileHelper!!.getOpenFileOnClickViewListener {
|
browseButtonView?.setOnClickListener(mOpenFileHelper!!.getOpenFileOnClickViewListener {
|
||||||
Uri.parse("file://" + openFileNameView!!.text.toString())
|
UriUtil.parse(openFileNameView?.text?.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// History list
|
// History list
|
||||||
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
|
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
|
||||||
fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
|
fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
|
||||||
@@ -159,9 +164,11 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
// Construct adapter with listeners
|
// Construct adapter with listeners
|
||||||
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this)
|
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this)
|
||||||
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
|
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
|
||||||
launchPasswordActivity(
|
UriUtil.parse(fileDatabaseHistoryEntityToOpen.databaseUri)?.let { databaseFileUri ->
|
||||||
fileDatabaseHistoryEntityToOpen.databaseUri,
|
launchPasswordActivity(
|
||||||
fileDatabaseHistoryEntityToOpen.keyFileUri)
|
databaseFileUri,
|
||||||
|
UriUtil.parse(fileDatabaseHistoryEntityToOpen.keyFileUri))
|
||||||
|
}
|
||||||
updateFileListVisibility()
|
updateFileListVisibility()
|
||||||
}
|
}
|
||||||
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete ->
|
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete ->
|
||||||
@@ -186,14 +193,12 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
&& savedInstanceState.containsKey(EXTRA_STAY)
|
&& savedInstanceState.containsKey(EXTRA_STAY)
|
||||||
&& savedInstanceState.getBoolean(EXTRA_STAY, false))) {
|
&& savedInstanceState.getBoolean(EXTRA_STAY, false))) {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "")
|
val databasePath = prefs.getString(PasswordActivity.KEY_DEFAULT_DATABASE_PATH, "")
|
||||||
|
|
||||||
try {
|
UriUtil.parse(databasePath)?.let { databaseFileUri ->
|
||||||
UriUtil.verifyFilePath(fileName) { path ->
|
launchPasswordActivityWithPath(databaseFileUri)
|
||||||
launchPasswordActivityWithPath(path)
|
} ?: run {
|
||||||
}
|
Log.e(TAG, "Unable to launch Password Activity")
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
Log.e(TAG, "Unable to launch Password Activity", e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,12 +234,12 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
Log.e(TAG, error, e)
|
Log.e(TAG, error, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchPasswordActivity(fileName: String, keyFile: String?) {
|
private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) {
|
||||||
EntrySelectionHelper.doEntrySelectionAction(intent,
|
EntrySelectionHelper.doEntrySelectionAction(intent,
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
PasswordActivity.launch(this@FileDatabaseSelectActivity,
|
PasswordActivity.launch(this@FileDatabaseSelectActivity,
|
||||||
fileName, keyFile)
|
databaseUri, keyFile)
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
fileNoFoundAction(e)
|
fileNoFoundAction(e)
|
||||||
}
|
}
|
||||||
@@ -242,7 +247,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity,
|
PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity,
|
||||||
fileName, keyFile)
|
databaseUri, keyFile)
|
||||||
finish()
|
finish()
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
fileNoFoundAction(e)
|
fileNoFoundAction(e)
|
||||||
@@ -252,7 +257,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
try {
|
try {
|
||||||
PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
|
PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity,
|
||||||
fileName, keyFile,
|
databaseUri, keyFile,
|
||||||
assistStructure)
|
assistStructure)
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
fileNoFoundAction(e)
|
fileNoFoundAction(e)
|
||||||
@@ -262,8 +267,8 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchPasswordActivityWithPath(path: String) {
|
private fun launchPasswordActivityWithPath(databaseUri: Uri) {
|
||||||
launchPasswordActivity(path, "")
|
launchPasswordActivity(databaseUri, null)
|
||||||
// Delete flickering for kitkat <=
|
// Delete flickering for kitkat <=
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||||
overridePendingTransition(0, 0)
|
overridePendingTransition(0, 0)
|
||||||
@@ -323,7 +328,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
keyFileChecked: Boolean, keyFile: Uri?) {
|
keyFileChecked: Boolean, keyFile: Uri?) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UriUtil.parseUriFile(mDatabaseFileUri)?.let { databaseUri ->
|
mDatabaseFileUri?.let { databaseUri ->
|
||||||
|
|
||||||
// Create the new database
|
// Create the new database
|
||||||
ProgressDialogThread(this@FileDatabaseSelectActivity,
|
ProgressDialogThread(this@FileDatabaseSelectActivity,
|
||||||
@@ -388,7 +393,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) {
|
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) {
|
||||||
launchPasswordActivityWithPath(uri.toString())
|
launchPasswordActivityWithPath(uri)
|
||||||
} else {
|
} else {
|
||||||
fileSelectExpandableLayout?.expand(false)
|
fileSelectExpandableLayout?.expand(false)
|
||||||
openFileNameView?.setText(uri.toString())
|
openFileNameView?.setText(uri.toString())
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import androidx.biometric.BiometricManager
|
|||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog
|
import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog
|
||||||
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
||||||
import com.kunzisoft.keepass.utils.ClipDataCompat
|
|
||||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
|
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||||
@@ -70,7 +69,6 @@ import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
|||||||
import com.kunzisoft.keepass.view.asError
|
import com.kunzisoft.keepass.view.asError
|
||||||
import kotlinx.android.synthetic.main.activity_password.*
|
import kotlinx.android.synthetic.main.activity_password.*
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.lang.Exception
|
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class PasswordActivity : StylishActivity() {
|
class PasswordActivity : StylishActivity() {
|
||||||
@@ -196,21 +194,16 @@ class PasswordActivity : StylishActivity() {
|
|||||||
|
|
||||||
val databaseUriRetrieve = intent.data
|
val databaseUriRetrieve = intent.data
|
||||||
// Stop activity here if we can't verify database URI
|
// Stop activity here if we can't verify database URI
|
||||||
try {
|
if (!UriUtil.verifyFileUri(databaseUriRetrieve)) {
|
||||||
UriUtil.verifyFileUri(databaseUriRetrieve)
|
Log.e(TAG, "File URI not validate")
|
||||||
} catch (e : Exception) {
|
|
||||||
Log.e(TAG, "File URI not validate", e)
|
|
||||||
Toast.makeText(this@PasswordActivity, e.message, Toast.LENGTH_LONG).show()
|
|
||||||
finish()
|
finish()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
databaseUri = databaseUriRetrieve
|
databaseUri = databaseUriRetrieve
|
||||||
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE)
|
keyFileUri = UriUtil.getUriFromIntent(intent, KEY_KEYFILE)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME))
|
databaseUri = intent.getParcelableExtra(KEY_FILENAME)
|
||||||
keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE))
|
keyFileUri = intent.getParcelableExtra(KEY_KEYFILE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post init uri with KeyFile if needed
|
// Post init uri with KeyFile if needed
|
||||||
@@ -245,14 +238,16 @@ class PasswordActivity : StylishActivity() {
|
|||||||
|
|
||||||
// Define listeners for default database checkbox and validate button
|
// Define listeners for default database checkbox and validate button
|
||||||
checkboxDefaultDatabaseView?.setOnCheckedChangeListener { _, isChecked ->
|
checkboxDefaultDatabaseView?.setOnCheckedChangeListener { _, isChecked ->
|
||||||
var newDefaultFileName = ""
|
var newDefaultFileName: Uri? = null
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
newDefaultFileName = databaseFileUri?.toString() ?: newDefaultFileName
|
newDefaultFileName = databaseFileUri ?: newDefaultFileName
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs?.edit()?.apply {
|
newDefaultFileName?.let {
|
||||||
putString(KEY_DEFAULT_FILENAME, newDefaultFileName)
|
prefs?.edit()?.apply {
|
||||||
apply()
|
putString(KEY_DEFAULT_DATABASE_PATH, newDefaultFileName.toString())
|
||||||
|
apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val backupManager = BackupManager(this@PasswordActivity)
|
val backupManager = BackupManager(this@PasswordActivity)
|
||||||
@@ -261,10 +256,10 @@ class PasswordActivity : StylishActivity() {
|
|||||||
confirmButtonView?.setOnClickListener { verifyCheckboxesAndLoadDatabase() }
|
confirmButtonView?.setOnClickListener { verifyCheckboxesAndLoadDatabase() }
|
||||||
|
|
||||||
// Retrieve settings for default database
|
// Retrieve settings for default database
|
||||||
val defaultFilename = prefs?.getString(KEY_DEFAULT_FILENAME, "")
|
val defaultFilename = prefs?.getString(KEY_DEFAULT_DATABASE_PATH, "")
|
||||||
if (databaseFileUri != null
|
if (databaseFileUri != null
|
||||||
&& databaseFileUri.path != null && databaseFileUri.path!!.isNotEmpty()
|
&& databaseFileUri.path != null && databaseFileUri.path!!.isNotEmpty()
|
||||||
&& databaseFileUri == UriUtil.parseUriFile(defaultFilename)) {
|
&& databaseFileUri == UriUtil.parse(defaultFilename)) {
|
||||||
checkboxDefaultDatabaseView?.isChecked = true
|
checkboxDefaultDatabaseView?.isChecked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +383,7 @@ class PasswordActivity : StylishActivity() {
|
|||||||
|
|
||||||
private fun verifyCheckboxesAndLoadDatabase(cipherDatabaseEntity: CipherDatabaseEntity? = null) {
|
private fun verifyCheckboxesAndLoadDatabase(cipherDatabaseEntity: CipherDatabaseEntity? = null) {
|
||||||
val password: String? = passwordView?.text?.toString()
|
val password: String? = passwordView?.text?.toString()
|
||||||
val keyFile: Uri? = UriUtil.parseUriFile(keyFileView?.text?.toString())
|
val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString())
|
||||||
verifyCheckboxesAndLoadDatabase(password, keyFile, cipherDatabaseEntity)
|
verifyCheckboxesAndLoadDatabase(password, keyFile, cipherDatabaseEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +396,7 @@ class PasswordActivity : StylishActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyKeyFileCheckboxAndLoadDatabase(password: String?) {
|
private fun verifyKeyFileCheckboxAndLoadDatabase(password: String?) {
|
||||||
val keyFile: Uri? = UriUtil.parseUriFile(keyFileView?.text?.toString())
|
val keyFile: Uri? = UriUtil.parse(keyFileView?.text?.toString())
|
||||||
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
|
val keyFileUri = if (checkboxKeyFileView?.isChecked != true) null else keyFile
|
||||||
loadDatabase(password, keyFileUri)
|
loadDatabase(password, keyFileUri)
|
||||||
}
|
}
|
||||||
@@ -632,7 +627,7 @@ class PasswordActivity : StylishActivity() {
|
|||||||
|
|
||||||
private val TAG = PasswordActivity::class.java.name
|
private val TAG = PasswordActivity::class.java.name
|
||||||
|
|
||||||
const val KEY_DEFAULT_FILENAME = "defaultFileName"
|
const val KEY_DEFAULT_DATABASE_PATH = "KEY_DEFAULT_DATABASE_PATH"
|
||||||
|
|
||||||
private const val KEY_FILENAME = "fileName"
|
private const val KEY_FILENAME = "fileName"
|
||||||
private const val KEY_KEYFILE = "keyFile"
|
private const val KEY_KEYFILE = "keyFile"
|
||||||
@@ -641,10 +636,10 @@ class PasswordActivity : StylishActivity() {
|
|||||||
private const val KEY_PASSWORD = "password"
|
private const val KEY_PASSWORD = "password"
|
||||||
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
||||||
|
|
||||||
private fun buildAndLaunchIntent(activity: Activity, fileName: String, keyFile: String?,
|
private fun buildAndLaunchIntent(activity: Activity, databaseFile: Uri, keyFile: Uri?,
|
||||||
intentBuildLauncher: (Intent) -> Unit) {
|
intentBuildLauncher: (Intent) -> Unit) {
|
||||||
val intent = Intent(activity, PasswordActivity::class.java)
|
val intent = Intent(activity, PasswordActivity::class.java)
|
||||||
intent.putExtra(KEY_FILENAME, fileName)
|
intent.putExtra(KEY_FILENAME, databaseFile)
|
||||||
if (keyFile != null)
|
if (keyFile != null)
|
||||||
intent.putExtra(KEY_KEYFILE, keyFile)
|
intent.putExtra(KEY_KEYFILE, keyFile)
|
||||||
intentBuildLauncher.invoke(intent)
|
intentBuildLauncher.invoke(intent)
|
||||||
@@ -659,10 +654,9 @@ class PasswordActivity : StylishActivity() {
|
|||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun launch(
|
fun launch(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
databaseFile: Uri,
|
||||||
keyFile: String?) {
|
keyFile: Uri?) {
|
||||||
UriUtil.verifyFilePath(fileName)
|
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
|
||||||
activity.startActivity(intent)
|
activity.startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,11 +670,9 @@ class PasswordActivity : StylishActivity() {
|
|||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun launchForKeyboardResult(
|
fun launchForKeyboardResult(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
databaseFile: Uri,
|
||||||
keyFile: String?) {
|
keyFile: Uri?) {
|
||||||
UriUtil.verifyFilePath(fileName)
|
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
|
||||||
|
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
|
||||||
EntrySelectionHelper.startActivityForEntrySelection(activity, intent)
|
EntrySelectionHelper.startActivityForEntrySelection(activity, intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,20 +687,18 @@ class PasswordActivity : StylishActivity() {
|
|||||||
@Throws(FileNotFoundException::class)
|
@Throws(FileNotFoundException::class)
|
||||||
fun launchForAutofillResult(
|
fun launchForAutofillResult(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
databaseFile: Uri,
|
||||||
keyFile: String?,
|
keyFile: Uri?,
|
||||||
assistStructure: AssistStructure?) {
|
assistStructure: AssistStructure?) {
|
||||||
UriUtil.verifyFilePath(fileName)
|
|
||||||
|
|
||||||
if (assistStructure != null) {
|
if (assistStructure != null) {
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
buildAndLaunchIntent(activity, databaseFile, keyFile) { intent ->
|
||||||
AutofillHelper.startActivityForAutofillResult(
|
AutofillHelper.startActivityForAutofillResult(
|
||||||
activity,
|
activity,
|
||||||
intent,
|
intent,
|
||||||
assistStructure)
|
assistStructure)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
launch(activity, fileName, keyFile)
|
launch(activity, databaseFile, keyFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,13 +200,11 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
var error = false
|
var error = false
|
||||||
if (keyFileCheckBox != null
|
if (keyFileCheckBox != null
|
||||||
&& keyFileCheckBox!!.isChecked) {
|
&& keyFileCheckBox!!.isChecked) {
|
||||||
val keyFile = UriUtil.parseUriFile(keyFileView?.text?.toString())
|
|
||||||
mKeyFile = keyFile
|
|
||||||
|
|
||||||
// Verify that a keyfile is set
|
UriUtil.parse(keyFileView?.text?.toString())?.let { uri ->
|
||||||
if (keyFile == null || keyFile.toString().isEmpty()) {
|
mKeyFile = uri
|
||||||
|
} ?: run {
|
||||||
error = true
|
error = true
|
||||||
// TODO better keyfile check
|
|
||||||
keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile)
|
keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +248,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
||||||
) { uri ->
|
) { uri ->
|
||||||
UriUtil.parseUriFile(uri)?.let { pathUri ->
|
uri?.let { pathUri ->
|
||||||
keyFileCheckBox?.isChecked = true
|
keyFileCheckBox?.isChecked = true
|
||||||
keyFileView?.text = pathUri.toString()
|
keyFileView?.text = pathUri.toString()
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class OpenFileHelper {
|
|||||||
this.fragment = context
|
this.fragment = context
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class OpenFileOnClickViewListener(private val dataUri: (() -> Uri)?) : View.OnClickListener {
|
inner class OpenFileOnClickViewListener(private val dataUri: (() -> Uri?)?) : View.OnClickListener {
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
try {
|
try {
|
||||||
@@ -97,7 +97,7 @@ class OpenFileHelper {
|
|||||||
activity?.startActivityForResult(i, GET_CONTENT)
|
activity?.startActivityForResult(i, GET_CONTENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOpenFileOnClickViewListener(dataUri: () -> Uri): OpenFileOnClickViewListener {
|
fun getOpenFileOnClickViewListener(dataUri: () -> Uri?): OpenFileOnClickViewListener {
|
||||||
return OpenFileOnClickViewListener(dataUri)
|
return OpenFileOnClickViewListener(dataUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ class OpenFileHelper {
|
|||||||
val filename = data?.dataString
|
val filename = data?.dataString
|
||||||
var keyUri: Uri? = null
|
var keyUri: Uri? = null
|
||||||
if (filename != null) {
|
if (filename != null) {
|
||||||
keyUri = UriUtil.parseUriFile(filename)
|
keyUri = UriUtil.parse(filename)
|
||||||
}
|
}
|
||||||
keyFileCallback?.invoke(keyUri)
|
keyFileCallback?.invoke(keyUri)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package com.kunzisoft.keepass.adapters
|
package com.kunzisoft.keepass.adapters
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
@@ -32,6 +31,7 @@ import android.widget.ViewSwitcher
|
|||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryEntity
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryEntity
|
||||||
import com.kunzisoft.keepass.utils.FileDatabaseInfo
|
import com.kunzisoft.keepass.utils.FileDatabaseInfo
|
||||||
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class FileDatabaseHistoryAdapter(private val context: Context)
|
class FileDatabaseHistoryAdapter(private val context: Context)
|
||||||
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
|
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
|
||||||
@@ -80,7 +80,7 @@ class FileDatabaseHistoryAdapter(private val context: Context)
|
|||||||
holder.fileAlias.text = fileDatabaseInfo.retrieveDatabaseAlias(fileHistoryEntity.databaseAlias)
|
holder.fileAlias.text = fileDatabaseInfo.retrieveDatabaseAlias(fileHistoryEntity.databaseAlias)
|
||||||
|
|
||||||
// File path
|
// File path
|
||||||
holder.filePath.text = Uri.decode(fileDatabaseInfo.fileUri.toString())
|
holder.filePath.text = UriUtil.decode(fileDatabaseInfo.fileUri?.toString())
|
||||||
|
|
||||||
holder.filePreciseInfoContainer.visibility = if (fileDatabaseInfo.found()) {
|
holder.filePreciseInfoContainer.visibility = if (fileDatabaseInfo.found()) {
|
||||||
// Modification
|
// Modification
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.app.database
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import com.kunzisoft.keepass.utils.SingletonHolderParameter
|
import com.kunzisoft.keepass.utils.SingletonHolderParameter
|
||||||
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class FileDatabaseHistoryAction(applicationContext: Context) {
|
class FileDatabaseHistoryAction(applicationContext: Context) {
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ class FileDatabaseHistoryAction(applicationContext: Context) {
|
|||||||
{
|
{
|
||||||
it?.let { fileHistoryEntity ->
|
it?.let { fileHistoryEntity ->
|
||||||
fileHistoryEntity.keyFileUri?.let { keyFileUri ->
|
fileHistoryEntity.keyFileUri?.let { keyFileUri ->
|
||||||
keyFileUriResultListener.invoke(Uri.parse(keyFileUri))
|
keyFileUriResultListener.invoke(UriUtil.parse(keyFileUri))
|
||||||
}
|
}
|
||||||
} ?: keyFileUriResultListener.invoke(null)
|
} ?: keyFileUriResultListener.invoke(null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import com.kunzisoft.keepass.settings.preference.IconPackListPreference
|
|||||||
import com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
import com.kunzisoft.keepass.settings.preference.InputNumberPreference
|
||||||
import com.kunzisoft.keepass.settings.preference.InputTextPreference
|
import com.kunzisoft.keepass.settings.preference.InputTextPreference
|
||||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
||||||
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
|
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
|
||||||
|
|
||||||
@@ -177,6 +178,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
|||||||
private fun startEnableService() {
|
private fun startEnableService() {
|
||||||
if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
|
if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
|
||||||
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
|
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
|
||||||
|
// TODO Autofill
|
||||||
intent.data = Uri.parse("package:com.example.android.autofill.service")
|
intent.data = Uri.parse("package:com.example.android.autofill.service")
|
||||||
Log.d(javaClass.name, "enableService(): intent=$intent")
|
Log.d(javaClass.name, "enableService(): intent=$intent")
|
||||||
startActivityForResult(intent, REQUEST_CODE_AUTOFILL)
|
startActivityForResult(intent, REQUEST_CODE_AUTOFILL)
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
|
||||||
*
|
|
||||||
* This file is part of KeePass DX.
|
|
||||||
*
|
|
||||||
* KeePass DX is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* KeePass DX is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package com.kunzisoft.keepass.utils;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class ClipDataCompat {
|
|
||||||
private static Method getClipDataFromIntent;
|
|
||||||
private static Method getDescription;
|
|
||||||
private static Method getItemCount;
|
|
||||||
private static Method getLabel;
|
|
||||||
private static Method getItemAt;
|
|
||||||
private static Method getUri;
|
|
||||||
|
|
||||||
private static boolean initSucceded;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
Class clipData = Class.forName("android.content.ClipData");
|
|
||||||
getDescription = clipData.getMethod("getDescription", (Class[])null);
|
|
||||||
getItemCount = clipData.getMethod("getItemCount", (Class[])null);
|
|
||||||
getItemAt = clipData.getMethod("getItemAt", new Class[]{int.class});
|
|
||||||
Class clipDescription = Class.forName("android.content.ClipDescription");
|
|
||||||
getLabel = clipDescription.getMethod("getLabel", (Class[])null);
|
|
||||||
|
|
||||||
Class clipDataItem = Class.forName("android.content.ClipData$Item");
|
|
||||||
getUri = clipDataItem.getMethod("getUri", (Class[])null);
|
|
||||||
|
|
||||||
getClipDataFromIntent = Intent.class.getMethod("getClipData", (Class[])null);
|
|
||||||
|
|
||||||
initSucceded = true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
initSucceded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Uri getUriFromIntent(Intent i, String key) {
|
|
||||||
if (initSucceded) {
|
|
||||||
try {
|
|
||||||
Object clip = getClipDataFromIntent.invoke(i);
|
|
||||||
|
|
||||||
if (clip != null) {
|
|
||||||
Object clipDescription = getDescription.invoke(clip);
|
|
||||||
CharSequence label = (CharSequence)getLabel.invoke(clipDescription);
|
|
||||||
if (label.equals(key)) {
|
|
||||||
int itemCount = (int) getItemCount.invoke(clip);
|
|
||||||
if (itemCount == 1) {
|
|
||||||
Object clipItem = getItemAt.invoke(clip,0);
|
|
||||||
if (clipItem != null) {
|
|
||||||
return (Uri)getUri.invoke(clipItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Fall through below to backup method if reflection fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i.getParcelableExtra(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,10 +21,12 @@ class FileDatabaseInfo : FileInfo {
|
|||||||
|
|
||||||
fun retrieveDatabaseTitle(titleCallback: (String)->Unit) {
|
fun retrieveDatabaseTitle(titleCallback: (String)->Unit) {
|
||||||
|
|
||||||
FileDatabaseHistoryAction.getInstance(context.applicationContext).getFileDatabaseHistory(fileUri) {
|
fileUri?.let { fileUri ->
|
||||||
fileDatabaseHistoryEntity ->
|
FileDatabaseHistoryAction.getInstance(context.applicationContext)
|
||||||
|
.getFileDatabaseHistory(fileUri) { fileDatabaseHistoryEntity ->
|
||||||
titleCallback.invoke(retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""))
|
titleCallback.invoke(retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
|
||||||
|
?: ""))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import java.util.*
|
|||||||
open class FileInfo : Serializable {
|
open class FileInfo : Serializable {
|
||||||
|
|
||||||
var context: Context
|
var context: Context
|
||||||
var fileUri: Uri
|
var fileUri: Uri?
|
||||||
var filePath: String? = null
|
var filePath: String? = null
|
||||||
var fileName: String? = ""
|
var fileName: String? = ""
|
||||||
var lastModification = Date()
|
var lastModification = Date()
|
||||||
@@ -45,17 +45,19 @@ open class FileInfo : Serializable {
|
|||||||
|
|
||||||
constructor(context: Context, filePath: String) {
|
constructor(context: Context, filePath: String) {
|
||||||
this.context = context
|
this.context = context
|
||||||
this.fileUri = Uri.parse(filePath)
|
this.fileUri = UriUtil.parse(filePath)
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init() {
|
fun init() {
|
||||||
this.filePath = fileUri.path
|
this.filePath = fileUri?.path
|
||||||
if (EXTERNAL_STORAGE_AUTHORITY == fileUri.authority) {
|
if (EXTERNAL_STORAGE_AUTHORITY == fileUri?.authority) {
|
||||||
DocumentFile.fromSingleUri(context, fileUri)?.let { file ->
|
fileUri?.let { fileUri ->
|
||||||
size = file.length()
|
DocumentFile.fromSingleUri(context, fileUri)?.let { file ->
|
||||||
fileName = file.name
|
size = file.length()
|
||||||
lastModification = Date(file.lastModified())
|
fileName = file.name
|
||||||
|
lastModification = Date(file.lastModified())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filePath?.let {
|
filePath?.let {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.content.ContentResolver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -131,81 +132,72 @@ object UriUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
fun verifyFileUri(fileUri: Uri?): Boolean {
|
||||||
fun verifyFilePath(fileName: String?, doActionIfFileExists: ((String) -> Unit)? = null) {
|
|
||||||
|
|
||||||
if (fileName != null && fileName.isNotEmpty()) {
|
if (fileUri == null || fileUri == Uri.EMPTY)
|
||||||
val fileUri = parseUriFile(fileName)
|
return false
|
||||||
verifyFileUri(fileUri, doActionIfFileExists)
|
|
||||||
} else {
|
val scheme = fileUri.scheme
|
||||||
throw FileNotFoundException("File name is empty")
|
return when {
|
||||||
|
scheme == null || scheme.isEmpty() -> {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
scheme.equals("file", ignoreCase = true) -> {
|
||||||
|
val filePath = fileUri.path
|
||||||
|
if (filePath == null || filePath.isEmpty())
|
||||||
|
false
|
||||||
|
else {
|
||||||
|
File(filePath).exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scheme.equals("content", ignoreCase = true) -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
fun parse(stringUri: String?): Uri? {
|
||||||
fun verifyFileUri(fileUri: Uri?, doActionIfFileExists: ((filePath: String) -> Unit)? = null) {
|
return if (stringUri?.isNotEmpty() == true) {
|
||||||
|
val uriParsed = Uri.parse(stringUri)
|
||||||
|
if (verifyFileUri(uriParsed))
|
||||||
|
uriParsed
|
||||||
|
else
|
||||||
|
null
|
||||||
|
} else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO errorString
|
fun decode(uri: String?): String {
|
||||||
@IntegerRes
|
return Uri.decode(uri) ?: ""
|
||||||
var errorStringId: Int? = null
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
val scheme = fileUri?.scheme
|
fun getUriFromIntent(intent: Intent, key: String): Uri? {
|
||||||
|
try {
|
||||||
if (scheme == null || scheme.isEmpty()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
throw FileNotFoundException("Uri scheme is empty")
|
val clipData = intent.clipData
|
||||||
// TODO error errorStringId = R.string.error_can_not_handle_uri
|
if (clipData != null) {
|
||||||
}
|
if (clipData.description.label == key) {
|
||||||
else {
|
if (clipData.itemCount == 1) {
|
||||||
when {
|
val clipItem = clipData.getItemAt(0)
|
||||||
scheme.equals("file", ignoreCase = true) -> {
|
if (clipItem != null) {
|
||||||
val filePath = fileUri.path
|
return clipItem.uri
|
||||||
|
}
|
||||||
if (filePath == null || filePath.isEmpty())
|
|
||||||
throw FileNotFoundException("Unable to retrieve file path")
|
|
||||||
else {
|
|
||||||
if (!File(filePath).exists()) {
|
|
||||||
throw FileNotFoundException("File do not exists")
|
|
||||||
// TODO error errorStringId = R.string.file_not_found
|
|
||||||
} else {
|
|
||||||
doActionIfFileExists?.invoke(filePath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scheme.equals("content", ignoreCase = true) -> {
|
|
||||||
doActionIfFileExists?.invoke(fileUri.toString())
|
|
||||||
// TODO verify
|
|
||||||
}
|
|
||||||
else -> throw FileNotFoundException("Uri scheme not recognized")
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return intent.getParcelableExtra(key)
|
||||||
}
|
}
|
||||||
}
|
return null
|
||||||
|
|
||||||
fun parseUriFile(text: String?): Uri? {
|
|
||||||
if (text == null || text.isEmpty()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return parseUriFile(Uri.parse(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseUriFile(uri: Uri?): Uri? {
|
|
||||||
if (uri == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add file scheme if URI scheme is null
|
|
||||||
var currentUri = uri
|
|
||||||
if (currentUri.scheme == null || currentUri.scheme!!.isEmpty()) {
|
|
||||||
currentUri = currentUri.buildUpon().scheme("file").authority("").build()
|
|
||||||
}
|
|
||||||
return currentUri
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ActivityNotFoundException::class)
|
@Throws(ActivityNotFoundException::class)
|
||||||
fun gotoUrl(context: Context, url: String?) {
|
fun gotoUrl(context: Context, url: String?) {
|
||||||
try {
|
try {
|
||||||
if (url != null && url.isNotEmpty()) {
|
if (url != null && url.isNotEmpty()) {
|
||||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
context.startActivity(Intent(Intent.ACTION_VIEW, parse(url)))
|
||||||
}
|
}
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Toast.makeText(context, R.string.no_url_handler, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, R.string.no_url_handler, Toast.LENGTH_LONG).show()
|
||||||
|
|||||||
Reference in New Issue
Block a user