fix: better static utils implementation

This commit is contained in:
J-Jamet 2023-05-08 20:30:16 +02:00
parent c8868f31e6
commit dfb418c12a
39 changed files with 226 additions and 310 deletions

View file

@ -2,10 +2,10 @@ package com.kunzisoft.keepass.tests.stream
import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import com.kunzisoft.keepass.utils.readAllBytes
import com.kunzisoft.keepass.database.element.binary.BinaryCache
import com.kunzisoft.keepass.database.element.binary.BinaryFile
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
import com.kunzisoft.keepass.utils.readAllBytes
import junit.framework.TestCase.assertEquals
import org.junit.Test
import java.io.DataInputStream
@ -19,7 +19,7 @@ class BinaryDataTest {
InstrumentationRegistry.getInstrumentation().context
}
private val cacheDirectory = UriUtil.getBinaryDir(InstrumentationRegistry.getInstrumentation().targetContext)
private val cacheDirectory = InstrumentationRegistry.getInstrumentation().targetContext.getBinaryDir()
private val fileA = File(cacheDirectory, TEST_FILE_CACHE_A)
private val fileB = File(cacheDirectory, TEST_FILE_CACHE_B)
private val fileC = File(cacheDirectory, TEST_FILE_CACHE_C)

View file

@ -30,7 +30,7 @@ import androidx.core.text.HtmlCompat
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import org.joda.time.DateTime
class AboutActivity : StylishActivity() {
@ -46,7 +46,7 @@ class AboutActivity : StylishActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
val appName = if (UriUtil.contributingUser(this))
val appName = if (this.isContributingUser())
getString(R.string.app_name) + " " + getString(R.string.app_name_part3)
else
getString(R.string.app_name)

View file

@ -36,11 +36,11 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat
import androidx.core.graphics.ColorUtils
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.progressindicator.LinearProgressIndicator
@ -67,7 +67,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.utils.UuidUtil
import com.kunzisoft.keepass.view.changeControlColor
import com.kunzisoft.keepass.view.changeTitleColor
@ -473,7 +473,7 @@ class EntryActivity : DatabaseLockActivity() {
}
R.id.menu_goto_url -> {
mUrl?.let { url ->
UriUtil.gotoUrl(this, url)
this.openUrl(url)
}
return true
}

View file

@ -76,7 +76,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import com.kunzisoft.keepass.view.*
import com.kunzisoft.keepass.viewmodels.ColorPickerViewModel
import com.kunzisoft.keepass.viewmodels.EntryEditViewModel
@ -185,7 +185,7 @@ class EntryEditActivity : DatabaseLockActivity(),
mExternalFileHelper = ExternalFileHelper(this)
mExternalFileHelper?.buildOpenDocument { uri ->
uri?.let { attachmentToUploadUri ->
UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile ->
attachmentToUploadUri.getDocumentFile(this)?.also { documentFile ->
documentFile.name?.let { fileName ->
if (documentFile.length() > MAX_WARNING_BINARY_FILE) {
FileTooBigDialogFragment.build(attachmentToUploadUri, fileName)

View file

@ -54,8 +54,8 @@ import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.autofill.AutofillComponent
import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo
@ -65,7 +65,12 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.*
import com.kunzisoft.keepass.utils.DexUtil
import com.kunzisoft.keepass.utils.MagikeyboardUtil
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriHelper.parseUri
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import com.kunzisoft.keepass.viewmodels.DatabaseFilesViewModel
@ -179,7 +184,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
&& savedInstanceState.getBoolean(EXTRA_STAY, false))) {
val databasePath = PreferencesUtil.getDefaultDatabasePath(this)
UriUtil.parse(databasePath)?.let { databaseFileUri ->
databasePath?.parseUri()?.let { databaseFileUri ->
launchPasswordActivityWithPath(databaseFileUri)
} ?: run {
Log.i(TAG, "No default database to prepare")
@ -326,7 +331,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
super.onResume()
// Define special title
specialTitle?.isVisible = UriUtil.contributingUser(this)
specialTitle?.isVisible = this.isContributingUser()
// Show open and create button or special mode
when (mSpecialMode) {
@ -426,7 +431,7 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> UriUtil.gotoUrl(this, R.string.file_manager_explanation_url)
android.R.id.home -> this.openUrl(R.string.file_manager_explanation_url)
}
MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
return super.onOptionsItemSelected(item)

View file

@ -69,7 +69,6 @@ import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.model.GroupInfo
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.model.RegisterInfo
import com.kunzisoft.keepass.model.SearchInfo
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
@ -81,7 +80,7 @@ import com.kunzisoft.keepass.settings.SettingsActivity
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.view.*
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
import com.kunzisoft.keepass.viewmodels.GroupViewModel
@ -302,7 +301,7 @@ class GroupActivity : DatabaseLockActivity(),
lockAndExit()
}
R.id.menu_contribute -> {
UriUtil.gotoUrl(this@GroupActivity, R.string.contribution_url)
this@GroupActivity.openUrl(R.string.contribution_url)
}
R.id.menu_about -> {
startActivity(Intent(this@GroupActivity, AboutActivity::class.java))

View file

@ -46,7 +46,8 @@ import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.BinaryDatabaseManager
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.view.updateLockPaddingLeft
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
@ -243,7 +244,7 @@ class IconPickerActivity : DatabaseLockActivity() {
}
}
R.id.menu_external_icon -> {
UriUtil.gotoUrl(this, R.string.external_icon_url)
this.openUrl(R.string.external_icon_url)
}
}
@ -257,7 +258,7 @@ class IconPickerActivity : DatabaseLockActivity() {
// on Progress with thread
val asyncResult: Deferred<IconPickerViewModel.IconCustomState?> = async {
val iconCustomState = IconPickerViewModel.IconCustomState(null, true, R.string.error_upload_file)
UriUtil.getFileData(this@IconPickerActivity, iconToUploadUri)?.also { documentFile ->
iconToUploadUri?.getDocumentFile(this@IconPickerActivity)?.also { documentFile ->
if (documentFile.length() > MAX_ICON_SIZE) {
iconCustomState.errorStringId = R.string.error_file_to_big
} else {

View file

@ -73,6 +73,7 @@ import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getUri
import com.kunzisoft.keepass.view.MainCredentialView
import com.kunzisoft.keepass.view.asError
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
@ -344,7 +345,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
if (action == VIEW_INTENT) {
fillCredentials(
intent.data,
UriUtil.getUriFromIntent(intent, KEY_KEYFILE),
intent.getUri(KEY_KEYFILE),
HardwareKey.getHardwareKeyFromString(intent.getStringExtra(KEY_HARDWARE_KEY))
)
} else {
@ -357,7 +358,7 @@ class MainCredentialActivity : DatabaseModeActivity(), AdvancedUnlockFragment.Bu
try {
intent?.removeExtra(KEY_KEYFILE)
intent?.removeExtra(KEY_HARDWARE_KEY)
} catch (e: Exception) {}
} catch (_: Exception) {}
mDatabaseFileUri?.let {
mDatabaseFileViewModel.checkIfIsDefaultDatabase(it)
}

View file

@ -21,12 +21,12 @@ package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.appcompat.app.AlertDialog
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openUrl
class FileManagerDialogFragment : DialogFragment() {
@ -42,7 +42,7 @@ class FileManagerDialogFragment : DialogFragment() {
textDescription.text = getString(R.string.file_manager_install_description)
root.findViewById<Button>(R.id.file_manager_button).setOnClickListener {
UriUtil.gotoUrl(requireContext(), R.string.file_manager_explanation_url)
context?.openUrl(R.string.file_manager_explanation_url)
dismiss()
}

View file

@ -28,7 +28,7 @@ import androidx.appcompat.app.AlertDialog
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import com.kunzisoft.keepass.view.MainCredentialView
class MainCredentialDialogFragment : DatabaseDialogFragment() {
@ -74,7 +74,7 @@ class MainCredentialDialogFragment : DatabaseDialogFragment() {
mainCredentialView = root.findViewById(R.id.main_credential_view)
databaseUri?.let {
root.findViewById<TextView>(R.id.title_database)?.text =
UriUtil.getFileData(requireContext(), it)?.name
it.getDocumentFile(requireContext())?.name
}
builder.setView(root)
// Add action buttons

View file

@ -28,7 +28,7 @@ import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openUrl
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
@ -45,7 +45,7 @@ class ProFeatureDialogFragment : DialogFragment() {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_ad_free), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_buy_pro), FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.download) { _, _ ->
UriUtil.gotoUrl(activity,
activity.openUrl(
activity.getString(R.string.play_store_url,
activity.getString(R.string.keepro_app_id))
)
@ -54,7 +54,7 @@ class ProFeatureDialogFragment : DialogFragment() {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_feature_generosity), FROM_HTML_MODE_LEGACY)).append("\n\n")
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_donation), FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ ->
UriUtil.gotoUrl(activity, R.string.contribution_url)
activity.openUrl(R.string.contribution_url)
}
}
builder.setMessage(stringBuilder)

View file

@ -35,11 +35,12 @@ import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.hardware.HardwareKeyActivity
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.password.PasswordEntropy
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.view.HardwareKeySelectionView
import com.kunzisoft.keepass.view.KeyFileSelectionView
import com.kunzisoft.keepass.view.PassKeyView
@ -136,7 +137,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
.setNegativeButton(android.R.string.cancel) { _, _ -> }
rootView.findViewById<View>(R.id.credentials_information)?.setOnClickListener {
UriUtil.gotoUrl(activity, R.string.credentials_explanation_url)
activity.openUrl(R.string.credentials_explanation_url)
}
passwordCheckBox = rootView.findViewById(R.id.password_checkbox)
@ -154,7 +155,7 @@ class SetMainCredentialDialogFragment : DatabaseDialogFragment() {
mExternalFileHelper = ExternalFileHelper(this)
mExternalFileHelper?.buildOpenDocument { uri ->
uri?.let { pathUri ->
UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile ->
pathUri.getDocumentFile(requireContext())?.length()?.let { lengthFile ->
keyFileSelectionView.error = null
keyFileCheckBox.isChecked = true
keyFileSelectionView.uri = pathUri

View file

@ -44,7 +44,8 @@ import com.kunzisoft.keepass.otp.OtpElement.Companion.MIN_TOTP_PERIOD
import com.kunzisoft.keepass.otp.OtpTokenType
import com.kunzisoft.keepass.otp.OtpType
import com.kunzisoft.keepass.otp.TokenCalculator
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import java.util.*
class SetOTPDialogFragment : DatabaseDialogFragment() {
@ -205,7 +206,7 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
}
// Proprietary only on full version
mTotpTokenTypeArray = OtpTokenType.getTotpTokenTypeValues(
UriUtil.contributingUser(activity)
activity.isContributingUser()
)
totpTokenTypeAdapter = ArrayAdapter(activity,
android.R.layout.simple_spinner_item, mTotpTokenTypeArray!!).apply {
@ -241,7 +242,7 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
}
root?.findViewById<View>(R.id.otp_information)?.setOnClickListener {
UriUtil.gotoUrl(activity, R.string.otp_explanation_url)
activity.openUrl(R.string.otp_explanation_url)
}
return builder.create()

View file

@ -26,7 +26,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openUrl
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
@ -40,7 +40,7 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
val stringBuilder = SpannableStringBuilder()
/*
if (UriUtil.contributingUser(activity)) {
if (activity.isContributingUser()) {
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_thanks), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(HtmlCompat.fromHtml(getString(R.string.html_rose), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_work_hard), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n")
@ -52,7 +52,7 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_contibute), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
builder.setPositiveButton(R.string.contribute) { _, _ ->
UriUtil.gotoUrl(requireContext(), R.string.contribution_url)
context?.openUrl(R.string.contribution_url)
}
//}
builder.setMessage(stringBuilder)

View file

@ -33,7 +33,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.takeUriPermission
class ExternalFileHelper {
@ -57,10 +57,8 @@ class ExternalFileHelper {
fun buildOpenDocument(onFileSelected: ((uri: Uri?) -> Unit)?) {
val resultCallback = ActivityResultCallback<Uri?> { result ->
result?.let { uri ->
UriUtil.takeUriPermission(activity?.contentResolver, uri)
onFileSelected?.invoke(uri)
}
activity?.contentResolver?.takeUriPermission(result)
onFileSelected?.invoke(result)
}
getContentResultLauncher = if (fragment != null) {

View file

@ -9,6 +9,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
@ -76,7 +77,7 @@ abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
}
protected fun closeDatabase() {
mDatabase?.clearAndClose(this)
mDatabase?.clearAndClose(this.getBinaryDir())
}
override fun onResume() {

View file

@ -27,7 +27,8 @@ import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.IOActionTask
import com.kunzisoft.keepass.utils.SingletonHolderParameter
import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.utils.UriHelper.decodeUri
import com.kunzisoft.keepass.utils.UriHelper.parseUri
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
class FileDatabaseHistoryAction(private val applicationContext: Context) {
@ -46,9 +47,9 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
databaseUri)
DatabaseFile(
databaseUri,
UriUtilDatabase.parse(fileDatabaseHistoryEntity?.keyFileUri),
fileDatabaseHistoryEntity?.keyFileUri?.parseUri(),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
UriUtilDatabase.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseHistoryEntity?.databaseUri?.decodeUri(),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
?: ""),
fileDatabaseInfo.exists,
@ -71,8 +72,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
{
it?.let { fileHistoryEntity ->
fileHistoryEntity.keyFileUri?.let { keyFileUri ->
keyFileUriResultListener.invoke(UriUtilDatabase.parse(
keyFileUri))
keyFileUriResultListener.invoke(keyFileUri.parseUri())
}
} ?: keyFileUriResultListener.invoke(null)
}
@ -96,10 +96,10 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
) {
databaseFileListLoaded.add(
DatabaseFile(
UriUtilDatabase.parse(fileDatabaseHistoryEntity.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistoryEntity.keyFileUri),
fileDatabaseHistoryEntity.databaseUri.parseUri(),
fileDatabaseHistoryEntity.keyFileUri?.parseUri(),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
UriUtilDatabase.decode(fileDatabaseHistoryEntity.databaseUri),
fileDatabaseHistoryEntity.databaseUri.decodeUri(),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
@ -165,10 +165,10 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
FileDatabaseInfo(applicationContext,
fileDatabaseHistory.databaseUri)
DatabaseFile(
UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
fileDatabaseHistory.databaseUri.parseUri(),
fileDatabaseHistory.keyFileUri?.parseUri(),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
fileDatabaseHistory.databaseUri.decodeUri(),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
@ -192,10 +192,10 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory)
if (returnValue > 0) {
DatabaseFile(
UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
fileDatabaseHistory.databaseUri.parseUri(),
fileDatabaseHistory.keyFileUri?.parseUri(),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
fileDatabaseHistory.databaseUri.decodeUri(),
databaseFileToDelete.databaseAlias
)
} else {

View file

@ -27,6 +27,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
class CreateDatabaseRunnable(
context: Context,
@ -47,7 +48,7 @@ class CreateDatabaseRunnable(
createData(mDatabaseUri, databaseName, rootName, templateGroupName)
}
} catch (e: Exception) {
mDatabase.clearAndClose(context)
mDatabase.clearAndClose(context.getBinaryDir())
setError(e)
}

View file

@ -32,7 +32,7 @@ import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
class LoadDatabaseRunnable(
private val context: Context,
@ -47,9 +47,11 @@ class LoadDatabaseRunnable(
private val mLoadDatabaseResult: ((Result) -> Unit)?,
) : ActionRunnable() {
private val binaryDir = context.getBinaryDir()
override fun onStartRun() {
// Clear before we load
mDatabase.clearAndClose(context)
mDatabase.clearAndClose(binaryDir)
}
override fun onActionRun() {
@ -60,7 +62,7 @@ class LoadDatabaseRunnable(
mMainCredential,
mChallengeResponseRetriever,
mReadonly,
UriUtilDatabase.getBinaryDir(context),
binaryDir,
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
@ -91,7 +93,7 @@ class LoadDatabaseRunnable(
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
} else {
mDatabase.clearAndClose(context)
mDatabase.clearAndClose(binaryDir)
}
}

View file

@ -26,7 +26,7 @@ import com.kunzisoft.keepass.database.exception.DatabaseException
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
class ReloadDatabaseRunnable(
private val context: Context,
@ -35,10 +35,11 @@ class ReloadDatabaseRunnable(
private val mLoadDatabaseResult: ((Result) -> Unit)?
) : ActionRunnable() {
private val binaryDir = context.getBinaryDir()
override fun onStartRun() {
// Clear before we load
mDatabase.clearIndexesAndBinaries(UriUtilDatabase.getBinaryDir(
context))
mDatabase.clearIndexesAndBinaries(binaryDir)
mDatabase.wasReloaded = true
}
@ -57,7 +58,7 @@ class ReloadDatabaseRunnable(
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
} else {
mDatabase.clearAndClose(context)
mDatabase.clearAndClose(binaryDir)
}
}

View file

@ -11,11 +11,10 @@ import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openExternalApp
/**
* Special activity to deal with hardware key drivers,
@ -158,8 +157,7 @@ class HardwareKeyActivity: DatabaseModeActivity(){
context.getString(R.string.error_driver_required, hardwareKey.toString())
)
.setPositiveButton(R.string.download) { _, _ ->
UriUtil.openExternalApp(
context,
context.openExternalApp(
context.getString(R.string.key_driver_app_id),
context.getString(R.string.key_driver_url)
)

View file

@ -13,7 +13,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openExternalApp
import kotlinx.coroutines.launch
class HardwareKeyResponseHelper {
@ -134,7 +134,7 @@ class HardwareKeyResponseHelper {
activity.getString(R.string.error_driver_required, hardwareKey.toString())
)
.setPositiveButton(R.string.download) { _, _ ->
UriUtil.openExternalApp(activity, activity.getString(R.string.key_driver_app_id))
activity.openExternalApp(activity.getString(R.string.key_driver_app_id))
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show()

View file

@ -36,7 +36,7 @@ import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.StreamDirection
import com.kunzisoft.keepass.tasks.BinaryDatabaseManager
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -218,7 +218,7 @@ class AttachmentFileNotificationService: LockNotificationService() {
}
)
val fileName = UriUtil.getFileData(this, attachmentNotification.uri)?.name
val fileName = attachmentNotification.uri.getDocumentFile(this)?.name
?: attachmentNotification.uri.path
val builder = buildNewNotification().apply {

View file

@ -47,7 +47,9 @@ import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.services.ClipboardEntryNotificationService
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import com.kunzisoft.keepass.utils.UriUtil.openUrl
import com.kunzisoft.keepass.utils.UriUtil.releaseAllUnnecessaryPermissionUris
class NestedAppSettingsFragment : NestedSettingsFragment() {
@ -81,7 +83,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<Preference>(getString(R.string.remember_database_locations_key))?.setOnPreferenceChangeListener { _, newValue ->
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAll {
UriUtil.releaseAllUnnecessaryPermissionUris(activity.applicationContext)
activity.releaseAllUnnecessaryPermissionUris()
}
}
true
@ -90,7 +92,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<Preference>(getString(R.string.remember_keyfile_locations_key))?.setOnPreferenceChangeListener { _, newValue ->
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAllKeyFiles {
UriUtil.releaseAllUnnecessaryPermissionUris(activity.applicationContext)
activity.releaseAllUnnecessaryPermissionUris()
}
}
true
@ -168,7 +170,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
findPreference<Preference>(getString(R.string.magic_keyboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(requireContext(), R.string.magic_keyboard_explanation_url)
context?.openUrl(R.string.magic_keyboard_explanation_url)
false
}
@ -185,7 +187,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
findPreference<Preference>(getString(R.string.autofill_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(requireContext(), R.string.autofill_explanation_url)
context?.openUrl(R.string.autofill_explanation_url)
false
}
@ -202,7 +204,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
findPreference<Preference>(getString(R.string.clipboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(requireContext(), R.string.clipboard_explanation_url)
context?.openUrl(R.string.clipboard_explanation_url)
false
}
@ -360,7 +362,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
findPreference<Preference>(getString(R.string.advanced_unlock_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(requireContext(), R.string.advanced_unlock_explanation_url)
context?.openUrl(R.string.advanced_unlock_explanation_url)
false
}
}
@ -404,7 +406,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<ListPreference>(getString(R.string.setting_style_key))?.setOnPreferenceChangeListener { _, newValue ->
var styleEnabled = true
val styleIdString = newValue as String
if (!UriUtil.contributingUser(activity)) {
if (!activity.isContributingUser()) {
for (themeIdDisabled in BuildConfig.STYLES_DISABLED) {
if (themeIdDisabled == styleIdString) {
styleEnabled = false
@ -435,7 +437,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<IconPackListPreference>(getString(R.string.setting_icon_pack_choose_key))?.setOnPreferenceChangeListener { _, newValue ->
var iconPackEnabled = true
val iconPackId = newValue as String
if (!UriUtil.contributingUser(activity)) {
if (!activity.isContributingUser()) {
for (iconPackIdDisabled in BuildConfig.ICON_PACKS_DISABLED) {
if (iconPackIdDisabled == iconPackId) {
iconPackEnabled = false

View file

@ -36,7 +36,7 @@ import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.password.PassphraseGenerator
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import java.util.*
object PreferencesUtil {
@ -173,7 +173,7 @@ object PreferencesUtil {
fun setStyle(context: Context, styleString: String) {
var tempThemeString = styleString
if (!UriUtil.contributingUser(context)) {
if (!context.isContributingUser()) {
if (tempThemeString in BuildConfig.STYLES_DISABLED) {
tempThemeString = Stylish.defaultStyle(context)
}

View file

@ -5,11 +5,12 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import com.kunzisoft.keepass.utils.readAllBytes
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.binary.BinaryCache
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
import com.kunzisoft.keepass.utils.UriHelper.getUriOutputStream
import com.kunzisoft.keepass.utils.readAllBytes
import kotlinx.coroutines.*
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@ -29,7 +30,7 @@ object BinaryDatabaseManager {
update: ((percent: Int)->Unit)? = null,
canceled: ()-> Boolean = { false },
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
UriUtil.getUriOutputStream(contentResolver, attachmentToUploadUri)?.use { outputStream ->
contentResolver.getUriOutputStream(attachmentToUploadUri)?.use { outputStream ->
downloadFromDatabase(database.binaryCache, outputStream, binaryData, update, canceled, bufferSize)
}
}
@ -64,7 +65,7 @@ object BinaryDatabaseManager {
canceled: ()-> Boolean = { false },
bufferSize: Int = DEFAULT_BUFFER_SIZE) {
val fileSize = contentResolver.openFileDescriptor(attachmentFromDownloadUri, "r")?.statSize ?: 0
UriUtil.getUriInputStream(contentResolver, attachmentFromDownloadUri)?.use { inputStream ->
contentResolver.getUriInputStream(attachmentFromDownloadUri)?.use { inputStream ->
uploadToDatabase(database.binaryCache, inputStream, fileSize, binaryData, update, canceled, bufferSize)
}
}
@ -97,7 +98,7 @@ object BinaryDatabaseManager {
binaryData: BinaryData?) {
try {
binaryData?.let {
UriUtil.getUriInputStream(contentResolver, bitmapUri)?.use { inputStream ->
contentResolver.getUriInputStream(bitmapUri)?.use { inputStream ->
BitmapFactory.decodeStream(inputStream)?.let { bitmap ->
val bitmapResized = bitmap.resize(DEFAULT_ICON_WIDTH)
val byteArrayOutputStream = ByteArrayOutputStream()

View file

@ -36,6 +36,8 @@ import com.kunzisoft.keepass.services.ClipboardEntryNotificationService
import com.kunzisoft.keepass.services.KeyboardEntryNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil.getBinaryDir
import com.kunzisoft.keepass.utils.UriUtil.releaseAllUnnecessaryPermissionUris
const val DATABASE_START_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_START_TASK_ACTION"
const val DATABASE_STOP_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_STOP_TASK_ACTION"
@ -161,8 +163,8 @@ fun Context.closeDatabase(database: Database?) {
cancelAll()
}
// Clear data
database?.clearAndClose(this)
database?.clearAndClose(this.getBinaryDir())
// Release not useful URI permission
UriUtil.releaseAllUnnecessaryPermissionUris(applicationContext)
applicationContext.releaseAllUnnecessaryPermissionUris()
}

View file

@ -28,13 +28,15 @@ import android.view.MenuItem
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.AboutActivity
import com.kunzisoft.keepass.settings.SettingsActivity
import com.kunzisoft.keepass.utils.UriUtil.isContributingUser
import com.kunzisoft.keepass.utils.UriUtil.openUrl
object MenuUtil {
fun defaultMenuInflater(context: Context, inflater: MenuInflater, menu: Menu) {
inflater.inflate(R.menu.settings, menu)
inflater.inflate(R.menu.about, menu)
if (!UriUtil.contributingUser(context))
if (!context.isContributingUser())
menu.findItem(R.id.menu_contribute)?.isVisible = false
}
@ -46,7 +48,7 @@ object MenuUtil {
timeoutEnable: Boolean = false) {
when (item.itemId) {
R.id.menu_contribute -> {
UriUtil.gotoUrl(activity, R.string.contribution_url)
activity.openUrl(R.string.contribution_url)
}
R.id.menu_app_settings -> {
// To avoid flickering when launch settings in a LockingActivity

View file

@ -1,34 +0,0 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX 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.
*
* KeePassDX 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 KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.utils
open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) {
@Volatile
private var instance: T? = null
fun getInstance(arg: A): T {
return when {
instance != null -> instance!!
else -> synchronized(this) {
if (instance == null) instance = constructor(arg)
instance!!
}
}
}
}

View file

@ -32,26 +32,25 @@ import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.education.Education
import java.io.*
import java.util.*
import com.kunzisoft.keepass.utils.UriHelper.withContentScheme
import com.kunzisoft.keepass.utils.UriHelper.withFileScheme
import java.io.File
object UriUtil {
fun getFileData(context: Context, fileUri: Uri?): DocumentFile? {
if (fileUri == null)
return null
fun Uri.getDocumentFile(context: Context): DocumentFile? {
return try {
when {
isFileScheme(fileUri) -> {
fileUri.path?.let {
this.withFileScheme() -> {
this.path?.let {
File(it).let { file ->
return DocumentFile.fromFile(file)
}
}
}
isContentScheme(fileUri) -> {
DocumentFile.fromSingleUri(context, fileUri)
this.withContentScheme() -> {
DocumentFile.fromSingleUri(context, this)
}
else -> {
Log.e("FileData", "Content scheme not known")
@ -64,66 +63,6 @@ object UriUtil {
}
}
@Throws(FileNotFoundException::class)
fun getUriOutputStream(contentResolver: ContentResolver, fileUri: Uri?): OutputStream? {
if (fileUri == null)
return null
return when {
isFileScheme(fileUri) -> fileUri.path?.let { FileOutputStream(it) }
isContentScheme(fileUri) -> {
try {
contentResolver.openOutputStream(fileUri, "wt")
} catch (e: FileNotFoundException) {
Log.e(TAG, "Unable to open stream in `wt` mode, retry in `rwt` mode.", e)
// https://issuetracker.google.com/issues/180526528
// Try with rwt to fix content provider issue
val outStream = contentResolver.openOutputStream(fileUri, "rwt")
Log.w(TAG, "`rwt` mode used.")
outStream
}
}
else -> null
}
}
@Throws(FileNotFoundException::class)
fun getUriInputStream(contentResolver: ContentResolver, fileUri: Uri?): InputStream? {
if (fileUri == null)
return null
return when {
isFileScheme(fileUri) -> fileUri.path?.let { FileInputStream(it) }
isContentScheme(fileUri) -> contentResolver.openInputStream(fileUri)
else -> null
}
}
private fun isFileScheme(fileUri: Uri): Boolean {
val scheme = fileUri.scheme
if (scheme == null || scheme.isEmpty() || scheme.lowercase(Locale.ENGLISH) == "file") {
return true
}
return false
}
private fun isContentScheme(fileUri: Uri): Boolean {
val scheme = fileUri.scheme
if (scheme != null && scheme.lowercase(Locale.ENGLISH) == "content") {
return true
}
return false
}
fun parse(stringUri: String?): Uri? {
return if (stringUri?.isNotEmpty() == true) {
Uri.parse(stringUri)
} else
null
}
fun decode(uri: String?): String {
return Uri.decode(uri) ?: ""
}
private fun persistUriPermission(contentResolver: ContentResolver?,
uri: Uri,
release: Boolean,
@ -190,18 +129,19 @@ object UriUtil {
}
}
fun takeUriPermission(contentResolver: ContentResolver?,
uri: Uri,
readOnly: Boolean = false) {
persistUriPermission(contentResolver, uri, false, readOnly)
fun ContentResolver.takeUriPermission(uri: Uri?, readOnly: Boolean = false) {
uri?.let {
persistUriPermission(this, it, false, readOnly)
}
}
fun releaseUriPermission(contentResolver: ContentResolver?,
uri: Uri) {
persistUriPermission(contentResolver, uri, release = true, readOnly = false)
fun ContentResolver.releaseUriPermission(uri: Uri?) {
uri?.let {
persistUriPermission(this, it, release = true, readOnly = false)
}
}
fun releaseAllUnnecessaryPermissionUris(applicationContext: Context?) {
fun Context.releaseAllUnnecessaryPermissionUris() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
applicationContext?.let { appContext ->
val fileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(appContext)
@ -220,17 +160,17 @@ object UriUtil {
resolver.persistedUriPermissions.forEach { uriPermission ->
val uri = uriPermission.uri
if (!listToNotRemove.contains(uri))
releaseUriPermission(resolver, uri)
resolver.releaseUriPermission(uri)
}
}
}
}
}
fun getUriFromIntent(intent: Intent?, key: String): Uri? {
fun Intent.getUri(key: String): Uri? {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val clipData = intent?.clipData
val clipData = this.clipData
if (clipData != null) {
if (clipData.description.label == key) {
if (clipData.itemCount == 1) {
@ -243,12 +183,12 @@ object UriUtil {
}
}
} catch (e: Exception) {
return intent?.getParcelableExtra(key)
return this.getParcelableExtra(key)
}
return null
}
fun gotoUrl(context: Context, url: String?) {
fun Context.openUrl(url: String?) {
try {
if (url != null && url.isNotEmpty()) {
// Default http:// if no protocol specified
@ -257,28 +197,30 @@ object UriUtil {
} else {
url
}
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(newUrl)))
this.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(newUrl)))
}
} catch (e: Exception) {
Toast.makeText(context, R.string.no_url_handler, Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show()
}
}
fun gotoUrl(context: Context, resId: Int) {
gotoUrl(context, context.getString(resId))
fun Context.openUrl(resId: Int) {
this.openUrl(this.getString(resId))
}
fun contributingUser(context: Context): Boolean {
return (Education.isEducationScreenReclickedPerformed(context)
fun Context.isContributingUser(): Boolean {
return (Education.isEducationScreenReclickedPerformed(this)
|| isExternalAppInstalled(
context,
context.getString(R.string.keepro_app_id),
this,
this.getString(R.string.keepro_app_id),
false
)
)
}
fun isExternalAppInstalled(context: Context, packageName: String, showError: Boolean = true): Boolean {
private fun isExternalAppInstalled(context: Context,
packageName: String,
showError: Boolean = true): Boolean {
try {
context.applicationContext.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES)
Education.setEducationScreenReclickedPerformed(context)
@ -290,16 +232,16 @@ object UriUtil {
return false
}
fun openExternalApp(context: Context, packageName: String, sourcesURL: String? = null) {
fun Context.openExternalApp(packageName: String, sourcesURL: String? = null) {
var launchIntent: Intent? = null
try {
launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply {
launchIntent = this.packageManager.getLaunchIntentForPackage(packageName)?.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
} catch (ignored: Exception) { }
try {
if (launchIntent == null) {
context.startActivity(
this.startActivity(
Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(
@ -309,7 +251,7 @@ object UriUtil {
) {
sourcesURL
} else {
context.getString(
this.getString(
if (BuildConfig.CLOSED_STORE)
R.string.play_store_url
else
@ -321,18 +263,18 @@ object UriUtil {
)
)
} else {
context.startActivity(launchIntent)
this.startActivity(launchIntent)
}
} catch (e: Exception) {
Log.e(TAG, "App cannot be open", e)
}
}
fun getBinaryDir(context: Context): File {
fun Context.getBinaryDir(): File {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
context.applicationContext.noBackupFilesDir
this.applicationContext.noBackupFilesDir
} else {
context.applicationContext.filesDir
this.applicationContext.filesDir
}
}

View file

@ -12,7 +12,7 @@ import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
class KeyFileSelectionView @JvmOverloads constructor(context: Context,
@ -66,7 +66,7 @@ class KeyFileSelectionView @JvmOverloads constructor(context: Context,
}
}
keyFileNameView.text = value?.let {
UriUtil.getFileData(context, value)?.name ?: value.path
value.getDocumentFile(context)?.name ?: value.path
} ?: ""
}

View file

@ -43,7 +43,7 @@ import com.kunzisoft.keepass.database.helper.isStandardPasswordName
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
import com.kunzisoft.keepass.password.PasswordGenerator
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtil.openExternalApp
class TextFieldView @JvmOverloads constructor(context: Context,
@ -253,7 +253,7 @@ class TextFieldView @JvmOverloads constructor(context: Context,
val packageName = valueView.text.toString()
// TODO #996 if (UriUtil.isExternalAppInstalled(context, packageName)) {
valueView.customLink {
UriUtil.openExternalApp(context, packageName)
context.openExternalApp(packageName)
}
//}
}

View file

@ -6,10 +6,10 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.utils.IOActionTask
import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.IOActionTask
import com.kunzisoft.keepass.utils.UriHelper.parseUri
class DatabaseFileViewModel(application: Application) : AndroidViewModel(application) {
@ -26,8 +26,8 @@ class DatabaseFileViewModel(application: Application) : AndroidViewModel(applica
fun checkIfIsDefaultDatabase(databaseUri: Uri) {
IOActionTask(
{
(UriUtil.parse(PreferencesUtil.getDefaultDatabasePath(getApplication<App>().applicationContext))
== databaseUri)
(PreferencesUtil.getDefaultDatabasePath(getApplication<App>().applicationContext)
?.parseUri() == databaseUri)
},
{
isDefaultDatabase.value = it

View file

@ -6,11 +6,12 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.utils.IOActionTask
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.IOActionTask
import com.kunzisoft.keepass.utils.UriHelper.parseUri
import com.kunzisoft.keepass.utils.UriUtil.releaseUriPermission
class DatabaseFilesViewModel(application: Application) : AndroidViewModel(application) {
@ -31,7 +32,8 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
fun checkDefaultDatabase() {
IOActionTask(
{
UriUtil.parse(PreferencesUtil.getDefaultDatabasePath(getApplication<App>().applicationContext))
PreferencesUtil.getDefaultDatabasePath(getApplication<App>().applicationContext)
?.parseUri()
},
{
defaultDatabase.value = it
@ -118,18 +120,8 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
databaseFileDeleted?.let { _ ->
// Release database and keyfile URIs permissions
val contentResolver = getApplication<App>().applicationContext.contentResolver
databaseFileDeleted.databaseUri?.let { databaseUri ->
UriUtil.releaseUriPermission(
contentResolver,
databaseUri
)
}
databaseFileDeleted.keyFileUri?.let { keyFileUri ->
UriUtil.releaseUriPermission(
contentResolver,
keyFileUri
)
}
contentResolver.releaseUriPermission(databaseFileDeleted.databaseUri)
contentResolver.releaseUriPermission(databaseFileDeleted.keyFileUri)
// Call the feedback
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
databaseFileAction = DatabaseFileAction.DELETE

View file

@ -23,10 +23,12 @@ import android.content.Context
import android.net.Uri
import android.text.format.Formatter
import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriHelper.parseUri
import com.kunzisoft.keepass.utils.UriUtil.getDocumentFile
import com.kunzisoft.keepass.utils.UriUtil.takeUriPermission
import java.io.Serializable
import java.text.DateFormat
import java.util.Date
import java.util.*
class FileDatabaseInfo : Serializable {
@ -43,16 +45,14 @@ class FileDatabaseInfo : Serializable {
constructor(context: Context, filePath: String) {
this.context = context
this.fileUri = UriUtil.parse(filePath)
this.fileUri = filePath.parseUri()
init()
}
fun init() {
// Check permission
fileUri?.let { uri ->
UriUtil.takeUriPermission(context.contentResolver, uri)
}
documentFile = UriUtil.getFileData(context, fileUri)
context.contentResolver.takeUriPermission(fileUri)
documentFile = fileUri?.getDocumentFile(context)
}
var exists: Boolean = false

View file

@ -20,7 +20,6 @@
package com.kunzisoft.keepass.database.element
import android.content.ContentResolver
import android.content.Context
import android.graphics.Color
import android.net.Uri
import android.util.Log
@ -58,6 +57,8 @@ import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.icons.InterfaceIconPackChooser
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.*
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
import com.kunzisoft.keepass.utils.UriHelper.getUriOutputStream
import java.io.*
import java.util.*
@ -786,7 +787,7 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
openDatabaseKDBX: (InputStream) -> Unit) {
try {
// Load Data, pass Uris as InputStreams
val databaseStream = UriUtilDatabase.getUriInputStream(contentResolver, databaseUri)
val databaseStream = contentResolver.getUriInputStream(databaseUri)
?: throw UnknownDatabaseLocationException()
BufferedInputStream(databaseStream).use { databaseInputStream ->
@ -867,7 +868,7 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
}
}
// Copy from the cache to the final stream
UriUtilDatabase.getUriOutputStream(contentResolver, saveUri)?.use { outputStream ->
contentResolver.getUriOutputStream(saveUri)?.use { outputStream ->
cacheFile.inputStream().use { inputStream ->
inputStream.readAllBytes { buffer ->
outputStream.write(buffer)
@ -1005,8 +1006,8 @@ class Database(private val iconPackChooser: InterfaceIconPackChooser) {
}
}
fun clearAndClose(context: Context? = null) {
clearIndexesAndBinaries(context?.let { UriUtilDatabase.getBinaryDir(context) })
fun clearAndClose(filesDirectory: File? = null) {
clearIndexesAndBinaries(filesDirectory)
this.mDatabaseKDB = null
this.mDatabaseKDBX = null
this.fileUri = null

View file

@ -29,7 +29,7 @@ import com.kunzisoft.keepass.database.element.database.DatabaseKDBX
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
import com.kunzisoft.keepass.utils.StringUtil.toHexString
import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.utils.UriHelper.getUriInputStream
import com.kunzisoft.keepass.utils.readEnum
import com.kunzisoft.keepass.utils.writeEnum
import org.apache.commons.codec.binary.Hex
@ -148,7 +148,7 @@ data class MainCredential(var password: String? = null,
@Throws(Exception::class)
private fun getKeyFileData(contentResolver: ContentResolver,
keyFileUri: Uri): ByteArray? {
UriUtilDatabase.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream ->
contentResolver.getUriInputStream(keyFileUri)?.use { keyFileInputStream ->
return keyFileInputStream.readBytes()
}
return null

View file

@ -36,3 +36,17 @@ open class SingletonHolder<out T>(private val constructor: (iconPackChooser : In
}
}
}
open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) {
@Volatile
private var instance: T? = null
fun getInstance(arg: A): T {
return when {
instance != null -> instance!!
else -> synchronized(this) {
if (instance == null) instance = constructor(arg)
instance!!
}
}
}
}

View file

@ -19,65 +19,50 @@
*/
package com.kunzisoft.keepass.utils
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import android.os.Build
import android.util.Log
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.util.Locale
import java.io.*
import java.util.*
object UriUtilDatabase {
fun parse(stringUri: String?): Uri? {
return if (stringUri?.isNotEmpty() == true) {
Uri.parse(stringUri)
} else
null
object UriHelper {
fun String.parseUri(): Uri? {
return if (this.isNotEmpty()) Uri.parse(this) else null
}
fun decode(uri: String?): String {
return Uri.decode(uri) ?: ""
}
fun getBinaryDir(context: Context): File {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
context.applicationContext.noBackupFilesDir
} else {
context.applicationContext.filesDir
}
fun String.decodeUri(): String {
return Uri.decode(this) ?: ""
}
@Throws(FileNotFoundException::class)
fun getUriInputStream(contentResolver: ContentResolver, fileUri: Uri?): InputStream? {
fun ContentResolver.getUriInputStream(fileUri: Uri?): InputStream? {
if (fileUri == null)
return null
return when {
isFileScheme(fileUri) -> fileUri.path?.let { FileInputStream(it) }
isContentScheme(fileUri) -> contentResolver.openInputStream(fileUri)
fileUri.withFileScheme() -> fileUri.path?.let { FileInputStream(it) }
fileUri.withContentScheme() -> this.openInputStream(fileUri)
else -> null
}
}
@SuppressLint("Recycle")
@Throws(FileNotFoundException::class)
fun getUriOutputStream(contentResolver: ContentResolver, fileUri: Uri?): OutputStream? {
fun ContentResolver.getUriOutputStream(fileUri: Uri?): OutputStream? {
if (fileUri == null)
return null
return when {
isFileScheme(fileUri) -> fileUri.path?.let { FileOutputStream(it) }
isContentScheme(fileUri) -> {
fileUri.withFileScheme() -> fileUri.path?.let { FileOutputStream(it) }
fileUri.withContentScheme() -> {
try {
contentResolver.openOutputStream(fileUri, "wt")
this.openOutputStream(fileUri, "wt")
} catch (e: FileNotFoundException) {
Log.e(TAG, "Unable to open stream in `wt` mode, retry in `rwt` mode.", e)
// https://issuetracker.google.com/issues/180526528
// Try with rwt to fix content provider issue
val outStream = contentResolver.openOutputStream(fileUri, "rwt")
val outStream = this.openOutputStream(fileUri, "rwt")
Log.w(TAG, "`rwt` mode used.")
outStream
}
@ -86,16 +71,16 @@ object UriUtilDatabase {
}
}
private fun isFileScheme(fileUri: Uri): Boolean {
val scheme = fileUri.scheme
fun Uri.withFileScheme(): Boolean {
val scheme = this.scheme
if (scheme == null || scheme.isEmpty() || scheme.lowercase(Locale.ENGLISH) == "file") {
return true
}
return false
}
private fun isContentScheme(fileUri: Uri): Boolean {
val scheme = fileUri.scheme
fun Uri.withContentScheme(): Boolean {
val scheme = this.scheme
if (scheme != null && scheme.lowercase(Locale.ENGLISH) == "content") {
return true
}