Better UriUtil methods

This commit is contained in:
J-Jamet
2019-09-12 17:07:42 +02:00
parent e4ac0ee258
commit f2666316e1
12 changed files with 135 additions and 228 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
?: ""))
}
} }
} }

View File

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

View File

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