From bb3620680b11a75176993d81de1f2b8be16d6b31 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 31 Mar 2021 17:58:49 +0200 Subject: [PATCH 01/25] Upgrade version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4f36d745b..b1c3d3c76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 15 targetSdkVersion 30 - versionCode = 68 - versionName = "2.9.15" + versionCode = 69 + versionName = "2.9.16" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" From 78d3b369bbdd860328a6bf704b5cc09c7f73d34d Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 31 Mar 2021 19:39:07 +0200 Subject: [PATCH 02/25] Move Parcelable inheritance --- .../com/kunzisoft/keepass/database/element/icon/IconImage.kt | 2 +- .../keepass/database/element/icon/IconImageCustom.kt | 2 +- .../kunzisoft/keepass/database/element/icon/IconImageDraw.kt | 4 +++- .../keepass/database/element/icon/IconImageStandard.kt | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImage.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImage.kt index 17d59cb1f..da9e47901 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImage.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImage.kt @@ -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() diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt index 514222fe9..5cc43c2c3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageCustom.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageDraw.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageDraw.kt index 978999f39..2e5e2c9da 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageDraw.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageDraw.kt @@ -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 /** diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageStandard.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageStandard.kt index bc5ea8543..b60990f00 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageStandard.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/icon/IconImageStandard.kt @@ -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 From 7fc2d95886ba2daf4bf448c99af3c69a39bbdbc6 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 1 Apr 2021 10:52:00 +0200 Subject: [PATCH 03/25] Fix document file retrievment --- .../AttachmentFileNotificationService.kt | 5 ++-- .../com/kunzisoft/keepass/utils/UriUtil.kt | 24 +++++++++++++------ .../keepass/view/KeyFileSelectionView.kt | 4 ++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt index 9de745620..ee65797df 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/AttachmentFileNotificationService.kt @@ -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) { diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt index c8ca7cae1..7c4814593 100644 --- a/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/utils/UriUtil.kt @@ -38,16 +38,26 @@ object UriUtil { fun getFileData(context: Context, fileUri: Uri?): DocumentFile? { if (fileUri == null) return null - return when { - isFileScheme(fileUri) -> { - fileUri.path?.let { - File(it).let { file -> - return DocumentFile.fromFile(file) + return try { + when { + isFileScheme(fileUri) -> { + fileUri.path?.let { + File(it).let { file -> + return DocumentFile.fromFile(file) + } } } + isContentScheme(fileUri) -> { + DocumentFile.fromSingleUri(context, fileUri) + } + else -> { + Log.e("FileData", "Content scheme not known") + null + } } - isContentScheme(fileUri) -> DocumentFile.fromSingleUri(context, fileUri) - else -> null + } catch (e: Exception) { + Log.e("FileData", "Unable to get document file", e) + null } } diff --git a/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt b/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt index 1dc1f370d..3bf85978a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt +++ b/app/src/main/java/com/kunzisoft/keepass/view/KeyFileSelectionView.kt @@ -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 } ?: "" } } \ No newline at end of file From 7b5e9d2344b46d92715ae85982995b70cbdfe2ef Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Fri, 2 Apr 2021 09:37:18 +0200 Subject: [PATCH 04/25] Better parcelable entry CREATOR implementation #948 --- CHANGELOG | 3 +++ .../keepass/database/element/Entry.kt | 22 ++++++++++--------- .../database/element/entry/EntryKDBX.kt | 4 ++-- .../metadata/android/en-US/changelogs/69.txt | 1 + .../metadata/android/fr-FR/changelogs/69.txt | 1 + 5 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/69.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/69.txt diff --git a/CHANGELOG b/CHANGELOG index 2dd84bfbe..395b8c9ae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt index 97a87df9e..3a21eb135 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Entry.kt @@ -466,16 +466,7 @@ class Entry : Node, EntryVersionedInterface { return result } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): Entry { - return Entry(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - + companion object { const val PMS_TAN_ENTRY = "" /** @@ -484,5 +475,16 @@ class Entry : Node, EntryVersionedInterface { fun newExtraFieldNameAllowed(field: Field): Boolean { return EntryKDBX.newCustomNameAllowed(field.name) } + + @JvmField + val CREATOR: Parcelable.Creator = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Entry { + return Entry(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index a2aec53f3..137fa861d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -349,6 +349,8 @@ class EntryKDBX : EntryVersioned, 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, NodeKDBXInte return arrayOfNulls(size) } } - - private const val FIXED_LENGTH_SIZE: Long = 128 // Approximate fixed length size } } diff --git a/fastlane/metadata/android/en-US/changelogs/69.txt b/fastlane/metadata/android/en-US/changelogs/69.txt new file mode 100644 index 000000000..04ae3b1c9 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/69.txt @@ -0,0 +1 @@ + * Fix small bugs #948 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/69.txt b/fastlane/metadata/android/fr-FR/changelogs/69.txt new file mode 100644 index 000000000..9fcc87621 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/69.txt @@ -0,0 +1 @@ + * Correction de petits bugs #948 From d5fbc8393f38a514bb408e6613b4f2853389851b Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 3 Apr 2021 19:40:55 +0200 Subject: [PATCH 05/25] Change file creation methods --- .../keepass/activities/EntryActivity.kt | 10 +- .../keepass/activities/EntryEditActivity.kt | 12 +- .../activities/FileDatabaseSelectActivity.kt | 18 +-- .../keepass/activities/IconPickerActivity.kt | 12 +- .../keepass/activities/PasswordActivity.kt | 10 +- .../dialogs/AssignMasterKeyDialogFragment.kt | 13 +-- ...ectFileHelper.kt => ExternalFileHelper.kt} | 103 ++++++++++++++---- .../keepass/utils/ExternalFileManager.kt | 86 --------------- 8 files changed, 123 insertions(+), 141 deletions(-) rename app/src/main/java/com/kunzisoft/keepass/activities/helpers/{SelectFileHelper.kt => ExternalFileHelper.kt} (71%) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/utils/ExternalFileManager.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt index 87b9de595..a6090f132 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -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 diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index d5d2dd9f8..3661cf08a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -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() @@ -241,7 +241,7 @@ class EntryEditActivity : LockingActivity(), } // To retrieve attachment - mSelectFileHelper = SelectFileHelper(this) + mExternalFileHelper = ExternalFileHelper(this) mAttachmentFileBinderManager = AttachmentFileBinderManager(this) // Save button @@ -459,7 +459,7 @@ class EntryEditActivity : LockingActivity(), * Add a new attachment */ private fun addNewAttachment(item: MenuItem) { - mSelectFileHelper?.selectFileOnClickViewListener?.onMenuItemClick(item) + mExternalFileHelper?.selectFileOnClickViewListener?.onMenuItemClick(item) } 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?.selectFileOnClickViewListener?.onClick(attachmentView) }, { performedNextEducation(entryEditActivityEducation) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 83dd275b1..56a31d432 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -42,7 +42,7 @@ 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.selection.SpecialModeActivity import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter @@ -82,7 +82,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,10 +103,10 @@ 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 { + mExternalFileHelper?.selectFileOnClickViewListener?.let { setOnClickListener(it) setOnLongClickListener(it) } @@ -234,7 +234,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 +286,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 +359,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 +414,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), openDatabaseButtonView!!, {tapTargetView -> tapTargetView?.let { - mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it) + mExternalFileHelper?.selectFileOnClickViewListener?.onClick(it) } }, {} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 1cd3e76ea..939bc5b75 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -34,7 +34,7 @@ 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.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged import com.kunzisoft.keepass.database.element.Database @@ -66,7 +66,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) @@ -87,10 +87,10 @@ class IconPickerActivity : LockingActivity() { uploadButton = findViewById(R.id.icon_picker_upload) if (mDatabase?.allowCustomIcons == true) { uploadButton.setOnClickListener { - mSelectFileHelper?.selectFileOnClickViewListener?.onClick(it) + mExternalFileHelper?.selectFileOnClickViewListener?.onClick(it) } uploadButton.setOnLongClickListener { - mSelectFileHelper?.selectFileOnClickViewListener?.onLongClick(it) + mExternalFileHelper?.selectFileOnClickViewListener?.onLongClick(it) true } } else { @@ -124,7 +124,7 @@ class IconPickerActivity : LockingActivity() { // Focus view to reinitialize timeout findViewById(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this) - mSelectFileHelper = SelectFileHelper(this) + mExternalFileHelper = ExternalFileHelper(this) iconPickerViewModel.standardIconPicked.observe(this) { iconStandard -> mIconImage.standard = iconStandard @@ -281,7 +281,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) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index df62f36b5..7b497854e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -44,7 +44,7 @@ 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.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.SpecialMode import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.selection.SpecialModeActivity @@ -95,7 +95,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,9 +138,9 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState) mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) - mSelectFileHelper = SelectFileHelper(this@PasswordActivity) + mExternalFileHelper = ExternalFileHelper(this@PasswordActivity) keyFileSelectionView?.apply { - mSelectFileHelper?.selectFileOnClickViewListener?.let { + mExternalFileHelper?.selectFileOnClickViewListener?.let { setOnClickListener(it) setOnLongClickListener(it) } @@ -702,7 +702,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil } var keyFileResult = false - mSelectFileHelper?.let { + mExternalFileHelper?.let { keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data ) { uri -> if (uri != null) { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index b9a857688..cba487782 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -30,13 +30,12 @@ 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.model.MainCredential import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.KeyFileSelectionView @@ -60,7 +59,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,10 +132,10 @@ class AssignMasterKeyDialogFragment : DialogFragment() { keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox) keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection) - mSelectFileHelper = SelectFileHelper(this) + mExternalFileHelper = ExternalFileHelper(this) keyFileSelectionView?.apply { - setOnClickListener(mSelectFileHelper?.selectFileOnClickViewListener) - setOnLongClickListener(mSelectFileHelper?.selectFileOnClickViewListener) + setOnClickListener(mExternalFileHelper?.selectFileOnClickViewListener) + setOnLongClickListener(mExternalFileHelper?.selectFileOnClickViewListener) } val dialog = builder.create() @@ -289,7 +288,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 diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/SelectFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt similarity index 71% rename from app/src/main/java/com/kunzisoft/keepass/activities/helpers/SelectFileHelper.kt rename to app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index eca32bd60..0fcbc5a65 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/SelectFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -20,7 +20,6 @@ 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 @@ -35,15 +34,15 @@ 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 } @@ -60,18 +59,10 @@ class SelectFileHelper { private fun onAbstractClick(longClick: Boolean = false) { try { - if (longClick) { - try { - openActivityWithActionGetContent() - } catch (e: Exception) { - openActivityWithActionOpenDocument() - } - } else { - try { - openActivityWithActionOpenDocument() - } catch (e: Exception) { - openActivityWithActionGetContent() - } + try { + openDocument(longClick) + } catch (e: Exception) { + openDocument(!longClick) } } catch (e: Exception) { Log.e(TAG, "Enable to start the file picker activity", e) @@ -96,6 +87,14 @@ class SelectFileHelper { } } + fun openDocument(getContent: Boolean) { + if (getContent) { + openActivityWithActionGetContent() + } else { + openActivityWithActionOpenDocument() + } + } + @SuppressLint("InlinedApi") private fun openActivityWithActionOpenDocument() { val intentOpenDocument = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { @@ -187,10 +186,7 @@ class SelectFileHelper { * @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,6 +227,50 @@ class SelectFileHelper { return false } + private fun showFileManagerDialogFragment() { + if (fragment != null) { + fragment?.parentFragmentManager + } else { + activity?.supportFragmentManager + }?.let { fragmentManager -> + FileManagerDialogFragment().show(fragmentManager, "browserDialog") + } + } + + 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) { + 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" @@ -240,5 +280,28 @@ class SelectFileHelper { 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() + + private 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 + } + } } } diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/ExternalFileManager.kt b/app/src/main/java/com/kunzisoft/keepass/utils/ExternalFileManager.kt deleted file mode 100644 index c42d25127..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/utils/ExternalFileManager.kt +++ /dev/null @@ -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 . - * - */ -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() - -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) - } -} \ No newline at end of file From 818b9751116f54b9774cb51df12dbe4ecda40ad3 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 4 Apr 2021 09:56:09 +0200 Subject: [PATCH 06/25] Change default type verification to create document --- .../keepass/activities/FileDatabaseSelectActivity.kt | 3 ++- .../keepass/activities/helpers/ExternalFileHelper.kt | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 56a31d432..9ecc51856 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -286,7 +286,8 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), // Show open and create button or special mode when (mSpecialMode) { SpecialMode.DEFAULT -> { - if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager)) { + if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager, + "application/x-keepass")) { // There is an activity which can handle this intent. createDatabaseButtonView?.visibility = View.VISIBLE } else{ diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index 0fcbc5a65..07dc461fc 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -291,13 +291,14 @@ class ExternalFileHelper { } @SuppressLint("InlinedApi") - fun allowCreateDocumentByStorageAccessFramework(packageManager: PackageManager): Boolean { + 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 = "application/x-keepass" + type = typeString }, PackageManager.MATCH_DEFAULT_ONLY).isNotEmpty() } else -> true From b864c39a0d49f62c51edcdbc2107c7c0d8de7817 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 4 Apr 2021 23:13:24 +0200 Subject: [PATCH 07/25] Fix add database workflow in some devices --- .../activities/FileDatabaseSelectActivity.kt | 6 +++--- .../viewmodels/DatabaseFilesViewModel.kt | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 9ecc51856..c0d093e43 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -171,8 +171,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 +183,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 +204,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() diff --git a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt index a69d42895..5e066dd08 100644 --- a/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt +++ b/app/src/main/java/com/kunzisoft/keepass/viewmodels/DatabaseFilesViewModel.kt @@ -50,14 +50,18 @@ class DatabaseFilesViewModel(application: Application) : AndroidViewModel(applic ).execute() } + private fun getDatabaseFilesLoadedValue(): DatabaseFileData { + var newValue = databaseFilesLoaded.value + if (newValue == null) { + newValue = DatabaseFileData() + } + return newValue + } + fun loadListOfDatabases() { checkDefaultDatabase() mFileDatabaseHistoryAction?.getDatabaseFileList { databaseFileListRetrieved -> - var newValue = databaseFilesLoaded.value - if (newValue == null) { - newValue = DatabaseFileData() - } - newValue.apply { + 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) From 43d6c76873b78fdeb3278c16052b42c3ab53a7dd Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 4 Apr 2021 23:44:25 +0200 Subject: [PATCH 08/25] Refactor open document click listener --- .../keepass/activities/EntryEditActivity.kt | 8 +-- .../activities/FileDatabaseSelectActivity.kt | 12 ++-- .../keepass/activities/IconPickerActivity.kt | 9 +-- .../keepass/activities/PasswordActivity.kt | 12 +--- .../dialogs/AssignMasterKeyDialogFragment.kt | 6 +- .../activities/helpers/ExternalFileHelper.kt | 56 ++++++------------- 6 files changed, 30 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index 3661cf08a..1e7c7eafb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -458,8 +458,8 @@ class EntryEditActivity : LockingActivity(), /** * Add a new attachment */ - private fun addNewAttachment(item: MenuItem) { - mExternalFileHelper?.selectFileOnClickViewListener?.onMenuItemClick(item) + private fun addNewAttachment() { + mExternalFileHelper?.openDocument() } override fun onValidateUploadFileTooBig(attachmentToUploadUri: Uri?, fileName: String?) { @@ -655,7 +655,7 @@ class EntryEditActivity : LockingActivity(), && entryEditActivityEducation.checkAndPerformedAttachmentEducation( attachmentView, { - mExternalFileHelper?.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 -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index c0d093e43..c7d754cbb 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -44,6 +44,7 @@ import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper 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 @@ -105,12 +106,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), // Open database button mExternalFileHelper = ExternalFileHelper(this) openDatabaseButtonView = findViewById(R.id.open_keyfile_button) - openDatabaseButtonView?.apply { - mExternalFileHelper?.selectFileOnClickViewListener?.let { - setOnClickListener(it) - setOnLongClickListener(it) - } - } + openDatabaseButtonView?.setOpenDocumentClickListener(mExternalFileHelper) // History list val fileDatabaseHistoryRecyclerView = findViewById(R.id.file_list) @@ -413,9 +409,9 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), openDatabaseButtonView != null && fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation( openDatabaseButtonView!!, - {tapTargetView -> + { tapTargetView -> tapTargetView?.let { - mExternalFileHelper?.selectFileOnClickViewListener?.onClick(it) + mExternalFileHelper?.openDocument() } }, {} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 939bc5b75..3ee055e7d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -35,6 +35,7 @@ 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.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 @@ -86,13 +87,7 @@ class IconPickerActivity : LockingActivity() { uploadButton = findViewById(R.id.icon_picker_upload) if (mDatabase?.allowCustomIcons == true) { - uploadButton.setOnClickListener { - mExternalFileHelper?.selectFileOnClickViewListener?.onClick(it) - } - uploadButton.setOnLongClickListener { - mExternalFileHelper?.selectFileOnClickViewListener?.onLongClick(it) - true - } + uploadButton.setOpenDocumentClickListener(mExternalFileHelper) } else { uploadButton.visibility = View.GONE } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index 7b497854e..c65a9f509 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -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.ExternalFileHelper -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 @@ -139,12 +136,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this) mExternalFileHelper = ExternalFileHelper(this@PasswordActivity) - keyFileSelectionView?.apply { - mExternalFileHelper?.selectFileOnClickViewListener?.let { - setOnClickListener(it) - setOnLongClickListener(it) - } - } + keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper) passwordView?.setOnEditorActionListener(onEditorActionListener) passwordView?.addTextChangedListener(object : TextWatcher { diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index cba487782..1c967f9be 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -36,6 +36,7 @@ import androidx.fragment.app.DialogFragment 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.model.MainCredential import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.view.KeyFileSelectionView @@ -133,10 +134,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() { keyFileSelectionView = rootView?.findViewById(R.id.keyfile_selection) mExternalFileHelper = ExternalFileHelper(this) - keyFileSelectionView?.apply { - setOnClickListener(mExternalFileHelper?.selectFileOnClickViewListener) - setOnLongClickListener(mExternalFileHelper?.selectFileOnClickViewListener) - } + keyFileSelectionView?.setOpenDocumentClickListener(mExternalFileHelper) val dialog = builder.create() diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index 07dc461fc..ce68e7628 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -27,7 +27,6 @@ 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.fragment.app.Fragment import androidx.fragment.app.FragmentActivity @@ -39,9 +38,6 @@ class ExternalFileHelper { private var activity: FragmentActivity? = null private var fragment: Fragment? = null - val selectFileOnClickViewListener: SelectFileOnClickViewListener - get() = SelectFileOnClickViewListener() - constructor(context: FragmentActivity) { this.activity = context this.fragment = null @@ -52,42 +48,7 @@ class ExternalFileHelper { this.fragment = context } - inner class SelectFileOnClickViewListener : - View.OnClickListener, - View.OnLongClickListener, - MenuItem.OnMenuItemClickListener { - - private fun onAbstractClick(longClick: Boolean = false) { - try { - try { - openDocument(longClick) - } catch (e: Exception) { - openDocument(!longClick) - } - } catch (e: Exception) { - Log.e(TAG, "Enable to start the file picker activity", e) - // Open browser dialog - if (lookForOpenIntentsFilePicker()) - showBrowserDialog() - } - } - - 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 - } - } - - fun openDocument(getContent: Boolean) { + fun openDocument(getContent: Boolean = false) { if (getContent) { openActivityWithActionGetContent() } else { @@ -306,3 +267,18 @@ class ExternalFileHelper { } } } + +fun View.setOpenDocumentClickListener(externalFileHelper: ExternalFileHelper?) { + externalFileHelper?.let { fileHelper -> + setOnClickListener { + fileHelper.openDocument() + } + setOnLongClickListener { + fileHelper.openDocument(true) + true + } + } ?: kotlin.run { + setOnClickListener(null) + setOnLongClickListener(null) + } +} From 196620e1bd6a27ad1647c5466b21a8123e1725a8 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 5 Apr 2021 00:04:25 +0200 Subject: [PATCH 09/25] Remove unused methods --- .../activities/FileDatabaseSelectActivity.kt | 3 +- .../activities/helpers/ExternalFileHelper.kt | 117 ++++++------------ 2 files changed, 42 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index c7d754cbb..48c65ac61 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -282,8 +282,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), // Show open and create button or special mode when (mSpecialMode) { SpecialMode.DEFAULT -> { - if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager, - "application/x-keepass")) { + if (ExternalFileHelper.allowCreateDocumentByStorageAccessFramework(packageManager)) { // There is an activity which can handle this intent. createDatabaseButtonView?.visibility = View.VISIBLE } else{ diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index ce68e7628..1223acaec 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -21,13 +21,13 @@ package com.kunzisoft.keepass.activities.helpers import android.annotation.SuppressLint 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.View +import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import com.kunzisoft.keepass.activities.dialogs.FileManagerDialogFragment @@ -48,21 +48,33 @@ class ExternalFileHelper { this.fragment = context } - fun openDocument(getContent: Boolean = false) { - if (getContent) { - openActivityWithActionGetContent() + fun openDocument(getContent: Boolean = false, + typeString: String = "*/*") { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + if (getContent) { + openActivityWithActionGetContent(typeString) + } else { + openActivityWithActionOpenDocument(typeString) + } + } catch (e: Exception) { + Log.e(TAG, "Unable to open document", e) + showFileManagerDialogFragment() + } } else { - openActivityWithActionOpenDocument() + showFileManagerDialogFragment() } } - @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) - addFlags(Intent.FLAG_GRANT_PREFIX_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) } @@ -72,13 +84,15 @@ class ExternalFileHelper { 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) - addFlags(Intent.FLAG_GRANT_PREFIX_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) } @@ -88,60 +102,6 @@ class ExternalFileHelper { 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 @@ -188,19 +148,25 @@ class ExternalFileHelper { return false } + /** + * Show Browser dialog to select file picker app + */ private fun showFileManagerDialogFragment() { - if (fragment != null) { - fragment?.parentFragmentManager - } else { - activity?.supportFragmentManager - }?.let { fragmentManager -> - FileManagerDialogFragment().show(fragmentManager, "browserDialog") + 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 { @@ -215,6 +181,7 @@ class ExternalFileHelper { activity?.startActivityForResult(intent, idCode) return idCode } catch (e: Exception) { + Log.e(TAG, "Unable to create document", e) showFileManagerDialogFragment() } } else { @@ -236,8 +203,6 @@ class ExternalFileHelper { 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 From 0487dea7fc0e36a48aeea86a1a2cfad114496c0f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 5 Apr 2021 11:32:07 +0200 Subject: [PATCH 10/25] Fix null cache directory --- .../com/kunzisoft/keepass/database/element/Database.kt | 4 ---- .../keepass/database/element/binary/BinaryCache.kt | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt index 71bcc72fe..f297a4d2f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/Database.kt @@ -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) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryCache.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryCache.kt index d41509cb0..ff204439a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryCache.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/binary/BinaryCache.kt @@ -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) } } From 0f3ad7c8b11e04335db5e4e9376ecbaa490541ee Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Mon, 5 Apr 2021 19:06:54 +0200 Subject: [PATCH 11/25] Fix select custom icon --- .../com/kunzisoft/keepass/activities/IconPickerActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 3ee055e7d..119f0cba3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -85,6 +85,8 @@ 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.setOpenDocumentClickListener(mExternalFileHelper) @@ -119,8 +121,6 @@ class IconPickerActivity : LockingActivity() { // Focus view to reinitialize timeout findViewById(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this) - mExternalFileHelper = ExternalFileHelper(this) - iconPickerViewModel.standardIconPicked.observe(this) { iconStandard -> mIconImage.standard = iconStandard // Remove the custom icon if a standard one is selected From 09f6c181896066988afc00e5867bb6ad2c7550b5 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 6 Apr 2021 15:02:24 +0200 Subject: [PATCH 12/25] Small changes --- .../database/element/entry/EntryKDBX.kt | 52 +++++++++---------- .../database/element/entry/EntryVersioned.kt | 4 ++ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index 137fa861d..f03260a94 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -56,32 +56,6 @@ class EntryKDBX : EntryVersioned, 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() @@ -228,6 +202,32 @@ class EntryKDBX : EntryVersioned, 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() } diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryVersioned.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryVersioned.kt index 654b0a312..c0d13f821 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryVersioned.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryVersioned.kt @@ -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 From 59ead4986f913bd99347c2a5af01c95a2adf4860 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 6 Apr 2021 15:05:11 +0200 Subject: [PATCH 13/25] Move Parent Parcelable --- .../keepass/database/element/entry/EntryKDBX.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt index f03260a94..772887d61 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDBX.kt @@ -76,6 +76,14 @@ class EntryKDBX : EntryVersioned, 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()) @@ -138,14 +146,6 @@ class EntryKDBX : EntryVersioned, 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 From a10d1c98a87c9ba7645dfc50405d5447de5e63b9 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 6 Apr 2021 17:32:40 +0200 Subject: [PATCH 14/25] Fix KDB parcelable --- .../kunzisoft/keepass/database/element/entry/EntryKDB.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt index 4c84e9d35..942a54e3c 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/element/entry/EntryKDB.kt @@ -90,7 +90,8 @@ class EntryKDB : EntryVersioned, 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, NodeKDBInterface dest.writeString(url) dest.writeString(notes) dest.writeString(binaryDescription) - binaryDataId?.let { - dest.writeInt(it) - } + dest.writeInt(binaryDataId ?: -1) } fun updateWith(source: EntryKDB) { From 66eeadca0b591fc270bf42dbcf94e8036011100a Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Tue, 6 Apr 2021 17:39:27 +0200 Subject: [PATCH 15/25] Upgrade version code --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b1c3d3c76..78752841c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,7 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 15 targetSdkVersion 30 - versionCode = 69 + versionCode = 70 versionName = "2.9.16" multiDexEnabled true From de69a78a9808805dbff85a336cf99b1632c10be4 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Wed, 7 Apr 2021 16:13:40 +0200 Subject: [PATCH 16/25] First commit to import and export app properties --- .../keepass/activities/EntryEditActivity.kt | 2 +- .../activities/FileDatabaseSelectActivity.kt | 2 +- .../keepass/activities/IconPickerActivity.kt | 2 +- .../keepass/activities/PasswordActivity.kt | 2 +- .../dialogs/AssignMasterKeyDialogFragment.kt | 2 +- .../activities/helpers/ExternalFileHelper.kt | 21 +++--- .../settings/NestedAppSettingsFragment.kt | 24 ++++--- .../keepass/settings/PreferencesUtil.kt | 17 +++++ .../keepass/settings/SettingsActivity.kt | 64 +++++++++++++++++++ app/src/main/res/values/donottranslate.xml | 4 ++ app/src/main/res/values/strings.xml | 7 ++ .../main/res/xml/preferences_application.xml | 14 ++++ 12 files changed, 140 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt index 1e7c7eafb..425f54e4f 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryEditActivity.kt @@ -505,7 +505,7 @@ class EntryEditActivity : LockingActivity(), entryEditFragment?.icon = icon } - mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> + mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri -> uri?.let { attachmentToUploadUri -> UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile -> documentFile.name?.let { fileName -> diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt index 48c65ac61..02500c8ce 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt @@ -355,7 +355,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(), AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data) } - mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> + mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri -> if (uri != null) { launchPasswordActivityWithPath(uri) } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt index 119f0cba3..403647a79 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/IconPickerActivity.kt @@ -276,7 +276,7 @@ class IconPickerActivity : LockingActivity() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> + mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri -> addCustomIcon(uri) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt index c65a9f509..e39089485 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/PasswordActivity.kt @@ -695,7 +695,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil var keyFileResult = false mExternalFileHelper?.let { - keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data + keyFileResult = it.onOpenDocumentResult(requestCode, resultCode, data ) { uri -> if (uri != null) { mDatabaseKeyFileUri = uri diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt index 1c967f9be..44c9f482e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/dialogs/AssignMasterKeyDialogFragment.kt @@ -286,7 +286,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) - mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri -> + mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri -> uri?.let { pathUri -> UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile -> keyFileSelectionView?.error = null diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt index 1223acaec..9dbfd3a97 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/helpers/ExternalFileHelper.kt @@ -104,11 +104,11 @@ class ExternalFileHelper { /** * To use in onActivityResultCallback in Fragment or Activity - * @param keyFileCallback Callback retrieve from data - * @return true if requestCode was captured, false elsechere + * @param onFileSelected Callback retrieve from data + * @return true if requestCode was captured, false elsewhere */ - fun onActivityResultCallback(requestCode: Int, resultCode: Int, data: Intent?, - keyFileCallback: ((uri: Uri?) -> Unit)?): Boolean { + fun onOpenDocumentResult(requestCode: Int, resultCode: Int, data: Intent?, + onFileSelected: ((uri: Uri?) -> Unit)?): Boolean { when (requestCode) { FILE_BROWSE -> { @@ -118,7 +118,7 @@ class ExternalFileHelper { if (filename != null) { keyUri = UriUtil.parse(filename) } - keyFileCallback?.invoke(keyUri) + onFileSelected?.invoke(keyUri) } return true } @@ -138,7 +138,7 @@ class ExternalFileHelper { } catch (e: Exception) { // nop } - keyFileCallback?.invoke(uri) + onFileSelected?.invoke(uri) } } } @@ -190,11 +190,16 @@ class ExternalFileHelper { return null } + /** + * To use in onActivityResultCallback in Fragment or Activity + * @param onFileCreated Callback retrieve from data + * @return true if requestCode was captured, false elsewhere + */ fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?, - action: (fileCreated: Uri?)->Unit) { + onFileCreated: (fileCreated: Uri?)->Unit) { // Retrieve the created URI from the file manager if (fileRequestCodes.contains(requestCode) && resultCode == RESULT_OK) { - action.invoke(data?.data) + onFileCreated.invoke(data?.data) fileRequestCodes.remove(requestCode) } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt index 0fcbeb393..34674b2d7 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt @@ -90,6 +90,20 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { } true } + + findPreference(getString(R.string.import_app_properties_key))?.setOnPreferenceClickListener { _ -> + (activity as? SettingsActivity?)?.apply { + importAppProperties() + } + true + } + + findPreference(getString(R.string.export_app_properties_key))?.setOnPreferenceClickListener { _ -> + (activity as? SettingsActivity?)?.apply { + exportAppProperties() + } + true + } } } @@ -388,10 +402,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { Stylish.assignStyle(activity, styleIdString) // Relaunch the current activity to redraw theme (activity as? SettingsActivity?)?.apply { - keepCurrentScreen() - startActivity(intent) - finish() - activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + relaunchCurrentScreen() } } styleEnabled @@ -399,10 +410,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { findPreference(getString(R.string.setting_style_brightness_key))?.setOnPreferenceChangeListener { _, _ -> (activity as? SettingsActivity?)?.apply { - keepCurrentScreen() - startActivity(intent) - finish() - activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + relaunchCurrentScreen() } true } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index df8182927..620170047 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -517,4 +517,21 @@ object PreferencesUtil { .putStringSet(context.getString(R.string.autofill_web_domain_blocklist_key), setItems) .apply() } + + fun getAppProperties(context: Context): Properties { + val allPreferences = PreferenceManager.getDefaultSharedPreferences(context).all + val properties = Properties() + for ((name, value) in allPreferences) { + properties[name] = value.toString() + } + return properties + } + + fun setAppProperties(context: Context, properties: Properties) { + PreferenceManager.getDefaultSharedPreferences(context).edit().also { + for ((name, value) in properties) { + // TODO Set app properties + } + }.apply() + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 4760e86c2..339ea3659 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -24,14 +24,17 @@ import android.app.backup.BackupManager import android.content.Intent import android.net.Uri import android.os.Bundle +import android.util.Log import android.view.MenuItem import android.view.View +import android.widget.Toast import androidx.appcompat.widget.Toolbar import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.fragment.app.Fragment import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment +import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged @@ -40,6 +43,7 @@ import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.view.showActionErrorIfNeeded +import java.util.* open class SettingsActivity : LockingActivity(), @@ -48,6 +52,8 @@ open class SettingsActivity PasswordEncodingDialogFragment.Listener { private var backupManager: BackupManager? = null + private var mExternalFileHelper: ExternalFileHelper? = null + private var appPropertiesFileCreationRequestCode: Int? = null private var coordinatorLayout: CoordinatorLayout? = null private var toolbar: Toolbar? = null @@ -70,6 +76,8 @@ open class SettingsActivity coordinatorLayout = findViewById(R.id.toolbar_coordinator) toolbar = findViewById(R.id.toolbar) + mExternalFileHelper = ExternalFileHelper(this) + if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty()) toolbar?.setTitle(R.string.settings) else @@ -216,6 +224,13 @@ open class SettingsActivity hideOrShowLockButton(key) } + fun relaunchCurrentScreen() { + keepCurrentScreen() + startActivity(intent) + finish() + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + } + /** * To keep the current screen when activity is reloaded */ @@ -235,6 +250,53 @@ open class SettingsActivity replaceFragment(key, reload) } + fun importAppProperties() { + mExternalFileHelper?.openDocument() + } + + fun exportAppProperties() { + appPropertiesFileCreationRequestCode = mExternalFileHelper?.createDocument(getString(R.string.app_properties_file_name)) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + // Import app properties result + try { + mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { selectedfileUri -> + selectedfileUri?.let { uri -> + val appProperties = Properties() + contentResolver?.openInputStream(uri)?.use { inputStream -> + appProperties.load(inputStream) + } + PreferencesUtil.setAppProperties(this, appProperties) + relaunchCurrentScreen() + } + } + } catch (e: Exception) { + Log.e(TAG, "Unable to import app properties", e) + } + + // Export app properties result + try { + if (requestCode == appPropertiesFileCreationRequestCode) { + mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri -> + createdFileUri?.let { uri -> + contentResolver?.openOutputStream(uri)?.use { outputStream -> + PreferencesUtil + .getAppProperties(this) + .store(outputStream, getString(R.string.description_app_properties)) + } + Toast.makeText(this, R.string.export_app_properties_success, Toast.LENGTH_LONG).show() + } + } + appPropertiesFileCreationRequestCode = null + } + } catch (e: Exception) { + Log.e(LockingActivity.TAG, "Unable to export app properties", e) + } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -244,6 +306,8 @@ open class SettingsActivity companion object { + private val TAG = SettingsActivity::class.java.name + private const val SHOW_LOCK = "SHOW_LOCK" private const val TITLE_KEY = "TITLE_KEY" private const val TAG_NESTED = "TAG_NESTED" diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 6644d6ce6..a29de3357 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -47,6 +47,7 @@ keepass .kdbx KeePassDX Database + keepassdx.properties settings_form_filling_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a12c2ef0e..722e281ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -234,6 +234,12 @@ Show locations of recent databases Hide broken database links Hide broken links in the list of recent databases + Import app properties + Select a file to import app properties + Export app properties + Create a file to export app properties + KeePassDX properties to manage app settings + App properties exported Root Database encryption algorithm used for all data. To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function. @@ -302,6 +308,7 @@ Unable to initialize advanced unlock prompt. Type in the password, and then click this button. History + Properties Appearance Biometric Device credential diff --git a/app/src/main/res/xml/preferences_application.xml b/app/src/main/res/xml/preferences_application.xml index 9c140c562..782b314a4 100644 --- a/app/src/main/res/xml/preferences_application.xml +++ b/app/src/main/res/xml/preferences_application.xml @@ -150,4 +150,18 @@ + + + + + + + From b9e26fecfd009c77f4f78f4608b724eb91adccca Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 8 Apr 2021 11:45:23 +0200 Subject: [PATCH 17/25] Export and import properties --- .../keepass/settings/PreferencesUtil.kt | 125 +++++++++++++++++- .../keepass/settings/SettingsActivity.kt | 9 +- app/src/main/res/values/strings.xml | 5 +- 3 files changed, 132 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index 620170047..c82caa977 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -21,14 +21,17 @@ package com.kunzisoft.keepass.settings import android.app.backup.BackupManager import android.content.Context +import android.content.SharedPreferences import android.content.res.Resources import android.net.Uri +import android.util.Log import androidx.preference.PreferenceManager import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.stylish.Stylish import com.kunzisoft.keepass.biometric.AdvancedUnlockManager import com.kunzisoft.keepass.database.element.SortNodeEnum +import com.kunzisoft.keepass.education.Education import com.kunzisoft.keepass.timeout.TimeoutHelper import java.util.* @@ -519,19 +522,131 @@ object PreferencesUtil { } fun getAppProperties(context: Context): Properties { - val allPreferences = PreferenceManager.getDefaultSharedPreferences(context).all val properties = Properties() - for ((name, value) in allPreferences) { + for ((name, value) in PreferenceManager.getDefaultSharedPreferences(context).all) { + properties[name] = value.toString() + } + for ((name, value) in Education.getEducationSharedPreferences(context).all) { properties[name] = value.toString() } return properties } - fun setAppProperties(context: Context, properties: Properties) { - PreferenceManager.getDefaultSharedPreferences(context).edit().also { + private fun getStringSetFromProperties(value: String): Set { + return value.removePrefix("[") + .removeSuffix("]") + .split(", ") + .toSet() + } + + private fun putPropertiesInPreferences(properties: Properties, + preferences: SharedPreferences, + putProperty: (editor: SharedPreferences.Editor, + name: String, + value: String) -> Unit) { + preferences.edit().apply { for ((name, value) in properties) { - // TODO Set app properties + try { + putProperty(this, name as String, value as String) + } catch (e:Exception) { + Log.e("PreferencesUtil", "Error when trying to parse app property $name=$value", e) + } } }.apply() } + + fun setAppProperties(context: Context, properties: Properties) { + putPropertiesInPreferences(properties, + PreferenceManager.getDefaultSharedPreferences(context)) { editor, name, value -> + when (name) { + context.getString(R.string.allow_no_password_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.delete_entered_password_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.enable_read_only_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.enable_auto_save_database_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.omit_backup_search_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.auto_focus_search_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.subdomain_search_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.app_timeout_key) -> editor.putString(name, value.toLong().toString()) + context.getString(R.string.lock_database_screen_off_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.lock_database_back_root_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.lock_database_show_button_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.password_length_key) -> editor.putInt(name, value.toInt()) + context.getString(R.string.list_password_generator_options_key) -> editor.putStringSet(name, getStringSetFromProperties(value)) + context.getString(R.string.hide_password_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.allow_copy_password_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.remember_database_locations_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.show_recent_files_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.hide_broken_locations_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.remember_keyfile_locations_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.biometric_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.device_credential_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.biometric_auto_open_prompt_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.temp_advanced_unlock_enable_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.temp_advanced_unlock_timeout_key) -> editor.putString(name, value.toLong().toString()) + + context.getString(R.string.magic_keyboard_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.clipboard_notifications_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.clear_clipboard_notification_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.clipboard_timeout_key) -> editor.putString(name, value.toLong().toString()) + context.getString(R.string.settings_autofill_enable_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_notification_entry_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_notification_entry_clear_close_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_entry_timeout_key) -> editor.putString(name, value.toLong().toString()) + context.getString(R.string.keyboard_selection_entry_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_search_share_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_save_search_info_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_auto_go_action_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_key_vibrate_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_key_sound_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_previous_database_credentials_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_previous_fill_in_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.keyboard_previous_lock_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_close_database_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_auto_search_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_inline_suggestions_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_save_search_info_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_ask_to_save_data_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.autofill_application_id_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value)) + context.getString(R.string.autofill_web_domain_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value)) + + context.getString(R.string.setting_style_key) -> editor.putString(name, value) + context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value) + context.getString(R.string.setting_icon_pack_choose_key) -> editor.putString(name, value) + context.getString(R.string.list_entries_show_username_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.list_groups_show_number_entries_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.list_size_key) -> editor.putString(name, value) + context.getString(R.string.monospace_font_fields_enable_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.hide_expired_entries_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.show_uuid_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean()) + + context.getString(R.string.sort_node_key) -> editor.putString(name, value) + context.getString(R.string.sort_group_before_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.sort_ascending_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.sort_recycle_bin_bottom_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.allow_copy_password_first_time_key) -> editor.putBoolean(name, value.toBoolean()) + } + } + + putPropertiesInPreferences(properties, + Education.getEducationSharedPreferences(context)) { editor, name, value -> + when (name) { + context.getString(R.string.education_create_db_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_select_db_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_unlock_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_read_only_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_biometric_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_search_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_new_node_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_sort_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_lock_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_copy_username_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_entry_edit_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_password_generator_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_entry_new_field_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_add_attachment_key) -> editor.putBoolean(name, value.toBoolean()) + context.getString(R.string.education_setup_OTP_key) -> editor.putBoolean(name, value.toBoolean()) + } + } + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index 339ea3659..f8d9d53d3 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -38,6 +38,7 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged +import com.kunzisoft.keepass.activities.stylish.Stylish import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.model.MainCredential import com.kunzisoft.keepass.services.DatabaseTaskNotificationService @@ -270,10 +271,15 @@ open class SettingsActivity appProperties.load(inputStream) } PreferencesUtil.setAppProperties(this, appProperties) + + // Restart the current activity + Stylish.assignStyle(this, PreferencesUtil.getStyle(this)) relaunchCurrentScreen() + Toast.makeText(this, R.string.success_import_app_properties, Toast.LENGTH_LONG).show() } } } catch (e: Exception) { + Toast.makeText(this, R.string.error_import_app_properties, Toast.LENGTH_LONG).show() Log.e(TAG, "Unable to import app properties", e) } @@ -287,12 +293,13 @@ open class SettingsActivity .getAppProperties(this) .store(outputStream, getString(R.string.description_app_properties)) } - Toast.makeText(this, R.string.export_app_properties_success, Toast.LENGTH_LONG).show() + Toast.makeText(this, R.string.success_export_app_properties, Toast.LENGTH_LONG).show() } } appPropertiesFileCreationRequestCode = null } } catch (e: Exception) { + Toast.makeText(this, R.string.error_export_app_properties, Toast.LENGTH_LONG).show() Log.e(LockingActivity.TAG, "Unable to export app properties", e) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 722e281ff..ba624eadd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -239,7 +239,10 @@ Export app properties Create a file to export app properties KeePassDX properties to manage app settings - App properties exported + App properties imported + Error during app properties importation + App properties exported + Error during app properties exportation Root Database encryption algorithm used for all data. To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function. From 346b517c9dd6b19203935e52912a5acf317d7231 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 8 Apr 2021 12:55:23 +0200 Subject: [PATCH 18/25] Force twofish padding compatibility #955 --- .../com/kunzisoft/keepass/database/crypto/CipherEngine.kt | 3 +++ .../kunzisoft/keepass/database/crypto/TwofishEngine.kt | 2 +- .../keepass/database/file/input/DatabaseInputKDBX.kt | 2 ++ .../src/main/java/com/kunzisoft/encrypt/CipherFactory.kt | 8 ++++++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/database/crypto/CipherEngine.kt b/app/src/main/java/com/kunzisoft/keepass/database/crypto/CipherEngine.kt index 38b653853..1850e9da8 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/crypto/CipherEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/crypto/CipherEngine.kt @@ -36,6 +36,9 @@ abstract class CipherEngine { return 16 } + // Used only with padding workaround + var forcePaddingCompatibility = false + @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) abstract fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher diff --git a/app/src/main/java/com/kunzisoft/keepass/database/crypto/TwofishEngine.kt b/app/src/main/java/com/kunzisoft/keepass/database/crypto/TwofishEngine.kt index bec5a17a3..48d2ec0b5 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/crypto/TwofishEngine.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/crypto/TwofishEngine.kt @@ -30,7 +30,7 @@ class TwofishEngine : CipherEngine() { @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) override fun getCipher(opmode: Int, key: ByteArray, IV: ByteArray): Cipher { - return CipherFactory.getTwofish(opmode, key, IV) + return CipherFactory.getTwofish(opmode, key, IV, forcePaddingCompatibility) } override fun getEncryptionAlgorithm(): EncryptionAlgorithm { diff --git a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt index adbe7e9af..c4f9a9f3a 100644 --- a/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt +++ b/app/src/main/java/com/kunzisoft/keepass/database/file/input/DatabaseInputKDBX.kt @@ -151,9 +151,11 @@ class DatabaseInputKDBX(cacheDirectory: File, val cipher: Cipher try { engine = EncryptionAlgorithm.getFrom(mDatabase.cipherUuid).cipherEngine + engine.forcePaddingCompatibility = true mDatabase.setDataEngine(engine) mDatabase.encryptionAlgorithm = engine.getEncryptionAlgorithm() cipher = engine.getCipher(Cipher.DECRYPT_MODE, mDatabase.finalKey!!, header.encryptionIV) + engine.forcePaddingCompatibility = false } catch (e: Exception) { throw InvalidAlgorithmDatabaseException(e) } diff --git a/crypto/src/main/java/com/kunzisoft/encrypt/CipherFactory.kt b/crypto/src/main/java/com/kunzisoft/encrypt/CipherFactory.kt index c2830f814..7b48bdc24 100644 --- a/crypto/src/main/java/com/kunzisoft/encrypt/CipherFactory.kt +++ b/crypto/src/main/java/com/kunzisoft/encrypt/CipherFactory.kt @@ -38,8 +38,12 @@ object CipherFactory { } @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class) - fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray): Cipher { - val cipher: Cipher = Cipher.getInstance("Twofish/CBC/PKCS7PADDING") + fun getTwofish(opmode: Int, key: ByteArray, IV: ByteArray, forceCompatibility: Boolean = false): Cipher { + val cipher: Cipher = if (forceCompatibility) { + Cipher.getInstance("Twofish/CBC/NoPadding") + } else { + Cipher.getInstance("Twofish/CBC/PKCS7PADDING") + } cipher.init(opmode, SecretKeySpec(key, "AES"), IvParameterSpec(IV)) return cipher } From ff185f6505d1730ba96952391a6d84e1c2a9c5c6 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 8 Apr 2021 12:58:39 +0200 Subject: [PATCH 19/25] Upgrade version and CHANGELOG --- CHANGELOG | 4 ++++ app/build.gradle | 4 ++-- fastlane/metadata/android/en-US/changelogs/71.txt | 2 ++ fastlane/metadata/android/fr-FR/changelogs/71.txt | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/71.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/71.txt diff --git a/CHANGELOG b/CHANGELOG index 395b8c9ae..eb3566ed5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +KeePassDX(2.9.17) + * Import / Export app properties #839 + * Force twofish padding compatibility #955 + KeePassDX(2.9.16) * Fix small bugs #948 diff --git a/app/build.gradle b/app/build.gradle index 78752841c..631efd682 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.kunzisoft.keepass" minSdkVersion 15 targetSdkVersion 30 - versionCode = 70 - versionName = "2.9.16" + versionCode = 71 + versionName = "2.9.17" multiDexEnabled true testApplicationId = "com.kunzisoft.keepass.tests" diff --git a/fastlane/metadata/android/en-US/changelogs/71.txt b/fastlane/metadata/android/en-US/changelogs/71.txt new file mode 100644 index 000000000..ebeb342ed --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/71.txt @@ -0,0 +1,2 @@ + * Import / Export app properties #839 + * Force twofish padding compatibility #955 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/71.txt b/fastlane/metadata/android/fr-FR/changelogs/71.txt new file mode 100644 index 000000000..8b0fda423 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/71.txt @@ -0,0 +1,2 @@ + * Import / Export des propriétés de l'application #839 + * Force la compatibilité du bourrage twofish #955 \ No newline at end of file From 1742d265f350b4b3a6d66802f47f58f5ca69df19 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 8 Apr 2021 16:26:14 +0200 Subject: [PATCH 20/25] Better stylish implementation --- .../keepass/activities/stylish/Stylish.kt | 10 ++++--- .../java/com/kunzisoft/keepass/app/App.kt | 2 +- .../keepass/settings/PreferencesUtil.kt | 26 ++++++++++++++----- .../keepass/settings/SettingsActivity.kt | 1 - 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.kt b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.kt index 4d6406fab..71ba57311 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.kt +++ b/app/src/main/java/com/kunzisoft/keepass/activities/stylish/Stylish.kt @@ -37,12 +37,12 @@ object Stylish { * Initialize the class with a theme preference * @param context Context to retrieve the theme preference */ - fun init(context: Context) { + fun load(context: Context) { Log.d(Stylish::class.java.name, "Attatching to " + context.packageName) themeString = PreferencesUtil.getStyle(context) } - private fun retrieveEquivalentSystemStyle(context: Context, styleString: String): String { + fun retrieveEquivalentSystemStyle(context: Context, styleString: String): String { val systemNightMode = when (PreferencesUtil.getStyleBrightness(context)) { context.getString(R.string.list_style_brightness_light) -> false context.getString(R.string.list_style_brightness_night) -> true @@ -84,12 +84,16 @@ object Stylish { } } + fun defaultStyle(context: Context): String { + return context.getString(R.string.list_style_name_light) + } + /** * Assign the style to the class attribute * @param styleString Style id String */ fun assignStyle(context: Context, styleString: String) { - themeString = retrieveEquivalentSystemStyle(context, styleString) + PreferencesUtil.setStyle(context, styleString) } /** diff --git a/app/src/main/java/com/kunzisoft/keepass/app/App.kt b/app/src/main/java/com/kunzisoft/keepass/app/App.kt index 5207b1e51..ccfa97abd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/app/App.kt +++ b/app/src/main/java/com/kunzisoft/keepass/app/App.kt @@ -29,7 +29,7 @@ class App : MultiDexApplication() { override fun onCreate() { super.onCreate() - Stylish.init(this) + Stylish.load(this) PRNGFixes.apply() } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index c82caa977..325a55bfd 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -137,18 +137,32 @@ object PreferencesUtil { } fun getStyle(context: Context): String { - val stylishPrefKey = context.getString(R.string.setting_style_key) - val defaultStyleString = context.getString(R.string.list_style_name_light) + val defaultStyleString = Stylish.defaultStyle(context) val styleString = PreferenceManager.getDefaultSharedPreferences(context) - .getString(stylishPrefKey, defaultStyleString) + .getString(context.getString(R.string.setting_style_key), defaultStyleString) ?: defaultStyleString - return Stylish.retrieveEquivalentLightStyle(context, styleString) + // Return the system style + return Stylish.retrieveEquivalentSystemStyle(context, styleString) + } + + fun setStyle(context: Context, styleString: String) { + var tempThemeString = styleString + if (tempThemeString in BuildConfig.STYLES_DISABLED) { + tempThemeString = Stylish.defaultStyle(context) + } + // Store light style to show selection in array list + tempThemeString = Stylish.retrieveEquivalentLightStyle(context, tempThemeString) + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(context.getString(R.string.setting_style_key), tempThemeString) + .apply() + Stylish.load(context) } fun getStyleBrightness(context: Context): String? { val prefs = PreferenceManager.getDefaultSharedPreferences(context) return prefs.getString(context.getString(R.string.setting_style_brightness_key), - context.resources.getString(R.string.list_style_brightness_follow_system)) + context.getString(R.string.list_style_brightness_follow_system)) } /** @@ -609,7 +623,7 @@ object PreferencesUtil { context.getString(R.string.autofill_application_id_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value)) context.getString(R.string.autofill_web_domain_blocklist_key) -> editor.putStringSet(name, getStringSetFromProperties(value)) - context.getString(R.string.setting_style_key) -> editor.putString(name, value) + context.getString(R.string.setting_style_key) -> setStyle(context, value) context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value) context.getString(R.string.setting_icon_pack_choose_key) -> editor.putString(name, value) context.getString(R.string.list_entries_show_username_key) -> editor.putBoolean(name, value.toBoolean()) diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt index f8d9d53d3..550079ca6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/SettingsActivity.kt @@ -273,7 +273,6 @@ open class SettingsActivity PreferencesUtil.setAppProperties(this, appProperties) // Restart the current activity - Stylish.assignStyle(this, PreferencesUtil.getStyle(this)) relaunchCurrentScreen() Toast.makeText(this, R.string.success_import_app_properties, Toast.LENGTH_LONG).show() } From c40b2550229bf461334bef789753e2ad6da5904c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Thu, 8 Apr 2021 16:40:01 +0200 Subject: [PATCH 21/25] Check properties --- .../keepass/settings/PreferencesUtil.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt index 325a55bfd..1a60bdc34 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/PreferencesUtil.kt @@ -169,10 +169,14 @@ object PreferencesUtil { * Retrieve the text size in % (1 for 100%) */ fun getListTextSize(context: Context): Float { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - val listSizeString = prefs.getString(context.getString(R.string.list_size_key), - context.getString(R.string.list_size_string_medium)) - val index = context.resources.getStringArray(R.array.list_size_string_values).indexOf(listSizeString) + val index = try { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + val listSizeString = prefs.getString(context.getString(R.string.list_size_key), + context.getString(R.string.list_size_string_medium)) + context.resources.getStringArray(R.array.list_size_string_values).indexOf(listSizeString) + } catch (e: Exception) { + 1 + } val typedArray = context.resources.obtainTypedArray(R.array.list_size_values) val listSize = typedArray.getFloat(index, 1.0F) typedArray.recycle() @@ -306,11 +310,13 @@ object PreferencesUtil { } fun getListSort(context: Context): SortNodeEnum { - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.getString(context.getString(R.string.sort_node_key), - SortNodeEnum.DB.name)?.let { - return SortNodeEnum.valueOf(it) - } + try { + val prefs = PreferenceManager.getDefaultSharedPreferences(context) + prefs.getString(context.getString(R.string.sort_node_key), + SortNodeEnum.DB.name)?.let { + return SortNodeEnum.valueOf(it) + } + } catch (e: Exception) {} return SortNodeEnum.DB } From 014b0cce14854de7b5afb9c0f1ab942f9930c5d0 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 10 Apr 2021 19:29:19 +0200 Subject: [PATCH 22/25] Add duration preference with number picker --- .../settings/MagikeyboardSettingsFragment.kt | 30 +++ .../settings/NestedAppSettingsFragment.kt | 29 ++- .../NestedDatabaseSettingsFragment.kt | 1 - .../preference/DurationDialogPreference.kt | 77 ++++++++ .../DurationDialogFragmentCompat.kt | 180 ++++++++++++++++++ .../InputPreferenceDialogFragmentCompat.kt | 10 + .../main/res/drawable/ic_day_white_24dp.xml | 5 + .../main/res/layout/pref_dialog_duration.xml | 149 +++++++++++++++ app/src/main/res/values/donottranslate.xml | 25 --- .../res/xml/preferences_advanced_unlock.xml | 4 +- .../main/res/xml/preferences_application.xml | 4 +- .../main/res/xml/preferences_form_filling.xml | 4 +- app/src/main/res/xml/preferences_keyboard.xml | 4 +- 13 files changed, 483 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/preference/DurationDialogPreference.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DurationDialogFragmentCompat.kt create mode 100644 app/src/main/res/drawable/ic_day_white_24dp.xml create mode 100644 app/src/main/res/layout/pref_dialog_duration.xml diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt index e087a7a82..3d7e6c9e6 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/MagikeyboardSettingsFragment.kt @@ -20,9 +20,12 @@ package com.kunzisoft.keepass.settings import android.os.Bundle +import androidx.fragment.app.DialogFragment +import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat class MagikeyboardSettingsFragment : PreferenceFragmentCompat() { @@ -30,4 +33,31 @@ class MagikeyboardSettingsFragment : PreferenceFragmentCompat() { // Load the preferences from an XML resource setPreferencesFromResource(R.xml.preferences_keyboard, rootKey) } + + override fun onDisplayPreferenceDialog(preference: Preference?) { + + var otherDialogFragment = false + + var dialogFragment: DialogFragment? = null + // Main Preferences + when (preference?.key) { + getString(R.string.keyboard_entry_timeout_key) -> { + dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key) + } + else -> otherDialogFragment = true + } + + if (dialogFragment != null) { + dialogFragment.setTargetFragment(this, 0) + dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT) + } + // Could not be handled here. Try with the super method. + else if (otherDialogFragment) { + super.onDisplayPreferenceDialog(preference) + } + } + + companion object { + private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" + } } diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt index 34674b2d7..ab6bcb22b 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedAppSettingsFragment.kt @@ -30,6 +30,7 @@ import android.view.autofill.AutofillManager import android.widget.Toast import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity import androidx.preference.ListPreference import androidx.preference.Preference @@ -46,6 +47,7 @@ import com.kunzisoft.keepass.education.Education import com.kunzisoft.keepass.icons.IconPackChooser import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService import com.kunzisoft.keepass.settings.preference.IconPackListPreference +import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat import com.kunzisoft.keepass.utils.UriUtil @@ -448,6 +450,31 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { } } + override fun onDisplayPreferenceDialog(preference: Preference?) { + + var otherDialogFragment = false + + var dialogFragment: DialogFragment? = null + // Main Preferences + when (preference?.key) { + getString(R.string.app_timeout_key), + getString(R.string.clipboard_timeout_key), + getString(R.string.temp_advanced_unlock_timeout_key) -> { + dialogFragment = DurationDialogFragmentCompat.newInstance(preference.key) + } + else -> otherDialogFragment = true + } + + if (dialogFragment != null) { + dialogFragment.setTargetFragment(this, 0) + dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT) + } + // Could not be handled here. Try with the super method. + else if (otherDialogFragment) { + super.onDisplayPreferenceDialog(preference) + } + } + override fun onResume() { super.onResume() activity?.let { activity -> @@ -478,7 +505,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() { } companion object { - private const val REQUEST_CODE_AUTOFILL = 5201 + private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt index 5f1ad6cb0..831929a92 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/NestedDatabaseSettingsFragment.kt @@ -576,7 +576,6 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() { } companion object { - private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" } } \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preference/DurationDialogPreference.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DurationDialogPreference.kt new file mode 100644 index 000000000..4d1f203d8 --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preference/DurationDialogPreference.kt @@ -0,0 +1,77 @@ +/* + * 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 . + * + */ +package com.kunzisoft.keepass.settings.preference + +import android.content.Context +import android.content.res.TypedArray +import android.util.AttributeSet +import androidx.preference.DialogPreference +import com.kunzisoft.keepass.R + +class DurationDialogPreference @JvmOverloads constructor(context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.dialogPreferenceStyle, + defStyleRes: Int = defStyleAttr) + : DialogPreference(context, attrs, defStyleAttr, defStyleRes) { + + private var mDuration: Long = 0L + + override fun getDialogLayoutResource(): Int { + return R.layout.pref_dialog_duration + } + + /** + * Get current duration of preference + */ + fun getDuration(): Long { + return if (mDuration >= 0) mDuration else -1 + } + + /** + * Assign [duration] of preference + */ + fun setDuration(duration: Long) { + persistString(duration.toString()) + notifyChanged() + } + + override fun onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any?) { + if (restorePersistedValue) { + mDuration = getPersistedString(mDuration.toString()).toLongOrNull() ?: mDuration + } else { + mDuration = defaultValue?.toString()?.toLongOrNull() ?: mDuration + persistString(mDuration.toString()) + } + } + + override fun onGetDefaultValue(a: TypedArray?, index: Int): Any { + return try { + a?.getString(index)?.toLongOrNull() ?: mDuration + } catch (e: Exception) { + mDuration + } + } + + // Was previously a string + override fun persistString(value: String?): Boolean { + mDuration = value?.toLongOrNull() ?: mDuration + return super.persistString(value) + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DurationDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DurationDialogFragmentCompat.kt new file mode 100644 index 000000000..2a5277c9c --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/DurationDialogFragmentCompat.kt @@ -0,0 +1,180 @@ +/* + * Copyright 2021 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 . + * + */ +package com.kunzisoft.keepass.settings.preferencedialogfragment + +import android.os.Bundle +import android.view.View +import android.widget.NumberPicker +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.settings.preference.DurationDialogPreference + + +class DurationDialogFragmentCompat : InputPreferenceDialogFragmentCompat() { + + private var mEnabled = true + private var mDays = 0 + private var mHours = 0 + private var mMinutes = 0 + private var mSeconds = 0 + + private var daysNumberPicker: NumberPicker? = null + private var hoursNumberPicker: NumberPicker? = null + private var minutesNumberPicker: NumberPicker? = null + private var secondsNumberPicker: NumberPicker? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // To get items from saved instance state + if (savedInstanceState != null + && savedInstanceState.containsKey(ENABLE_KEY) + && savedInstanceState.containsKey(DAYS_KEY) + && savedInstanceState.containsKey(HOURS_KEY) + && savedInstanceState.containsKey(MINUTES_KEY) + && savedInstanceState.containsKey(SECONDS_KEY)) { + mEnabled = savedInstanceState.getBoolean(ENABLE_KEY) + mDays = savedInstanceState.getInt(DAYS_KEY) + mHours = savedInstanceState.getInt(HOURS_KEY) + mMinutes = savedInstanceState.getInt(MINUTES_KEY) + mSeconds = savedInstanceState.getInt(SECONDS_KEY) + } else { + val currentPreference = preference + if (currentPreference is DurationDialogPreference) { + durationToDaysHoursMinutesSeconds(currentPreference.getDuration()) + } + } + } + + private fun durationToDaysHoursMinutesSeconds(duration: Long) { + if (duration < 0) { + mDays = 0 + mHours = 0 + mMinutes = 0 + mSeconds = 0 + } else { + mDays = (duration / (24L * 60L * 60L * 1000L)).toInt() + val daysMilliseconds = mDays * 24L * 60L * 60L * 1000L + mHours = ((duration - daysMilliseconds) / (60L * 60L * 1000L)).toInt() + val hoursMilliseconds = mHours * 60L * 60L * 1000L + mMinutes = ((duration - daysMilliseconds - hoursMilliseconds) / (60L * 1000L)).toInt() + val minutesMilliseconds = mMinutes * 60L * 1000L + mSeconds = ((duration - daysMilliseconds - hoursMilliseconds - minutesMilliseconds) / (1000L)).toInt() + } + } + + private fun assignValuesInViews() { + daysNumberPicker?.value = mDays + hoursNumberPicker?.value = mHours + minutesNumberPicker?.value = mMinutes + secondsNumberPicker?.value = mSeconds + } + + override fun onBindDialogView(view: View) { + super.onBindDialogView(view) + + daysNumberPicker = view.findViewById(R.id.days_picker).apply { + minValue = 0 + maxValue = 364 + setOnValueChangedListener { _, _, newVal -> + mDays = newVal + activateSwitch() + } + } + + hoursNumberPicker = view.findViewById(R.id.hours_picker).apply { + minValue = 0 + maxValue = 23 + setOnValueChangedListener { _, _, newVal -> + mHours = newVal + activateSwitch() + } + } + + minutesNumberPicker = view.findViewById(R.id.minutes_picker).apply { + minValue = 0 + maxValue = 59 + setOnValueChangedListener { _, _, newVal -> + mMinutes = newVal + activateSwitch() + } + } + + secondsNumberPicker = view.findViewById(R.id.seconds_picker).apply { + minValue = 0 + maxValue = 59 + setOnValueChangedListener { _, _, newVal -> + mSeconds = newVal + activateSwitch() + } + } + + setSwitchAction({ isChecked -> + mEnabled = isChecked + }, mDays + mHours + mMinutes + mSeconds > 0) + + assignValuesInViews() + } + + private fun buildDuration(): Long { + return if (mEnabled) { + mDays * 24L * 60L * 60L * 1000L + + mHours * 60L * 60L * 1000L + + mMinutes * 60L * 1000L + + mSeconds * 1000L + } else { + -1 + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(ENABLE_KEY, mEnabled) + outState.putInt(DAYS_KEY, mDays) + outState.putInt(HOURS_KEY, mHours) + outState.putInt(MINUTES_KEY, mMinutes) + outState.putInt(SECONDS_KEY, mSeconds) + } + + override fun onDialogClosed(positiveResult: Boolean) { + if (positiveResult) { + val currentPreference = preference + if (currentPreference is DurationDialogPreference) { + currentPreference.setDuration(buildDuration()) + } + } + } + + companion object { + private const val ENABLE_KEY = "ENABLE_KEY" + private const val DAYS_KEY = "DAYS_KEY" + private const val HOURS_KEY = "HOURS_KEY" + private const val MINUTES_KEY = "MINUTES_KEY" + private const val SECONDS_KEY = "SECONDS_KEY" + + fun newInstance(key: String): DurationDialogFragmentCompat { + val fragment = DurationDialogFragmentCompat() + val bundle = Bundle(1) + bundle.putString(ARG_KEY, key) + fragment.arguments = bundle + + return fragment + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt index 11751d2fb..0b6b719c2 100644 --- a/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt +++ b/app/src/main/java/com/kunzisoft/keepass/settings/preferencedialogfragment/InputPreferenceDialogFragmentCompat.kt @@ -154,4 +154,14 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom onCheckedChange?.invoke(isChecked) } } + + fun activateSwitch() { + if (switchElementView?.isChecked != true) + switchElementView?.isChecked = true + } + + fun deactivateSwitch() { + if (switchElementView?.isChecked == true) + switchElementView?.isChecked = false + } } diff --git a/app/src/main/res/drawable/ic_day_white_24dp.xml b/app/src/main/res/drawable/ic_day_white_24dp.xml new file mode 100644 index 000000000..c4ba94d90 --- /dev/null +++ b/app/src/main/res/drawable/ic_day_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/pref_dialog_duration.xml b/app/src/main/res/layout/pref_dialog_duration.xml new file mode 100644 index 000000000..4570a6f4a --- /dev/null +++ b/app/src/main/res/layout/pref_dialog_duration.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index a29de3357..ebe164511 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -288,31 +288,6 @@ timeout_backup_key 300000 1500 - - 5000 - 10000 - 20000 - 30000 - 60000 - 300000 - 900000 - 1800000 - -1 - - - 300000 - 900000 - 1800000 - 3600000 - 7200000 - 18000000 - 36000000 - 86400000 - 172800000 - 604800000 - 2592000000 - -1 - 32dp diff --git a/app/src/main/res/xml/preferences_advanced_unlock.xml b/app/src/main/res/xml/preferences_advanced_unlock.xml index 0b66ceefb..b66b7168e 100644 --- a/app/src/main/res/xml/preferences_advanced_unlock.xml +++ b/app/src/main/res/xml/preferences_advanced_unlock.xml @@ -47,13 +47,11 @@ android:title="@string/temp_advanced_unlock_enable_title" android:summary="@string/temp_advanced_unlock_enable_summary" android:defaultValue="@bool/temp_advanced_unlock_enable_default"/> - - - - From b8890aca7f4b85b0f9f834ccba76e837f04fad1c Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 10 Apr 2021 20:30:48 +0200 Subject: [PATCH 23/25] Better notification timer implementation --- .../AdvancedUnlockNotificationService.kt | 23 ++------ .../ClipboardEntryNotificationService.kt | 52 ++++++------------- .../KeyboardEntryNotificationService.kt | 28 ++-------- .../services/LockNotificationService.kt | 5 -- .../keepass/services/NotificationService.kt | 29 +++++++++++ 5 files changed, 52 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/kunzisoft/keepass/services/AdvancedUnlockNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/AdvancedUnlockNotificationService.kt index 26c56d01f..7d3d42235 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/AdvancedUnlockNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/AdvancedUnlockNotificationService.kt @@ -10,7 +10,6 @@ import com.kunzisoft.keepass.R import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.timeout.TimeoutHelper -import kotlinx.coroutines.* class AdvancedUnlockNotificationService : NotificationService() { @@ -18,9 +17,6 @@ class AdvancedUnlockNotificationService : NotificationService() { private var mActionTaskBinder = AdvancedUnlockBinder() - private var notificationTimeoutMilliSecs: Long = 0 - private var mTimerJob: Job? = null - inner class AdvancedUnlockBinder: Binder() { fun getCipherDatabase(databaseUri: Uri): CipherDatabaseEntity? { return mTempCipherDao.firstOrNull { it.databaseUri == databaseUri.toString()} @@ -80,23 +76,11 @@ class AdvancedUnlockNotificationService : NotificationService() { when (intent?.action) { ACTION_TIMEOUT -> { - notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this) + val notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this) // Not necessarily a foreground service if (mTimerJob == null && notificationTimeoutMilliSecs != TimeoutHelper.NEVER) { - mTimerJob = CoroutineScope(Dispatchers.Main).launch { - val maxPos = 100 - val posDurationMills = notificationTimeoutMilliSecs / maxPos - for (pos in maxPos downTo 0) { - notificationBuilder.setProgress(maxPos, pos, false) - startForeground(notificationId, notificationBuilder.build()) - delay(posDurationMills) - if (pos <= 0) { - stopSelf() - } - } - notificationManager?.cancel(notificationId) - mTimerJob = null - cancel() + defineTimerJob(notificationBuilder, notificationTimeoutMilliSecs) { + stopSelf() } } else { startForeground(notificationId, notificationBuilder.build()) @@ -118,7 +102,6 @@ class AdvancedUnlockNotificationService : NotificationService() { override fun onDestroy() { mTempCipherDao.clear() - mTimerJob?.cancel() super.onDestroy() } diff --git a/app/src/main/java/com/kunzisoft/keepass/services/ClipboardEntryNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/ClipboardEntryNotificationService.kt index 2930f5a78..83cb4534d 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/ClipboardEntryNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/ClipboardEntryNotificationService.kt @@ -37,8 +37,7 @@ class ClipboardEntryNotificationService : LockNotificationService() { override val notificationId = 485 private var mEntryInfo: EntryInfo? = null private var clipboardHelper: ClipboardHelper? = null - private var notificationTimeoutMilliSecs: Long = 0 - private var cleanCopyNotificationTimerTask: Thread? = null + private var mNotificationTimeoutMilliSecs: Long = 0 override fun retrieveChannelId(): String { return CHANNEL_CLIPBOARD_ID @@ -70,7 +69,7 @@ class ClipboardEntryNotificationService : LockNotificationService() { mEntryInfo = intent?.getParcelableExtra(EXTRA_ENTRY_INFO) //Get settings - notificationTimeoutMilliSecs = PreferencesUtil.getClipboardTimeout(this) + mNotificationTimeoutMilliSecs = PreferencesUtil.getClipboardTimeout(this) when { intent == null -> Log.w(TAG, "null intent") @@ -78,7 +77,7 @@ class ClipboardEntryNotificationService : LockNotificationService() { newNotification(mEntryInfo?.title, constructListOfField(intent)) } ACTION_CLEAN_CLIPBOARD == intent.action -> { - stopTask(cleanCopyNotificationTimerTask) + mTimerJob?.cancel() cleanClipboard() stopNotificationAndSendLockIfNeeded() } @@ -121,7 +120,7 @@ class ClipboardEntryNotificationService : LockNotificationService() { } private fun newNotification(title: String?, fieldsToAdd: ArrayList) { - stopTask(cleanCopyNotificationTimerTask) + mTimerJob?.cancel() val builder = buildNewNotification() .setSmallIcon(R.drawable.notification_ic_clipboard_key_24dp) @@ -147,7 +146,7 @@ class ClipboardEntryNotificationService : LockNotificationService() { } private fun copyField(fieldToCopy: ClipboardEntryNotificationField, nextFields: ArrayList) { - stopTask(cleanCopyNotificationTimerTask) + mTimerJob?.cancel() try { var generatedValue = fieldToCopy.getGeneratedValue(mEntryInfo) @@ -170,40 +169,23 @@ class ClipboardEntryNotificationService : LockNotificationService() { this, 0, cleanIntent, PendingIntent.FLAG_UPDATE_CURRENT) builder.setDeleteIntent(cleanPendingIntent) - val myNotificationId = notificationId - - if (notificationTimeoutMilliSecs != NEVER) { - cleanCopyNotificationTimerTask = Thread { - val maxPos = 100 - val posDurationMills = notificationTimeoutMilliSecs / maxPos - for (pos in maxPos downTo 0) { - val newGeneratedValue = fieldToCopy.getGeneratedValue(mEntryInfo) - // New auto generated value - if (generatedValue != newGeneratedValue) { - generatedValue = newGeneratedValue - clipboardHelper?.copyToClipboard(fieldToCopy.label, generatedValue) - } - builder.setProgress(maxPos, pos, false) - notificationManager?.notify(myNotificationId, builder.build()) - try { - Thread.sleep(posDurationMills) - } catch (e: InterruptedException) { - break - } - if (pos <= 0) { - stopNotificationAndSendLockIfNeeded() - } + if (mNotificationTimeoutMilliSecs != NEVER) { + defineTimerJob(builder, mNotificationTimeoutMilliSecs, { + val newGeneratedValue = fieldToCopy.getGeneratedValue(mEntryInfo) + // New auto generated value + if (generatedValue != newGeneratedValue) { + generatedValue = newGeneratedValue + clipboardHelper?.copyToClipboard(fieldToCopy.label, generatedValue) } - stopTask(cleanCopyNotificationTimerTask) - notificationManager?.cancel(myNotificationId) + }) { + stopNotificationAndSendLockIfNeeded() // Clean password only if no next field if (nextFields.size <= 0) cleanClipboard() } - cleanCopyNotificationTimerTask?.start() } else { // No timer - notificationManager?.notify(myNotificationId, builder.build()) + notificationManager?.notify(notificationId, builder.build()) } } catch (e: Exception) { @@ -228,10 +210,6 @@ class ClipboardEntryNotificationService : LockNotificationService() { override fun onDestroy() { cleanClipboard() - - stopTask(cleanCopyNotificationTimerTask) - cleanCopyNotificationTimerTask = null - super.onDestroy() } diff --git a/app/src/main/java/com/kunzisoft/keepass/services/KeyboardEntryNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/KeyboardEntryNotificationService.kt index af214e50b..ce13eecc9 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/KeyboardEntryNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/KeyboardEntryNotificationService.kt @@ -35,8 +35,7 @@ import com.kunzisoft.keepass.utils.LOCK_ACTION class KeyboardEntryNotificationService : LockNotificationService() { override val notificationId = 486 - private var cleanNotificationTimerTask: Thread? = null - private var notificationTimeoutMilliSecs: Long = 0 + private var mNotificationTimeoutMilliSecs: Long = 0 private var pendingDeleteIntent: PendingIntent? = null @@ -61,7 +60,7 @@ class KeyboardEntryNotificationService : LockNotificationService() { super.onStartCommand(intent, flags, startId) //Get settings - notificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this) + mNotificationTimeoutMilliSecs = PreferenceManager.getDefaultSharedPreferences(this) .getString(getString(R.string.keyboard_entry_timeout_key), getString(R.string.timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT @@ -107,27 +106,12 @@ class KeyboardEntryNotificationService : LockNotificationService() { notificationManager?.cancel(notificationId) notificationManager?.notify(notificationId, builder.build()) - stopTask(cleanNotificationTimerTask) // Timeout only if notification clear is available if (PreferencesUtil.isClearKeyboardNotificationEnable(this)) { - if (notificationTimeoutMilliSecs != TimeoutHelper.NEVER) { - cleanNotificationTimerTask = Thread { - val maxPos = 100 - val posDurationMills = notificationTimeoutMilliSecs / maxPos - for (pos in maxPos downTo 0) { - builder.setProgress(maxPos, pos, false) - notificationManager?.notify(notificationId, builder.build()) - try { - Thread.sleep(posDurationMills) - } catch (e: InterruptedException) { - break - } - if (pos <= 0) { - stopNotificationAndSendLockIfNeeded() - } - } + if (mNotificationTimeoutMilliSecs != TimeoutHelper.NEVER) { + defineTimerJob(builder, mNotificationTimeoutMilliSecs) { + stopNotificationAndSendLockIfNeeded() } - cleanNotificationTimerTask?.start() } } } @@ -142,8 +126,6 @@ class KeyboardEntryNotificationService : LockNotificationService() { // Remove the entry from the keyboard MagikIME.removeEntry(this) - stopTask(cleanNotificationTimerTask) - cleanNotificationTimerTask = null pendingDeleteIntent?.cancel() super.onDestroy() diff --git a/app/src/main/java/com/kunzisoft/keepass/services/LockNotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/LockNotificationService.kt index daf37a124..e1865cf1e 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/LockNotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/LockNotificationService.kt @@ -50,11 +50,6 @@ abstract class LockNotificationService : NotificationService() { return super.onStartCommand(intent, flags, startId) } - protected fun stopTask(task: Thread?) { - if (task != null && task.isAlive) - task.interrupt() - } - override fun onTaskRemoved(rootIntent: Intent?) { notificationManager?.cancel(notificationId) diff --git a/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt b/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt index e475ad4eb..f9faf3c93 100644 --- a/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt +++ b/app/src/main/java/com/kunzisoft/keepass/services/NotificationService.kt @@ -11,6 +11,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.stylish.Stylish +import kotlinx.coroutines.* abstract class NotificationService : Service() { @@ -18,6 +19,8 @@ abstract class NotificationService : Service() { protected var notificationManager: NotificationManagerCompat? = null private var colorNotificationAccent: Int = 0 + protected var mTimerJob: Job? = null + protected abstract val notificationId: Int override fun onBind(intent: Intent): IBinder? { @@ -71,7 +74,33 @@ abstract class NotificationService : Service() { } } + protected fun defineTimerJob(builder: NotificationCompat.Builder, + timeoutMilliseconds: Long, + actionAfterASecond: (() -> Unit)? = null, + actionEnd: () -> Unit) { + mTimerJob?.cancel() + mTimerJob = CoroutineScope(Dispatchers.Main).launch { + val timeoutInSeconds = timeoutMilliseconds / 1000L + for (currentTime in timeoutInSeconds downTo 0) { + actionAfterASecond?.invoke() + builder.setProgress(100, + (currentTime * 100 / timeoutInSeconds).toInt(), + false) + startForeground(notificationId, builder.build()) + delay(1000) + if (currentTime <= 0) { + actionEnd() + } + } + notificationManager?.cancel(notificationId) + mTimerJob = null + cancel() + } + } + override fun onDestroy() { + mTimerJob?.cancel() + mTimerJob = null notificationManager?.cancel(notificationId) super.onDestroy() From a3c51884f4a7790c5aebf061eea808894895f85f Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 10 Apr 2021 20:53:24 +0200 Subject: [PATCH 24/25] Fix timeout strings --- app/src/main/res/values-ca/strings.xml | 11 --------- app/src/main/res/values-cs/strings.xml | 11 --------- app/src/main/res/values-da/strings.xml | 11 --------- app/src/main/res/values-de/strings.xml | 11 --------- app/src/main/res/values-el/strings.xml | 11 --------- app/src/main/res/values-es/strings.xml | 11 --------- app/src/main/res/values-eu/strings.xml | 11 --------- app/src/main/res/values-fi/strings.xml | 11 --------- app/src/main/res/values-fr/strings.xml | 11 --------- app/src/main/res/values-hu/strings.xml | 11 --------- app/src/main/res/values-it/strings.xml | 11 --------- app/src/main/res/values-iw/strings.xml | 11 --------- app/src/main/res/values-ja/strings.xml | 11 --------- app/src/main/res/values-lv/strings.xml | 11 --------- app/src/main/res/values-nl/strings.xml | 11 --------- app/src/main/res/values-nn/strings.xml | 11 --------- app/src/main/res/values-pl/strings.xml | 11 --------- app/src/main/res/values-pt-rBR/strings.xml | 11 --------- app/src/main/res/values-pt-rPT/strings.xml | 11 --------- app/src/main/res/values-ru/strings.xml | 11 --------- app/src/main/res/values-sk/strings.xml | 11 --------- app/src/main/res/values-sv/strings.xml | 11 --------- app/src/main/res/values-uk/strings.xml | 11 --------- app/src/main/res/values-zh-rCN/strings.xml | 11 --------- app/src/main/res/values-zh-rTW/strings.xml | 11 --------- app/src/main/res/values/strings.xml | 27 +--------------------- 26 files changed, 1 insertion(+), 301 deletions(-) diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index d1b275412..931119bd8 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -122,17 +122,6 @@ Majúscules Versió %1$s Introdueix una contrasenya i/o un arxiu clau per desbloquejar la base de dades. - - 5 segons - 10 segons - 20 segons - 30 segons - 1 minut - 5 minuts - 15 minuts - 30 minuts - Mai - Petita Mitjana diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index c8bfc69df..831469384 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -133,17 +133,6 @@ Databázi odemknete zadáním hesla a/nebo souboru s klíčem. \n \nNezapomeňte po každé úpravě zálohovat kopii svého .kdbx souboru na bezpečné místo. - - 5 sekund - 10 sekund - 20 sekund - 30 sekund - 1 minuta - 5 minut - 15 minut - 30 minut - Nikdy - Malý Střední diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 11ad6029b..7d1c9e228 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -132,17 +132,6 @@ Angiv en adgangskode og/eller en nøglefil til at låse databasen op. \n \nHusk at gemme en kopi af .kdbx filen i et sikkert sted efter hver ændring. - - 5 sekunder - 10 sekunder - 20 sekunder - 30 sekunder - 1 minut - 5 minutter - 15 minutter - 30 minutter - Aldrig - Lille Mellem diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 89fd2907b..cdb8a4560 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -147,17 +147,6 @@ Geben Sie das Passwort und/oder die Schlüsseldatei ein, um Ihre Datenbank zu entsperren. \n \nSichern Sie Ihre Datenbankdatei nach jeder Änderung an einem sicheren Ort. - - 5 Sekunden - 10 Sekunden - 20 Sekunden - 30 Sekunden - 1 Minute - 5 Minuten - 15 Minuten - 30 Minuten - Nie - Klein Mittel diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1c275fa1b..030dbd271 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -135,17 +135,6 @@ Καταχωρίστε τον κωδικό πρόσβασης και /ή το αρχείο-κλειδί για να ξεκλειδώσετε τη βάση δεδομένων σας. \n \nΔημιουργήστε αντίγραφα ασφαλείας του αρχείου βάσης δεδομένων σας, σε ασφαλές μέρος μετά από κάθε αλλαγή. - - 5 δευτερόλεπτα - 10 δευτερόλεπτα - 20 δευτερόλεπτα - 30 δευτερόλεπτα - 1 λεπτό - 5 λεπτά - 15 λεπτά - 30 λεπτά - Ποτέ - Μικρά Μεσαία diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 38a61cc55..2a326a1c0 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -125,17 +125,6 @@ Introduzca la contraseña y/o el archivo clave para desbloquear su base de datos. \n \nHaga una copia de seguridad de su archivo de base de datos en un lugar seguro después de cada cambio. - - 5 segundos - 10 segundos - 20 segundos - 30 segundos - 1 minuto - 5 minutos - 15 minutos - 30 minutos - Nunca - Pequeño Mediano diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 72e10b689..e501ae3fe 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -132,17 +132,6 @@ Maiuskulak Bertsioa %1$s Sartu pasahitz eta / edo gako fitxategi bat zure datubasea desblokeatzeko. - - 5 segundu - 10 segundu - 20 segundu - 30 segundu - minutu 1 - 5 minutu - 15 minutu - 30 minutu - Inoiz ez - Txikia Ertaina diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index a26f69785..17825f5ad 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -132,17 +132,6 @@ Isot kirjaimet Versio %1$s Syötä salasana ja/tai avaintiedosto avataksesi tietokantasi. - - 5 sekuntia - 10 sekuntia - 20 sekuntia - 30 sekuntia - 1 minuutti - 5 minuttia - 15 minuttia - 30 minuttia - Ei koskaan - Pieni Keskikokoinen diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 506599704..d22426531 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -258,17 +258,6 @@ N’oubliez pas de garder votre application à jour en installant les nouvelles versions. Télécharger Contribuer - - 5 secondes - 10 secondes - 20 secondes - 30 secondes - 1 minute - 5 minutes - 15 minutes - 30 minutes - Jamais - Petit Moyen diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index e3faae646..42d97e39a 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -140,17 +140,6 @@ Adja meg a jelszót és/vagy a kulcsfájlt, hogy kinyithassa az adatbázist. \n \nKészítsen biztonsági mentést az adatbázisról minden egyes módosítás után. - - 5 másodperc - 10 másodperc - 20 másodperc - 30 másodperc - 1 perc - 5 perc - 15 perc - 30 perc - Soha - Kicsi Közepes diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ad0d3d5f9..3014df4e5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -142,17 +142,6 @@ Inserisci la password o il file chiave per sbloccare la base di dati. \n \nEseguire il backup del file del database in un luogo sicuro dopo ogni modifica. - - 5 secondi - 10 secondi - 20 secondi - 30 secondi - 1 minuto - 5 minuti - 15 minuti - 30 minuti - Mai - Piccolo Medio diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index c10c9157e..f1b2a045d 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -129,17 +129,6 @@ רישית גרסה %1$s הזן סיסמה ו/או קובץ מפתח כדי לפתוח את מסד הנתונים. - - 5 שניות - 10 שניות - 20 שניות - 30 שניות - דקה אחת - 5 דקות - 15 דקות - 30 דקות - אף פעם - קטן בינוני diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 4bbeb1d45..8deaf976b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -470,17 +470,6 @@ 進行中:%1$d%% 終了しています… 完了しました! - - 5秒 - 10秒 - 20秒 - 30秒 - 1分 - 5分 - 15分 - 30分 - なし - diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 4cce5e774..f3100b69e 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -129,17 +129,6 @@ Lielie burti Versija %1$s Ievadiet paroli/atslēgas failu, lai atbloķētu savu datu bāzi. - - 5 sekundes - 10 sekundes - 20 sekundes - 30 sekundes - 1 minūte - 5 minūtes - 15 minūtes - 30 minūtes - Nekad - Mazs Vidējs diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 10647efd7..648a4c256 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -125,17 +125,6 @@ Voer het wachtwoord en/of sleutelbestand in om je database te ontgrendelen. \n \nMaak na elke aanpassing een kopie van je .kdbx-bestand op een veilige locatie. - - 5 seconden - 10 seconden - 20 seconden - 30 seconden - 1 minuut - 5 minuten - 15 minuten - 30 minuten - Nooit - Klein Medium diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index be7d4839e..71da3ef73 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -120,17 +120,6 @@ Store bokstavar Utgåve %1$s Skriv inn passordet og/eller nøkkelfil for å låsa opp databasen. - - 5 sekund - 10 sekund - 20 sekund - 30 sekund - 1 minutt - 5 minutt - 15 minutt - 30 minutt - Aldri - Liten Middels diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 00c603379..3748a8073 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -120,17 +120,6 @@ prowadź hasło i/lub plik klucza, aby odblokować bazę danych. \n \nUtwórz kopię zapasową pliku bazy danych w bezpiecznym miejscu po każdej zmianie. - - 5 sekund - 10 sekund - 20 sekund - 30 sekund - 1 minuta - 5 minut - 15 minut - 30 minut - Nigdy - Mała Średnia diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 6a13abbcf..32d102ad2 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -123,17 +123,6 @@ Entre com a senha e/ou com o caminho para o arquivo-chave do banco de dados. \n \nGuarde uma cópia do seu arquivo do banco em um lugar mais seguro depois de cada alteração. - - 5 segundos - 10 segundos - 20 segundos - 30 segundos - 1 minuto - 5 minutos - 15 minutos - 30 minutos - Nunca - Pequeno Médio diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 2ee8a71cb..a0810454c 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -139,17 +139,6 @@ Entre com a palavra-passe e/ou com o caminho para o ficheiro-chave da base de dados. \n \nGuarde uma cópia do seu ficheiro do banco num lugar mais seguro depois de cada alteração. - - 5 segundos - 10 segundos - 20 segundos - 30 segundos - 1 minuto - 5 minutos - 15 minutos - 30 minutos - Nunca - Pequena Média diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4fdd40cbd..ba177823f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -141,17 +141,6 @@ Введите пароль и/или файл ключа, чтобы разблокировать базу. \n \nНе забывайте сохранять копию файла базы в безопасном месте после каждого изменения. - - 5 секунд - 10 секунд - 20 секунд - 30 секунд - 1 минута - 5 минут - 15 минут - 30 минут - Никогда - Мелкий Обычный diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index eaeee45ca..7edb79f9e 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -120,17 +120,6 @@ Veľké písmená Version %1$s Vložte heslo a / alebo keyfile pre odomknutie databázy. - - 5 sekúnd - 10 sekúnd - 20 sekúnd - 30 sekúnd - 1 minúta - 5 minút - 15 minút - 30 minút - Nikdy - Malé Stredné diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 6b8bbd01a..fc946bedf 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -133,17 +133,6 @@ Ange lösenord och/eller nyckelfil för att öppna databasen. \n \nBacka upp databasfilen på ett säkert ställe efter varje ändring. - - 5 sekunder - 10 sekunder - 20 sekunder - 30 sekunder - 1 minut - 5 minuter - 15 minuter - 30 minuter - Aldrig - Liten Medium diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 719bc2a46..cc7664ae3 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -123,17 +123,6 @@ Введіть пароль та/або файл ключа, щоб відкрити базу даних. \n \nСтворюйте резервну копію файлу бази даних після кожної внесеної зміни та зберігайте її у безпечному місці. - - 5 секунд - 10 секунд - 20 секунд - 30 секунд - 1 хвилина - 5 хвилин - 15 хвилин - 30 хвилин - Ніколи - Малий Середній diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 616f99f29..6dfa67dca 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -119,17 +119,6 @@ 输入密码和/或密钥文件来解锁你的数据库。 \n \n记得在每次做出更改后,将数据库文件备份至安全的地方。 - - 5秒 - 10秒 - 20秒 - 30秒 - 1分钟 - 5分钟 - 15分钟 - 30分钟 - 从不 - diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 978a77d85..d9603a2cb 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -117,17 +117,6 @@ 不支援的資料庫版本。 大寫 輸入密碼和/或一個密鑰檔來解鎖你的資料庫. - - 5秒 - 10秒 - 20秒 - 30秒 - 1分鐘 - 5分鐘 - 15分鐘 - 30分鐘 - 從不 - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ba624eadd..21475de8e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,7 +31,7 @@ Encryption Encryption algorithm Key derivation function - App timeout + Timeout Idle time before locking the database App Brackets @@ -535,31 +535,6 @@ KiB MiB GiB - - 5 seconds - 10 seconds - 20 seconds - 30 seconds - 1 minute - 5 minutes - 15 minutes - 30 minutes - Never - - - 5 minutes - 15 minutes - 30 minutes - 1 hour - 2 hours - 5 hours - 10 hours - 24 hours - 48 hours - 1 week - 1 month - Never - Small Medium From 11aae77caf55f550f452325d50f6ca4884a9fd43 Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sat, 10 Apr 2021 20:56:25 +0200 Subject: [PATCH 25/25] Update CHANGELOG --- CHANGELOG | 1 + fastlane/metadata/android/en-US/changelogs/71.txt | 3 ++- fastlane/metadata/android/fr-FR/changelogs/71.txt | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eb3566ed5..cc4f5458f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ KeePassDX(2.9.17) * Import / Export app properties #839 * Force twofish padding compatibility #955 + * Better timeout preference #579 KeePassDX(2.9.16) * Fix small bugs #948 diff --git a/fastlane/metadata/android/en-US/changelogs/71.txt b/fastlane/metadata/android/en-US/changelogs/71.txt index ebeb342ed..0834ca47a 100644 --- a/fastlane/metadata/android/en-US/changelogs/71.txt +++ b/fastlane/metadata/android/en-US/changelogs/71.txt @@ -1,2 +1,3 @@ * Import / Export app properties #839 - * Force twofish padding compatibility #955 \ No newline at end of file + * Force twofish padding compatibility #955 + * Better timeout preference #579 \ No newline at end of file diff --git a/fastlane/metadata/android/fr-FR/changelogs/71.txt b/fastlane/metadata/android/fr-FR/changelogs/71.txt index 8b0fda423..4e8d5a849 100644 --- a/fastlane/metadata/android/fr-FR/changelogs/71.txt +++ b/fastlane/metadata/android/fr-FR/changelogs/71.txt @@ -1,2 +1,3 @@ * Import / Export des propriétés de l'application #839 - * Force la compatibilité du bourrage twofish #955 \ No newline at end of file + * Force la compatibilité du bourrage twofish #955 + * Meilleure préférence d'expiration #579 \ No newline at end of file