mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'release/2.9.16'
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
KeePassDX(2.9.16)
|
||||
* Fix small bugs #948
|
||||
|
||||
KeePassDX(2.9.15)
|
||||
* Fix themes #935 #926
|
||||
* Decrease default clipboard time #934
|
||||
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.kunzisoft.keepass"
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 30
|
||||
versionCode = 68
|
||||
versionName = "2.9.15"
|
||||
versionCode = 70
|
||||
versionName = "2.9.16"
|
||||
multiDexEnabled true
|
||||
|
||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||
|
||||
@@ -39,6 +39,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
@@ -93,6 +94,8 @@ class EntryActivity : LockingActivity() {
|
||||
private var clipboardHelper: ClipboardHelper? = null
|
||||
private var mFirstLaunchOfActivity: Boolean = false
|
||||
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var iconColor: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -140,6 +143,9 @@ class EntryActivity : LockingActivity() {
|
||||
clipboardHelper = ClipboardHelper(this)
|
||||
mFirstLaunchOfActivity = savedInstanceState?.getBoolean(KEY_FIRST_LAUNCH_ACTIVITY) ?: true
|
||||
|
||||
// Init SAF manager
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
|
||||
// Init attachment service binder manager
|
||||
mAttachmentFileBinderManager = AttachmentFileBinderManager(this)
|
||||
|
||||
@@ -344,7 +350,7 @@ class EntryActivity : LockingActivity() {
|
||||
|
||||
// Manage attachments
|
||||
entryContentsView?.assignAttachments(entryInfo.attachments.toSet(), StreamDirection.DOWNLOAD) { attachmentItem ->
|
||||
createDocument(this, attachmentItem.name)?.let { requestCode ->
|
||||
mExternalFileHelper?.createDocument(attachmentItem.name)?.let { requestCode ->
|
||||
mAttachmentsToDownload[requestCode] = attachmentItem
|
||||
}
|
||||
}
|
||||
@@ -380,7 +386,7 @@ class EntryActivity : LockingActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
|
||||
mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
|
||||
if (createdFileUri != null) {
|
||||
mAttachmentsToDownload[requestCode]?.let { attachmentToDownload ->
|
||||
mAttachmentFileBinderManager
|
||||
|
||||
@@ -45,7 +45,7 @@ import com.kunzisoft.keepass.activities.dialogs.*
|
||||
import com.kunzisoft.keepass.activities.dialogs.FileTooBigDialogFragment.Companion.MAX_WARNING_BINARY_FILE
|
||||
import com.kunzisoft.keepass.activities.fragments.EntryEditFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||
@@ -103,7 +103,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
private var lockView: View? = null
|
||||
|
||||
// To manage attachments
|
||||
private var mSelectFileHelper: SelectFileHelper? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
||||
private var mAllowMultipleAttachments: Boolean = false
|
||||
private var mTempAttachments = ArrayList<EntryAttachmentState>()
|
||||
@@ -241,7 +241,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
}
|
||||
|
||||
// To retrieve attachment
|
||||
mSelectFileHelper = SelectFileHelper(this)
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
mAttachmentFileBinderManager = AttachmentFileBinderManager(this)
|
||||
|
||||
// Save button
|
||||
@@ -458,8 +458,8 @@ class EntryEditActivity : LockingActivity(),
|
||||
/**
|
||||
* Add a new attachment
|
||||
*/
|
||||
private fun addNewAttachment(item: MenuItem) {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.onMenuItemClick(item)
|
||||
private fun addNewAttachment() {
|
||||
mExternalFileHelper?.openDocument()
|
||||
}
|
||||
|
||||
override fun onValidateUploadFileTooBig(attachmentToUploadUri: Uri?, fileName: String?) {
|
||||
@@ -505,7 +505,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
entryEditFragment?.icon = icon
|
||||
}
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
uri?.let { attachmentToUploadUri ->
|
||||
UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile ->
|
||||
documentFile.name?.let { fileName ->
|
||||
@@ -655,7 +655,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
&& entryEditActivityEducation.checkAndPerformedAttachmentEducation(
|
||||
attachmentView,
|
||||
{
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(attachmentView)
|
||||
mExternalFileHelper?.openDocument()
|
||||
},
|
||||
{
|
||||
performedNextEducation(entryEditActivityEducation)
|
||||
@@ -683,7 +683,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
return true
|
||||
}
|
||||
R.id.menu_add_attachment -> {
|
||||
addNewAttachment(item)
|
||||
addNewAttachment()
|
||||
return true
|
||||
}
|
||||
R.id.menu_add_otp -> {
|
||||
|
||||
@@ -42,8 +42,9 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
||||
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
|
||||
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
|
||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||
@@ -82,7 +83,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
|
||||
private var mDatabaseFileUri: Uri? = null
|
||||
|
||||
private var mSelectFileHelper: SelectFileHelper? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
|
||||
@@ -103,14 +104,9 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
createDatabaseButtonView?.setOnClickListener { createNewFile() }
|
||||
|
||||
// Open database button
|
||||
mSelectFileHelper = SelectFileHelper(this)
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
openDatabaseButtonView = findViewById(R.id.open_keyfile_button)
|
||||
openDatabaseButtonView?.apply {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.let {
|
||||
setOnClickListener(it)
|
||||
setOnLongClickListener(it)
|
||||
}
|
||||
}
|
||||
openDatabaseButtonView?.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
|
||||
// History list
|
||||
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
|
||||
@@ -171,8 +167,6 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
databaseFiles.databaseFileToActivate?.let { databaseFileToAdd ->
|
||||
mAdapterDatabaseHistory?.addDatabaseFileHistory(databaseFileToAdd)
|
||||
}
|
||||
GroupActivity.launch(this@FileDatabaseSelectActivity,
|
||||
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity))
|
||||
}
|
||||
DatabaseFilesViewModel.DatabaseFileAction.UPDATE -> {
|
||||
databaseFiles.databaseFileToActivate?.let { databaseFileToUpdate ->
|
||||
@@ -185,10 +179,10 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
databaseFilesViewModel.consumeAction()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to observe database action", e)
|
||||
}
|
||||
databaseFilesViewModel.consumeAction()
|
||||
}
|
||||
|
||||
// Observe default database
|
||||
@@ -206,6 +200,8 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
val mainCredential = result.data?.getParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY) ?: MainCredential()
|
||||
databaseFilesViewModel.addDatabaseFile(databaseUri, mainCredential.keyFileUri)
|
||||
}
|
||||
GroupActivity.launch(this@FileDatabaseSelectActivity,
|
||||
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity))
|
||||
}
|
||||
ACTION_DATABASE_LOAD_TASK -> {
|
||||
val database = Database.getInstance()
|
||||
@@ -234,7 +230,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
* Create a new file by calling the content provider
|
||||
*/
|
||||
private fun createNewFile() {
|
||||
createDocument(this, getString(R.string.database_file_name_default) +
|
||||
mExternalFileHelper?.createDocument( getString(R.string.database_file_name_default) +
|
||||
getString(R.string.database_file_extension_default), "application/x-keepass")
|
||||
}
|
||||
|
||||
@@ -286,7 +282,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
// Show open and create button or special mode
|
||||
when (mSpecialMode) {
|
||||
SpecialMode.DEFAULT -> {
|
||||
if (allowCreateDocumentByStorageAccessFramework(packageManager)) {
|
||||
if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager)) {
|
||||
// There is an activity which can handle this intent.
|
||||
createDatabaseButtonView?.visibility = View.VISIBLE
|
||||
} else{
|
||||
@@ -359,14 +355,14 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
if (uri != null) {
|
||||
launchPasswordActivityWithPath(uri)
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the created URI from the file manager
|
||||
onCreateDocumentResult(requestCode, resultCode, data) { databaseFileCreatedUri ->
|
||||
mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { databaseFileCreatedUri ->
|
||||
mDatabaseFileUri = databaseFileCreatedUri
|
||||
if (mDatabaseFileUri != null) {
|
||||
AssignMasterKeyDialogFragment.getInstance(true)
|
||||
@@ -414,7 +410,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
openDatabaseButtonView!!,
|
||||
{ tapTargetView ->
|
||||
tapTargetView?.let {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it)
|
||||
mExternalFileHelper?.openDocument()
|
||||
}
|
||||
},
|
||||
{}
|
||||
|
||||
@@ -34,7 +34,8 @@ import androidx.fragment.app.commit
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.fragments.IconPickerFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
@@ -66,7 +67,7 @@ class IconPickerActivity : LockingActivity() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
private var mSelectFileHelper: SelectFileHelper? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -84,15 +85,11 @@ class IconPickerActivity : LockingActivity() {
|
||||
|
||||
coordinatorLayout = findViewById(R.id.icon_picker_coordinator)
|
||||
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
|
||||
uploadButton = findViewById(R.id.icon_picker_upload)
|
||||
if (mDatabase?.allowCustomIcons == true) {
|
||||
uploadButton.setOnClickListener {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it)
|
||||
}
|
||||
uploadButton.setOnLongClickListener {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.onLongClick(it)
|
||||
true
|
||||
}
|
||||
uploadButton.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
} else {
|
||||
uploadButton.visibility = View.GONE
|
||||
}
|
||||
@@ -124,8 +121,6 @@ class IconPickerActivity : LockingActivity() {
|
||||
// Focus view to reinitialize timeout
|
||||
findViewById<ViewGroup>(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this)
|
||||
|
||||
mSelectFileHelper = SelectFileHelper(this)
|
||||
|
||||
iconPickerViewModel.standardIconPicked.observe(this) { iconStandard ->
|
||||
mIconImage.standard = iconStandard
|
||||
// Remove the custom icon if a standard one is selected
|
||||
@@ -281,7 +276,7 @@ class IconPickerActivity : LockingActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
addCustomIcon(uri)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,7 @@ import androidx.fragment.app.commit
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.DuplicateUuidDialog
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.helpers.*
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
@@ -95,7 +92,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
private var mDatabaseKeyFileUri: Uri? = null
|
||||
|
||||
private var mRememberKeyFile: Boolean = false
|
||||
private var mSelectFileHelper: SelectFileHelper? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var mPermissionAsked = false
|
||||
private var readOnly: Boolean = false
|
||||
@@ -138,13 +135,8 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState)
|
||||
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this)
|
||||
|
||||
mSelectFileHelper = SelectFileHelper(this@PasswordActivity)
|
||||
keyFileSelectionView?.apply {
|
||||
mSelectFileHelper?.selectFileOnClickViewListener?.let {
|
||||
setOnClickListener(it)
|
||||
setOnLongClickListener(it)
|
||||
}
|
||||
}
|
||||
mExternalFileHelper = ExternalFileHelper(this@PasswordActivity)
|
||||
keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
|
||||
passwordView?.setOnEditorActionListener(onEditorActionListener)
|
||||
passwordView?.addTextChangedListener(object : TextWatcher {
|
||||
@@ -702,7 +694,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
}
|
||||
|
||||
var keyFileResult = false
|
||||
mSelectFileHelper?.let {
|
||||
mExternalFileHelper?.let {
|
||||
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data
|
||||
) { uri ->
|
||||
if (uri != null) {
|
||||
|
||||
@@ -30,13 +30,13 @@ import android.text.SpannableStringBuilder
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.SelectFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.setOpenDocumentClickListener
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import com.kunzisoft.keepass.view.KeyFileSelectionView
|
||||
@@ -60,7 +60,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
||||
|
||||
private var mListener: AssignPasswordDialogListener? = null
|
||||
|
||||
private var mSelectFileHelper: SelectFileHelper? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var mEmptyPasswordConfirmationDialog: AlertDialog? = null
|
||||
private var mNoKeyConfirmationDialog: AlertDialog? = null
|
||||
@@ -133,11 +133,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
||||
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
|
||||
keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection)
|
||||
|
||||
mSelectFileHelper = SelectFileHelper(this)
|
||||
keyFileSelectionView?.apply {
|
||||
setOnClickListener(mSelectFileHelper?.selectFileOnClickViewListener)
|
||||
setOnLongClickListener(mSelectFileHelper?.selectFileOnClickViewListener)
|
||||
}
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
|
||||
val dialog = builder.create()
|
||||
|
||||
@@ -289,7 +286,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
mSelectFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
|
||||
uri?.let { pathUri ->
|
||||
UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile ->
|
||||
keyFileSelectionView?.error = null
|
||||
|
||||
@@ -20,30 +20,25 @@
|
||||
package com.kunzisoft.keepass.activities.helpers
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
class SelectFileHelper {
|
||||
class ExternalFileHelper {
|
||||
|
||||
private var activity: Activity? = null
|
||||
private var activity: FragmentActivity? = null
|
||||
private var fragment: Fragment? = null
|
||||
|
||||
val selectFileOnClickViewListener: SelectFileOnClickViewListener
|
||||
get() = SelectFileOnClickViewListener()
|
||||
|
||||
constructor(context: Activity) {
|
||||
constructor(context: FragmentActivity) {
|
||||
this.activity = context
|
||||
this.fragment = null
|
||||
}
|
||||
@@ -53,56 +48,33 @@ class SelectFileHelper {
|
||||
this.fragment = context
|
||||
}
|
||||
|
||||
inner class SelectFileOnClickViewListener :
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener,
|
||||
MenuItem.OnMenuItemClickListener {
|
||||
|
||||
private fun onAbstractClick(longClick: Boolean = false) {
|
||||
fun openDocument(getContent: Boolean = false,
|
||||
typeString: String = "*/*") {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
if (longClick) {
|
||||
try {
|
||||
openActivityWithActionGetContent()
|
||||
if (getContent) {
|
||||
openActivityWithActionGetContent(typeString)
|
||||
} else {
|
||||
openActivityWithActionOpenDocument(typeString)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
openActivityWithActionOpenDocument()
|
||||
Log.e(TAG, "Unable to open document", e)
|
||||
showFileManagerDialogFragment()
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
openActivityWithActionOpenDocument()
|
||||
} catch (e: Exception) {
|
||||
openActivityWithActionGetContent()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Enable to start the file picker activity", e)
|
||||
// Open browser dialog
|
||||
if (lookForOpenIntentsFilePicker())
|
||||
showBrowserDialog()
|
||||
showFileManagerDialogFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
onAbstractClick()
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
onAbstractClick(true)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
onAbstractClick()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun openActivityWithActionOpenDocument() {
|
||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||
private fun openActivityWithActionOpenDocument(typeString: String) {
|
||||
val intentOpenDocument = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
type = typeString
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
|
||||
}
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
}
|
||||
@@ -112,13 +84,15 @@ class SelectFileHelper {
|
||||
activity?.startActivityForResult(intentOpenDocument, OPEN_DOC)
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun openActivityWithActionGetContent() {
|
||||
@RequiresApi(Build.VERSION_CODES.KITKAT)
|
||||
private fun openActivityWithActionGetContent(typeString: String) {
|
||||
val intentGetContent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
type = typeString
|
||||
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
|
||||
}
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||
}
|
||||
@@ -128,69 +102,12 @@ class SelectFileHelper {
|
||||
activity?.startActivityForResult(intentGetContent, GET_CONTENT)
|
||||
}
|
||||
|
||||
private fun lookForOpenIntentsFilePicker(): Boolean {
|
||||
var showBrowser = false
|
||||
try {
|
||||
if (isIntentAvailable(activity!!, OPEN_INTENTS_FILE_BROWSE)) {
|
||||
val intent = Intent(OPEN_INTENTS_FILE_BROWSE)
|
||||
if (fragment != null)
|
||||
fragment?.startActivityForResult(intent, FILE_BROWSE)
|
||||
else
|
||||
activity?.startActivityForResult(intent, FILE_BROWSE)
|
||||
} else {
|
||||
showBrowser = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Enable to start OPEN_INTENTS_FILE_BROWSE", e)
|
||||
showBrowser = true
|
||||
}
|
||||
|
||||
return showBrowser
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the specified action can be used as an intent. This
|
||||
* method queries the package manager for installed packages that can
|
||||
* respond to an intent with the specified action. If no suitable package is
|
||||
* found, this method returns false.
|
||||
*
|
||||
* @param context The application's environment.
|
||||
* @param action The Intent action to check for availability.
|
||||
*
|
||||
* @return True if an Intent with the specified action can be sent and
|
||||
* responded to, false otherwise.
|
||||
*/
|
||||
private fun isIntentAvailable(context: Context, action: String): Boolean {
|
||||
val packageManager = context.packageManager
|
||||
val intent = Intent(action)
|
||||
val list = packageManager.queryIntentActivities(intent,
|
||||
PackageManager.MATCH_DEFAULT_ONLY)
|
||||
return list.size > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Browser dialog to select file picker app
|
||||
*/
|
||||
private fun showBrowserDialog() {
|
||||
try {
|
||||
val fileManagerDialogFragment = FileManagerDialogFragment()
|
||||
fragment?.let {
|
||||
fileManagerDialogFragment.show(it.parentFragmentManager, "browserDialog")
|
||||
} ?: fileManagerDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Can't open BrowserDialog", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To use in onActivityResultCallback in Fragment or Activity
|
||||
* @param keyFileCallback Callback retrieve from data
|
||||
* @return true if requestCode was captured, false elsechere
|
||||
*/
|
||||
fun onActivityResultCallback(
|
||||
requestCode: Int,
|
||||
resultCode: Int,
|
||||
data: Intent?,
|
||||
fun onActivityResultCallback(requestCode: Int, resultCode: Int, data: Intent?,
|
||||
keyFileCallback: ((uri: Uri?) -> Unit)?): Boolean {
|
||||
|
||||
when (requestCode) {
|
||||
@@ -231,14 +148,102 @@ class SelectFileHelper {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Browser dialog to select file picker app
|
||||
*/
|
||||
private fun showFileManagerDialogFragment() {
|
||||
try {
|
||||
if (fragment != null) {
|
||||
fragment?.parentFragmentManager
|
||||
} else {
|
||||
activity?.supportFragmentManager
|
||||
}?.let { fragmentManager ->
|
||||
FileManagerDialogFragment().show(fragmentManager, "browserDialog")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Can't open BrowserDialog", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun createDocument(titleString: String,
|
||||
typeString: String = "application/octet-stream"): Int? {
|
||||
val idCode = getUnusedCreateFileRequestCode()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = typeString
|
||||
putExtra(Intent.EXTRA_TITLE, titleString)
|
||||
}
|
||||
if (fragment != null)
|
||||
fragment?.startActivityForResult(intent, idCode)
|
||||
else
|
||||
activity?.startActivityForResult(intent, idCode)
|
||||
return idCode
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to create document", e)
|
||||
showFileManagerDialogFragment()
|
||||
}
|
||||
} else {
|
||||
showFileManagerDialogFragment()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
|
||||
action: (fileCreated: Uri?)->Unit) {
|
||||
// Retrieve the created URI from the file manager
|
||||
if (fileRequestCodes.contains(requestCode) && resultCode == RESULT_OK) {
|
||||
action.invoke(data?.data)
|
||||
fileRequestCodes.remove(requestCode)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG = "OpenFileHelper"
|
||||
|
||||
const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE"
|
||||
|
||||
private const val GET_CONTENT = 25745
|
||||
private const val OPEN_DOC = 25845
|
||||
private const val FILE_BROWSE = 25645
|
||||
|
||||
private var CREATE_FILE_REQUEST_CODE_DEFAULT = 3853
|
||||
private var fileRequestCodes = ArrayList<Int>()
|
||||
|
||||
private fun getUnusedCreateFileRequestCode(): Int {
|
||||
val newCreateFileRequestCode = CREATE_FILE_REQUEST_CODE_DEFAULT++
|
||||
fileRequestCodes.add(newCreateFileRequestCode)
|
||||
return newCreateFileRequestCode
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
fun allowCreateDocumentByStorageAccessFramework(packageManager: PackageManager,
|
||||
typeString: String = "application/octet-stream"): Boolean {
|
||||
return when {
|
||||
// To check if a custom file manager can manage the ACTION_CREATE_DOCUMENT
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> {
|
||||
packageManager.queryIntentActivities(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = typeString
|
||||
}, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty()
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun View.setOpenDocumentClickListener(externalFileHelper: ExternalFileHelper?) {
|
||||
externalFileHelper?.let { fileHelper ->
|
||||
setOnClickListener {
|
||||
fileHelper.openDocument()
|
||||
}
|
||||
setOnLongClickListener {
|
||||
fileHelper.openDocument(true)
|
||||
true
|
||||
}
|
||||
} ?: kotlin.run {
|
||||
setOnClickListener(null)
|
||||
setOnLongClickListener(null)
|
||||
}
|
||||
}
|
||||
@@ -105,10 +105,6 @@ class Database {
|
||||
return mDatabaseKDB?.binaryCache ?: mDatabaseKDBX?.binaryCache ?: BinaryCache()
|
||||
}
|
||||
|
||||
fun setCacheDirectory(cacheDirectory: File) {
|
||||
binaryCache.cacheDirectory = cacheDirectory
|
||||
}
|
||||
|
||||
private val iconsManager: IconsManager
|
||||
get() {
|
||||
return mDatabaseKDB?.iconsManager ?: mDatabaseKDBX?.iconsManager ?: IconsManager(binaryCache)
|
||||
|
||||
@@ -466,16 +466,7 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Entry> {
|
||||
override fun createFromParcel(parcel: Parcel): Entry {
|
||||
return Entry(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Entry?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PMS_TAN_ENTRY = "<TAN>"
|
||||
|
||||
/**
|
||||
@@ -484,5 +475,16 @@ class Entry : Node, EntryVersionedInterface<Group> {
|
||||
fun newExtraFieldNameAllowed(field: Field): Boolean {
|
||||
return EntryKDBX.newCustomNameAllowed(field.name)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<Entry> = object : Parcelable.Creator<Entry> {
|
||||
override fun createFromParcel(parcel: Parcel): Entry {
|
||||
return Entry(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Entry?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class BinaryCache {
|
||||
*/
|
||||
var loadedCipherKey: LoadedKey = LoadedKey.generateNewCipherKey()
|
||||
|
||||
lateinit var cacheDirectory: File
|
||||
var cacheDirectory: File? = null
|
||||
|
||||
private val voidBinary = KeyByteArray(UNKNOWN, ByteArray(0))
|
||||
|
||||
@@ -19,11 +19,12 @@ class BinaryCache {
|
||||
smallSize: Boolean = false,
|
||||
compression: Boolean = false,
|
||||
protection: Boolean = false): BinaryData {
|
||||
return if (smallSize) {
|
||||
val cacheDir = cacheDirectory
|
||||
return if (smallSize || cacheDir == null) {
|
||||
BinaryByte(binaryId, compression, protection)
|
||||
} else {
|
||||
val fileInCache = File(cacheDirectory, binaryId)
|
||||
return BinaryFile(fileInCache, compression, protection)
|
||||
val fileInCache = File(cacheDir, binaryId)
|
||||
BinaryFile(fileInCache, compression, protection)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,8 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
url = parcel.readString() ?: url
|
||||
notes = parcel.readString() ?: notes
|
||||
binaryDescription = parcel.readString() ?: binaryDescription
|
||||
binaryDataId = parcel.readInt()
|
||||
val rawBinaryDataId = parcel.readInt()
|
||||
binaryDataId = if (rawBinaryDataId == -1) null else rawBinaryDataId
|
||||
}
|
||||
|
||||
override fun readParentParcelable(parcel: Parcel): GroupKDB? {
|
||||
@@ -109,9 +110,7 @@ class EntryKDB : EntryVersioned<Int, UUID, GroupKDB, EntryKDB>, NodeKDBInterface
|
||||
dest.writeString(url)
|
||||
dest.writeString(notes)
|
||||
dest.writeString(binaryDescription)
|
||||
binaryDataId?.let {
|
||||
dest.writeInt(it)
|
||||
}
|
||||
dest.writeInt(binaryDataId ?: -1)
|
||||
}
|
||||
|
||||
fun updateWith(source: EntryKDB) {
|
||||
|
||||
@@ -56,32 +56,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
var additional = ""
|
||||
var tags = ""
|
||||
|
||||
fun getSize(attachmentPool: AttachmentPool): Long {
|
||||
var size = FIXED_LENGTH_SIZE
|
||||
|
||||
for (entry in fields.entries) {
|
||||
size += entry.key.length.toLong()
|
||||
size += entry.value.length().toLong()
|
||||
}
|
||||
|
||||
size += getAttachmentsSize(attachmentPool)
|
||||
|
||||
size += autoType.defaultSequence.length.toLong()
|
||||
for ((key, value) in autoType.entrySet()) {
|
||||
size += key.length.toLong()
|
||||
size += value.length.toLong()
|
||||
}
|
||||
|
||||
for (entry in history) {
|
||||
size += entry.getSize(attachmentPool)
|
||||
}
|
||||
|
||||
size += overrideURL.length.toLong()
|
||||
size += tags.length.toLong()
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
override var expires: Boolean = false
|
||||
|
||||
constructor() : super()
|
||||
@@ -102,6 +76,14 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
tags = parcel.readString() ?: tags
|
||||
}
|
||||
|
||||
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
||||
return parcel.readParcelable(GroupKDBX::class.java.classLoader)
|
||||
}
|
||||
|
||||
override fun writeParentParcelable(parent: GroupKDBX?, parcel: Parcel, flags: Int) {
|
||||
parcel.writeParcelable(parent, flags)
|
||||
}
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
super.writeToParcel(dest, flags)
|
||||
dest.writeLong(usageCount.toKotlinLong())
|
||||
@@ -164,14 +146,6 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
return NodeIdUUID(nodeId.id)
|
||||
}
|
||||
|
||||
override fun readParentParcelable(parcel: Parcel): GroupKDBX? {
|
||||
return parcel.readParcelable(GroupKDBX::class.java.classLoader)
|
||||
}
|
||||
|
||||
override fun writeParentParcelable(parent: GroupKDBX?, parcel: Parcel, flags: Int) {
|
||||
parcel.writeParcelable(parent, flags)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a reference key with the FieldReferencesEngine
|
||||
* @param decodeRef
|
||||
@@ -228,6 +202,32 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
|
||||
override var locationChanged = DateInstant()
|
||||
|
||||
fun getSize(attachmentPool: AttachmentPool): Long {
|
||||
var size = FIXED_LENGTH_SIZE
|
||||
|
||||
for (entry in fields.entries) {
|
||||
size += entry.key.length.toLong()
|
||||
size += entry.value.length().toLong()
|
||||
}
|
||||
|
||||
size += getAttachmentsSize(attachmentPool)
|
||||
|
||||
size += autoType.defaultSequence.length.toLong()
|
||||
for ((key, value) in autoType.entrySet()) {
|
||||
size += key.length.toLong()
|
||||
size += value.length.toLong()
|
||||
}
|
||||
|
||||
for (entry in history) {
|
||||
size += entry.getSize(attachmentPool)
|
||||
}
|
||||
|
||||
size += overrideURL.length.toLong()
|
||||
size += tags.length.toLong()
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
fun afterChangeParent() {
|
||||
locationChanged = DateInstant()
|
||||
}
|
||||
@@ -349,6 +349,8 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
const val STR_URL = "URL"
|
||||
const val STR_NOTES = "Notes"
|
||||
|
||||
private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size
|
||||
|
||||
fun newCustomNameAllowed(name: String): Boolean {
|
||||
return !(name.equals(STR_TITLE, true)
|
||||
|| name.equals(STR_USERNAME, true)
|
||||
@@ -367,7 +369,5 @@ class EntryKDBX : EntryVersioned<UUID, UUID, GroupKDBX, EntryKDBX>, NodeKDBXInte
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ abstract class EntryVersioned
|
||||
|
||||
constructor(parcel: Parcel) : super(parcel)
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
super.writeToParcel(dest, flags)
|
||||
}
|
||||
|
||||
override fun nodeIndexInParentForNaturalOrder(): Int {
|
||||
if (nodeIndexInParentForNaturalOrder == -1) {
|
||||
val numberOfGroups = parent?.getChildGroups()?.size
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.icon
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class IconImage() : IconImageDraw(), Parcelable {
|
||||
class IconImage() : IconImageDraw() {
|
||||
|
||||
var standard: IconImageStandard = IconImageStandard()
|
||||
var custom: IconImageCustom = IconImageCustom()
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.database.element.database.DatabaseVersioned
|
||||
import java.util.*
|
||||
|
||||
class IconImageCustom : Parcelable, IconImageDraw {
|
||||
class IconImageCustom : IconImageDraw {
|
||||
|
||||
var uuid: UUID
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.database.element.icon
|
||||
|
||||
abstract class IconImageDraw {
|
||||
import android.os.Parcelable
|
||||
|
||||
abstract class IconImageDraw : Parcelable {
|
||||
|
||||
var selected = false
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import com.kunzisoft.keepass.icons.IconPack.Companion.NB_ICONS
|
||||
|
||||
class IconImageStandard : Parcelable, IconImageDraw {
|
||||
class IconImageStandard : IconImageDraw {
|
||||
|
||||
val id: Int
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.net.Uri
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
@@ -34,6 +33,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 kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
@@ -173,7 +173,8 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
putExtra(FILE_URI_KEY, attachmentNotification.uri)
|
||||
}, PendingIntent.FLAG_CANCEL_CURRENT)
|
||||
|
||||
val fileName = DocumentFile.fromSingleUri(this, attachmentNotification.uri)?.name ?: ""
|
||||
val fileName = UriUtil.getFileData(this, attachmentNotification.uri)?.name
|
||||
?: attachmentNotification.uri.path
|
||||
|
||||
val builder = buildNewNotification().apply {
|
||||
when (attachmentNotification.entryAttachmentState.streamDirection) {
|
||||
|
||||
@@ -1,86 +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
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment
|
||||
|
||||
|
||||
private var CREATE_FILE_REQUEST_CODE_DEFAULT = 3853
|
||||
private var fileRequestCodes = ArrayList<Int>()
|
||||
|
||||
fun getUnusedCreateFileRequestCode(): Int {
|
||||
val newCreateFileRequestCode = CREATE_FILE_REQUEST_CODE_DEFAULT++
|
||||
fileRequestCodes.add(newCreateFileRequestCode)
|
||||
return newCreateFileRequestCode
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
fun allowCreateDocumentByStorageAccessFramework(packageManager: PackageManager): Boolean {
|
||||
return when {
|
||||
// To check if a custom file manager can manage the ACTION_CREATE_DOCUMENT
|
||||
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> {
|
||||
packageManager.queryIntentActivities(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/x-keepass"
|
||||
}, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty()
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
fun createDocument(activity: FragmentActivity,
|
||||
titleString: String,
|
||||
typeString: String = "application/octet-stream"): Int? {
|
||||
|
||||
val idCode = getUnusedCreateFileRequestCode()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
try {
|
||||
activity.startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = typeString
|
||||
putExtra(Intent.EXTRA_TITLE, titleString)
|
||||
}, idCode)
|
||||
return idCode
|
||||
} catch (e: Exception) {
|
||||
FileManagerDialogFragment().show(activity.supportFragmentManager, "browserDialog")
|
||||
}
|
||||
} else {
|
||||
FileManagerDialogFragment().show(activity.supportFragmentManager, "browserDialog")
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
|
||||
action: (fileCreated: Uri?)->Unit) {
|
||||
// Retrieve the created URI from the file manager
|
||||
if (fileRequestCodes.contains(requestCode) && resultCode == Activity.RESULT_OK) {
|
||||
action.invoke(data?.data)
|
||||
fileRequestCodes.remove(requestCode)
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,8 @@ object UriUtil {
|
||||
fun getFileData(context: Context, fileUri: Uri?): DocumentFile? {
|
||||
if (fileUri == null)
|
||||
return null
|
||||
return when {
|
||||
return try {
|
||||
when {
|
||||
isFileScheme(fileUri) -> {
|
||||
fileUri.path?.let {
|
||||
File(it).let { file ->
|
||||
@@ -46,8 +47,17 @@ object UriUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
isContentScheme(fileUri) -> DocumentFile.fromSingleUri(context, fileUri)
|
||||
else -> null
|
||||
isContentScheme(fileUri) -> {
|
||||
DocumentFile.fromSingleUri(context, fileUri)
|
||||
}
|
||||
else -> {
|
||||
Log.e("FileData", "Content scheme not known")
|
||||
null
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("FileData", "Unable to get document file", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
class KeyFileSelectionView @JvmOverloads constructor(context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@@ -51,7 +51,7 @@ class KeyFileSelectionView @JvmOverloads constructor(context: Context,
|
||||
set(value) {
|
||||
mUri = value
|
||||
keyFileNameView.text = value?.let {
|
||||
DocumentFile.fromSingleUri(context, value)?.name ?: value.path
|
||||
UriUtil.getFileData(context, value)?.name ?: value.path
|
||||
} ?: ""
|
||||
}
|
||||
}
|
||||
@@ -50,14 +50,18 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun loadListOfDatabases() {
|
||||
checkDefaultDatabase()
|
||||
mFileDatabaseHistoryAction?.getDatabaseFileList { databaseFileListRetrieved ->
|
||||
private fun getDatabaseFilesLoadedValue(): DatabaseFileData {
|
||||
var newValue = databaseFilesLoaded.value
|
||||
if (newValue == null) {
|
||||
newValue = DatabaseFileData()
|
||||
}
|
||||
newValue.apply {
|
||||
return newValue
|
||||
}
|
||||
|
||||
fun loadListOfDatabases() {
|
||||
checkDefaultDatabase()
|
||||
mFileDatabaseHistoryAction?.getDatabaseFileList { databaseFileListRetrieved ->
|
||||
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
|
||||
databaseFileAction = DatabaseFileAction.NONE
|
||||
databaseFileToActivate = null
|
||||
databaseFileList.apply {
|
||||
@@ -65,14 +69,13 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
|
||||
addAll(databaseFileListRetrieved)
|
||||
}
|
||||
}
|
||||
databaseFilesLoaded.value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
fun addDatabaseFile(databaseUri: Uri, keyFileUri: Uri?) {
|
||||
mFileDatabaseHistoryAction?.addOrUpdateDatabaseUri(databaseUri, keyFileUri) { databaseFileAdded ->
|
||||
databaseFileAdded?.let { _ ->
|
||||
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply {
|
||||
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
|
||||
this.databaseFileAction = DatabaseFileAction.ADD
|
||||
this.databaseFileList.add(databaseFileAdded)
|
||||
this.databaseFileToActivate = databaseFileAdded
|
||||
@@ -84,7 +87,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
|
||||
fun updateDatabaseFile(databaseFileToUpdate: DatabaseFile) {
|
||||
mFileDatabaseHistoryAction?.addOrUpdateDatabaseFile(databaseFileToUpdate) { databaseFileUpdated ->
|
||||
databaseFileUpdated?.let { _ ->
|
||||
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply {
|
||||
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
|
||||
this.databaseFileAction = DatabaseFileAction.UPDATE
|
||||
this.databaseFileList
|
||||
.find { it.databaseUri == databaseFileUpdated.databaseUri }
|
||||
@@ -104,7 +107,7 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic
|
||||
fun deleteDatabaseFile(databaseFileToDelete: DatabaseFile) {
|
||||
mFileDatabaseHistoryAction?.deleteDatabaseFile(databaseFileToDelete) { databaseFileDeleted ->
|
||||
databaseFileDeleted?.let { _ ->
|
||||
databaseFilesLoaded.value = databaseFilesLoaded.value?.apply {
|
||||
databaseFilesLoaded.value = getDatabaseFilesLoadedValue().apply {
|
||||
databaseFileAction = DatabaseFileAction.DELETE
|
||||
databaseFileToActivate = databaseFileDeleted
|
||||
databaseFileList.remove(databaseFileDeleted)
|
||||
|
||||
1
fastlane/metadata/android/en-US/changelogs/69.txt
Normal file
1
fastlane/metadata/android/en-US/changelogs/69.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Fix small bugs #948
|
||||
1
fastlane/metadata/android/fr-FR/changelogs/69.txt
Normal file
1
fastlane/metadata/android/fr-FR/changelogs/69.txt
Normal file
@@ -0,0 +1 @@
|
||||
* Correction de petits bugs #948
|
||||
Reference in New Issue
Block a user