From f2666316e13bec89948fe8003e06148f643a174f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 12 Sep 2019 17:07:42 +0200 Subject: [PATCH] Better UriUtil methods --- .../keepass/activities/EntryActivity.kt | 3 +- .../activities/FileDatabaseSelectActivity.kt | 47 ++++---- .../keepass/activities/PasswordActivity.kt | 68 +++++------ .../dialogs/AssignMasterKeyDialogFragment.kt | 10 +- .../activities/helpers/OpenFileHelper.kt | 6 +- .../adapters/FileDatabaseHistoryAdapter.kt | 4 +- .../app/database/FileDatabaseHistoryAction.kt | 3 +- .../settings/NestedSettingsFragment.kt | 2 + .../keepass/utils/ClipDataCompat.java | 84 -------------- .../keepass/utils/FileDatabaseInfo.kt | 10 +- .../com/kunzisoft/keepass/utils/FileInfo.kt | 18 +-- .../com/kunzisoft/keepass/utils/UriUtil.kt | 108 ++++++++---------- 12 files changed, 135 insertions(+), 228 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/ClipDataCompat.java diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index f99eeea84..9e06328bf 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -21,7 +21,6 @@ package com.kunzisoft.keepass.activities import android.app.Activity import android.content.Intent import android.graphics.Color -import android.net.Uri import android.os.Bundle import android.os.Handler import com.google.android.material.appbar.CollapsingToolbarLayout @@ -348,7 +347,7 @@ class EntryActivity : LockingHideActivity() { { // Open Keepass doc to create field references startActivity(Intent(Intent.ACTION_VIEW, - Uri.parse(getString(R.string.field_references_url)))) + UriUtil.parse(getString(R.string.field_references_url)))) }) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 4510ae652..f7666bb9d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -125,7 +125,13 @@ class FileDatabaseSelectActivity : StylishActivity(), if (fileName.isEmpty()) 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 @@ -147,10 +153,9 @@ class FileDatabaseSelectActivity : StylishActivity(), mOpenFileHelper = OpenFileHelper(this) browseButtonView = findViewById(R.id.browse_button) browseButtonView?.setOnClickListener(mOpenFileHelper!!.getOpenFileOnClickViewListener { - Uri.parse("file://" + openFileNameView!!.text.toString()) + UriUtil.parse(openFileNameView?.text?.toString()) }) - // History list val fileDatabaseHistoryRecyclerView = findViewById(R.id.file_list) fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false) @@ -159,9 +164,11 @@ class FileDatabaseSelectActivity : StylishActivity(), // Construct adapter with listeners mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this) mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen -> - launchPasswordActivity( - fileDatabaseHistoryEntityToOpen.databaseUri, - fileDatabaseHistoryEntityToOpen.keyFileUri) + UriUtil.parse(fileDatabaseHistoryEntityToOpen.databaseUri)?.let { databaseFileUri -> + launchPasswordActivity( + databaseFileUri, + UriUtil.parse(fileDatabaseHistoryEntityToOpen.keyFileUri)) + } updateFileListVisibility() } mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete -> @@ -186,14 +193,12 @@ class FileDatabaseSelectActivity : StylishActivity(), && savedInstanceState.containsKey(EXTRA_STAY) && savedInstanceState.getBoolean(EXTRA_STAY, false))) { val prefs = PreferenceManager.getDefaultSharedPreferences(this) - val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "") + val databasePath = prefs.getString(PasswordActivity.KEY_DEFAULT_DATABASE_PATH, "") - try { - UriUtil.verifyFilePath(fileName) { path -> - launchPasswordActivityWithPath(path) - } - } catch (e: FileNotFoundException) { - Log.e(TAG, "Unable to launch Password Activity", e) + UriUtil.parse(databasePath)?.let { databaseFileUri -> + launchPasswordActivityWithPath(databaseFileUri) + } ?: run { + Log.e(TAG, "Unable to launch Password Activity") } } @@ -229,12 +234,12 @@ class FileDatabaseSelectActivity : StylishActivity(), Log.e(TAG, error, e) } - private fun launchPasswordActivity(fileName: String, keyFile: String?) { + private fun launchPasswordActivity(databaseUri: Uri, keyFile: Uri?) { EntrySelectionHelper.doEntrySelectionAction(intent, { try { PasswordActivity.launch(this@FileDatabaseSelectActivity, - fileName, keyFile) + databaseUri, keyFile) } catch (e: FileNotFoundException) { fileNoFoundAction(e) } @@ -242,7 +247,7 @@ class FileDatabaseSelectActivity : StylishActivity(), { try { PasswordActivity.launchForKeyboardResult(this@FileDatabaseSelectActivity, - fileName, keyFile) + databaseUri, keyFile) finish() } catch (e: FileNotFoundException) { fileNoFoundAction(e) @@ -252,7 +257,7 @@ class FileDatabaseSelectActivity : StylishActivity(), if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { try { PasswordActivity.launchForAutofillResult(this@FileDatabaseSelectActivity, - fileName, keyFile, + databaseUri, keyFile, assistStructure) } catch (e: FileNotFoundException) { fileNoFoundAction(e) @@ -262,8 +267,8 @@ class FileDatabaseSelectActivity : StylishActivity(), }) } - private fun launchPasswordActivityWithPath(path: String) { - launchPasswordActivity(path, "") + private fun launchPasswordActivityWithPath(databaseUri: Uri) { + launchPasswordActivity(databaseUri, null) // Delete flickering for kitkat <= if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) overridePendingTransition(0, 0) @@ -323,7 +328,7 @@ class FileDatabaseSelectActivity : StylishActivity(), keyFileChecked: Boolean, keyFile: Uri?) { try { - UriUtil.parseUriFile(mDatabaseFileUri)?.let { databaseUri -> + mDatabaseFileUri?.let { databaseUri -> // Create the new database ProgressDialogThread(this@FileDatabaseSelectActivity, @@ -388,7 +393,7 @@ class FileDatabaseSelectActivity : StylishActivity(), ) { uri -> if (uri != null) { if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) { - launchPasswordActivityWithPath(uri.toString()) + launchPasswordActivityWithPath(uri) } else { fileSelectExpandableLayout?.expand(false) openFileNameView?.setText(uri.toString()) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index f05321dd9..1df82893d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -46,7 +46,6 @@ import androidx.biometric.BiometricManager import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.FingerPrintExplanationDialog 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.OpenFileHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper @@ -70,7 +69,6 @@ import com.kunzisoft.keepass.view.AdvancedUnlockInfoView import com.kunzisoft.keepass.view.asError import kotlinx.android.synthetic.main.activity_password.* import java.io.FileNotFoundException -import java.lang.Exception import java.lang.ref.WeakReference class PasswordActivity : StylishActivity() { @@ -196,21 +194,16 @@ class PasswordActivity : StylishActivity() { val databaseUriRetrieve = intent.data // Stop activity here if we can't verify database URI - try { - UriUtil.verifyFileUri(databaseUriRetrieve) - } catch (e : Exception) { - Log.e(TAG, "File URI not validate", e) - Toast.makeText(this@PasswordActivity, e.message, Toast.LENGTH_LONG).show() + if (!UriUtil.verifyFileUri(databaseUriRetrieve)) { + Log.e(TAG, "File URI not validate") finish() - return } - databaseUri = databaseUriRetrieve - keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE) + keyFileUri = UriUtil.getUriFromIntent(intent, KEY_KEYFILE) } else { - databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME)) - keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE)) + databaseUri = intent.getParcelableExtra(KEY_FILENAME) + keyFileUri = intent.getParcelableExtra(KEY_KEYFILE) } // Post init uri with KeyFile if needed @@ -245,14 +238,16 @@ class PasswordActivity : StylishActivity() { // Define listeners for default database checkbox and validate button checkboxDefaultDatabaseView?.setOnCheckedChangeListener { _, isChecked -> - var newDefaultFileName = "" + var newDefaultFileName: Uri? = null if (isChecked) { - newDefaultFileName = databaseFileUri?.toString() ?: newDefaultFileName + newDefaultFileName = databaseFileUri ?: newDefaultFileName } - prefs?.edit()?.apply { - putString(KEY_DEFAULT_FILENAME, newDefaultFileName) - apply() + newDefaultFileName?.let { + prefs?.edit()?.apply { + putString(KEY_DEFAULT_DATABASE_PATH, newDefaultFileName.toString()) + apply() + } } val backupManager = BackupManager(this@PasswordActivity) @@ -261,10 +256,10 @@ class PasswordActivity : StylishActivity() { confirmButtonView?.setOnClickListener { verifyCheckboxesAndLoadDatabase() } // Retrieve settings for default database - val defaultFilename = prefs?.getString(KEY_DEFAULT_FILENAME, "") + val defaultFilename = prefs?.getString(KEY_DEFAULT_DATABASE_PATH, "") if (databaseFileUri != null && databaseFileUri.path != null && databaseFileUri.path!!.isNotEmpty() - && databaseFileUri == UriUtil.parseUriFile(defaultFilename)) { + && databaseFileUri == UriUtil.parse(defaultFilename)) { checkboxDefaultDatabaseView?.isChecked = true } @@ -388,7 +383,7 @@ class PasswordActivity : StylishActivity() { private fun verifyCheckboxesAndLoadDatabase(cipherDatabaseEntity: CipherDatabaseEntity? = null) { 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) } @@ -401,7 +396,7 @@ class PasswordActivity : StylishActivity() { } 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 loadDatabase(password, keyFileUri) } @@ -632,7 +627,7 @@ class PasswordActivity : StylishActivity() { 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_KEYFILE = "keyFile" @@ -641,10 +636,10 @@ class PasswordActivity : StylishActivity() { private const val KEY_PASSWORD = "password" 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) { val intent = Intent(activity, PasswordActivity::class.java) - intent.putExtra(KEY_FILENAME, fileName) + intent.putExtra(KEY_FILENAME, databaseFile) if (keyFile != null) intent.putExtra(KEY_KEYFILE, keyFile) intentBuildLauncher.invoke(intent) @@ -659,10 +654,9 @@ class PasswordActivity : StylishActivity() { @Throws(FileNotFoundException::class) fun launch( activity: Activity, - fileName: String, - keyFile: String?) { - UriUtil.verifyFilePath(fileName) - buildAndLaunchIntent(activity, fileName, keyFile) { intent -> + databaseFile: Uri, + keyFile: Uri?) { + buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> activity.startActivity(intent) } } @@ -676,11 +670,9 @@ class PasswordActivity : StylishActivity() { @Throws(FileNotFoundException::class) fun launchForKeyboardResult( activity: Activity, - fileName: String, - keyFile: String?) { - UriUtil.verifyFilePath(fileName) - - buildAndLaunchIntent(activity, fileName, keyFile) { intent -> + databaseFile: Uri, + keyFile: Uri?) { + buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> EntrySelectionHelper.startActivityForEntrySelection(activity, intent) } } @@ -695,20 +687,18 @@ class PasswordActivity : StylishActivity() { @Throws(FileNotFoundException::class) fun launchForAutofillResult( activity: Activity, - fileName: String, - keyFile: String?, + databaseFile: Uri, + keyFile: Uri?, assistStructure: AssistStructure?) { - UriUtil.verifyFilePath(fileName) - if (assistStructure != null) { - buildAndLaunchIntent(activity, fileName, keyFile) { intent -> + buildAndLaunchIntent(activity, databaseFile, keyFile) { intent -> AutofillHelper.startActivityForAutofillResult( activity, intent, assistStructure) } } else { - launch(activity, fileName, keyFile) + launch(activity, databaseFile, keyFile) } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index dfbc8296d..a71a8f09b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -200,13 +200,11 @@ class AssignMasterKeyDialogFragment : DialogFragment() { var error = false if (keyFileCheckBox != null && keyFileCheckBox!!.isChecked) { - val keyFile = UriUtil.parseUriFile(keyFileView?.text?.toString()) - mKeyFile = keyFile - // Verify that a keyfile is set - if (keyFile == null || keyFile.toString().isEmpty()) { + UriUtil.parse(keyFileView?.text?.toString())?.let { uri -> + mKeyFile = uri + } ?: run { error = true - // TODO better keyfile check keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile) } } @@ -250,7 +248,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() { mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data ) { uri -> - UriUtil.parseUriFile(uri)?.let { pathUri -> + uri?.let { pathUri -> keyFileCheckBox?.isChecked = true keyFileView?.text = pathUri.toString() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt index 64798ab13..52ebf4bb5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/OpenFileHelper.kt @@ -51,7 +51,7 @@ class OpenFileHelper { 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) { try { @@ -97,7 +97,7 @@ class OpenFileHelper { activity?.startActivityForResult(i, GET_CONTENT) } - fun getOpenFileOnClickViewListener(dataUri: () -> Uri): OpenFileOnClickViewListener { + fun getOpenFileOnClickViewListener(dataUri: () -> Uri?): OpenFileOnClickViewListener { return OpenFileOnClickViewListener(dataUri) } @@ -181,7 +181,7 @@ class OpenFileHelper { val filename = data?.dataString var keyUri: Uri? = null if (filename != null) { - keyUri = UriUtil.parseUriFile(filename) + keyUri = UriUtil.parse(filename) } keyFileCallback?.invoke(keyUri) } diff --git a/app/src/main/java/com/kunzisoft/keepass/adapters/FileDatabaseHistoryAdapter.kt b/app/src/main/java/com/kunzisoft/keepass/adapters/FileDatabaseHistoryAdapter.kt index bb42783fa..3e2805914 100644 --- a/app/src/main/java/com/kunzisoft/keepass/adapters/FileDatabaseHistoryAdapter.kt +++ b/app/src/main/java/com/kunzisoft/keepass/adapters/FileDatabaseHistoryAdapter.kt @@ -20,7 +20,6 @@ package com.kunzisoft.keepass.adapters import android.content.Context -import android.net.Uri import androidx.annotation.ColorInt import androidx.recyclerview.widget.RecyclerView import android.util.TypedValue @@ -32,6 +31,7 @@ import android.widget.ViewSwitcher import com.kunzisoft.keepass.R import com.kunzisoft.keepass.app.database.FileDatabaseHistoryEntity import com.kunzisoft.keepass.utils.FileDatabaseInfo +import com.kunzisoft.keepass.utils.UriUtil class FileDatabaseHistoryAdapter(private val context: Context) : RecyclerView.Adapter() { @@ -80,7 +80,7 @@ class FileDatabaseHistoryAdapter(private val context: Context) holder.fileAlias.text = fileDatabaseInfo.retrieveDatabaseAlias(fileHistoryEntity.databaseAlias) // File path - holder.filePath.text = Uri.decode(fileDatabaseInfo.fileUri.toString()) + holder.filePath.text = UriUtil.decode(fileDatabaseInfo.fileUri?.toString()) holder.filePreciseInfoContainer.visibility = if (fileDatabaseInfo.found()) { // Modification diff --git a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt index c31711108..6638830d6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/database/FileDatabaseHistoryAction.kt @@ -22,6 +22,7 @@ package com.kunzisoft.keepass.app.database import android.content.Context import android.net.Uri import com.kunzisoft.keepass.utils.SingletonHolderParameter +import com.kunzisoft.keepass.utils.UriUtil class FileDatabaseHistoryAction(applicationContext: Context) { @@ -51,7 +52,7 @@ class FileDatabaseHistoryAction(applicationContext: Context) { { it?.let { fileHistoryEntity -> fileHistoryEntity.keyFileUri?.let { keyFileUri -> - keyFileUriResultListener.invoke(Uri.parse(keyFileUri)) + keyFileUriResultListener.invoke(UriUtil.parse(keyFileUri)) } } ?: keyFileUriResultListener.invoke(null) } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt index 2e3588cdb..f8836ccd3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedSettingsFragment.kt @@ -53,6 +53,7 @@ import com.kunzisoft.keepass.settings.preference.IconPackListPreference import com.kunzisoft.keepass.settings.preference.InputNumberPreference import com.kunzisoft.keepass.settings.preference.InputTextPreference import com.kunzisoft.keepass.settings.preferencedialogfragment.* +import com.kunzisoft.keepass.utils.UriUtil class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener { @@ -177,6 +178,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen private fun startEnableService() { if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) { val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE) + // TODO Autofill intent.data = Uri.parse("package:com.example.android.autofill.service") Log.d(javaClass.name, "enableService(): intent=$intent") startActivityForResult(intent, REQUEST_CODE_AUTOFILL) diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/ClipDataCompat.java b/app/src/main/java/com/kunzisoft/keepass/utils/ClipDataCompat.java deleted file mode 100644 index 8a61c38ec..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/utils/ClipDataCompat.java +++ /dev/null @@ -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 . - * - */ -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); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/FileDatabaseInfo.kt b/app/src/main/java/com/kunzisoft/keepass/utils/FileDatabaseInfo.kt index 87c5ce95e..4df403ac0 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/FileDatabaseInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/FileDatabaseInfo.kt @@ -21,10 +21,12 @@ class FileDatabaseInfo : FileInfo { fun retrieveDatabaseTitle(titleCallback: (String)->Unit) { - FileDatabaseHistoryAction.getInstance(context.applicationContext).getFileDatabaseHistory(fileUri) { - fileDatabaseHistoryEntity -> - - titleCallback.invoke(retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: "")) + fileUri?.let { fileUri -> + FileDatabaseHistoryAction.getInstance(context.applicationContext) + .getFileDatabaseHistory(fileUri) { fileDatabaseHistoryEntity -> + titleCallback.invoke(retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias + ?: "")) + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt b/app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt index 004cc5808..4117539f8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt @@ -31,7 +31,7 @@ import java.util.* open class FileInfo : Serializable { var context: Context - var fileUri: Uri + var fileUri: Uri? var filePath: String? = null var fileName: String? = "" var lastModification = Date() @@ -45,17 +45,19 @@ open class FileInfo : Serializable { constructor(context: Context, filePath: String) { this.context = context - this.fileUri = Uri.parse(filePath) + this.fileUri = UriUtil.parse(filePath) init() } fun init() { - this.filePath = fileUri.path - if (EXTERNAL_STORAGE_AUTHORITY == fileUri.authority) { - DocumentFile.fromSingleUri(context, fileUri)?.let { file -> - size = file.length() - fileName = file.name - lastModification = Date(file.lastModified()) + this.filePath = fileUri?.path + if (EXTERNAL_STORAGE_AUTHORITY == fileUri?.authority) { + fileUri?.let { fileUri -> + DocumentFile.fromSingleUri(context, fileUri)?.let { file -> + size = file.length() + fileName = file.name + lastModification = Date(file.lastModified()) + } } } else { filePath?.let { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt index 2fbdadce2..92f0dba58 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt @@ -24,6 +24,7 @@ import android.content.ContentResolver import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Build import android.widget.Toast import com.kunzisoft.keepass.R import java.io.File @@ -131,81 +132,72 @@ object UriUtil { } } - @Throws(FileNotFoundException::class) - fun verifyFilePath(fileName: String?, doActionIfFileExists: ((String) -> Unit)? = null) { + fun verifyFileUri(fileUri: Uri?): Boolean { - if (fileName != null && fileName.isNotEmpty()) { - val fileUri = parseUriFile(fileName) - verifyFileUri(fileUri, doActionIfFileExists) - } else { - throw FileNotFoundException("File name is empty") + if (fileUri == null || fileUri == Uri.EMPTY) + return false + + val scheme = fileUri.scheme + 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 verifyFileUri(fileUri: Uri?, doActionIfFileExists: ((filePath: String) -> Unit)? = null) { + fun parse(stringUri: String?): Uri? { + return if (stringUri?.isNotEmpty() == true) { + val uriParsed = Uri.parse(stringUri) + if (verifyFileUri(uriParsed)) + uriParsed + else + null + } else + null + } - /* TODO errorString - @IntegerRes - var errorStringId: Int? = null - */ + fun decode(uri: String?): String { + return Uri.decode(uri) ?: "" + } - val scheme = fileUri?.scheme - - if (scheme == null || scheme.isEmpty()) { - throw FileNotFoundException("Uri scheme is empty") - // TODO error errorStringId = R.string.error_can_not_handle_uri - } - else { - when { - scheme.equals("file", ignoreCase = true) -> { - val filePath = fileUri.path - - 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) + fun getUriFromIntent(intent: Intent, key: String): Uri? { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + val clipData = intent.clipData + if (clipData != null) { + if (clipData.description.label == key) { + if (clipData.itemCount == 1) { + val clipItem = clipData.getItemAt(0) + if (clipItem != null) { + return clipItem.uri + } } } } - 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) } - } - - 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 + return null } @Throws(ActivityNotFoundException::class) fun gotoUrl(context: Context, url: String?) { try { 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) { Toast.makeText(context, R.string.no_url_handler, Toast.LENGTH_LONG).show()