Merge branch 'extract-database' of github.com:GianpaMX/KeePassDX into GianpaMX-extract-database

This commit is contained in:
J-Jamet 2023-05-08 15:19:32 +02:00
commit 48cc8b3f75
207 changed files with 1469 additions and 862 deletions

View file

@ -21,13 +21,6 @@ android {
buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
manifestPlaceholders = [ googleAndroidBackupAPIKey:"unused" ]
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
}
buildTypes {
@ -129,8 +122,9 @@ dependencies {
implementation 'commons-codec:commons-codec:1.15'
// Password generator
implementation 'me.gosimple:nbvcxz:1.5.0'
// Encrypt lib
implementation project(path: ':crypto')
implementation project(path: ':database')
// Icon pack
implementation project(path: ':icon-pack-classic')
implementation project(path: ':icon-pack-material')

View file

@ -59,6 +59,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
import com.kunzisoft.keepass.database.exception.getLocalizedMessage
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.*

View file

@ -78,7 +78,8 @@ class DatabaseChangedDialogFragment : DatabaseDialogFragment() {
private const val NEW_FILE_DATABASE_INFO = "NEW_FILE_DATABASE_INFO"
fun getInstance(oldSnapFileDatabaseInfo: SnapFileDatabaseInfo,
newSnapFileDatabaseInfo: SnapFileDatabaseInfo)
newSnapFileDatabaseInfo: SnapFileDatabaseInfo
)
: DatabaseChangedDialogFragment {
val fragment = DatabaseChangedDialogFragment()
fragment.arguments = Bundle().apply {

View file

@ -32,7 +32,6 @@ import android.view.inputmethod.EditorInfo
import android.widget.*
import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.OtpModel
import com.kunzisoft.keepass.otp.OtpElement

View file

@ -17,6 +17,7 @@ import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
import com.kunzisoft.keepass.database.element.Attachment
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.getLocalizedName
import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.StreamDirection

View file

@ -322,7 +322,7 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
mDatabase?.let { database ->
// If recycle bin enabled, ensure it exists
if (database.isRecycleBinEnabled) {
database.ensureRecycleBinExists(resources)
database.ensureRecycleBinExists(resources.getString(R.string.recycle_bin))
}
// If recycle bin enabled and not in recycle bin, move in recycle bin

View file

@ -33,7 +33,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.ImageViewerActivity
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.model.AttachmentState
import com.kunzisoft.keepass.model.EntryAttachmentState
import com.kunzisoft.keepass.model.StreamDirection
@ -130,7 +130,7 @@ class EntryAttachmentsItemsAdapter(context: Context)
holder.binaryFileSize.text = Formatter.formatFileSize(context, size)
holder.binaryFileCompression.apply {
if (entryAttachmentState.attachment.binaryData.isCompressed) {
text = CompressionAlgorithm.GZip.getName(context.resources)
text = NamedCompressionAlgorithm.GZip.getName(context.resources)
visibility = View.VISIBLE
} else {
text = ""

View file

@ -42,6 +42,7 @@ import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeVersionedInterface
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.getLocalizedName
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpType
import com.kunzisoft.keepass.settings.PreferencesUtil

View file

@ -11,6 +11,7 @@ import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.template.Template
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.getLocalizedName
import com.kunzisoft.keepass.icons.IconDrawableFactory

View file

@ -19,7 +19,10 @@
*/
package com.kunzisoft.keepass.app.database
import android.content.*
import android.content.ComponentName
import android.content.Context
import android.content.IntentFilter
import android.content.ServiceConnection
import android.net.Uri
import android.os.IBinder
import android.util.Base64
@ -28,15 +31,13 @@ import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter
import java.util.*
import java.util.LinkedList
class CipherDatabaseAction(context: Context) {
private val applicationContext = context.applicationContext
private val cipherDatabaseDao =
AppDatabase
.getDatabase(applicationContext)
.cipherDatabaseDao()
AppDatabase.getDatabase(applicationContext).cipherDatabaseDao()
// Temp DAO to easily remove content if object no longer in memory
private var useTempDao = PreferencesUtil.isTempAdvancedUnlockEnable(applicationContext)
@ -149,7 +150,8 @@ class CipherDatabaseAction(context: Context) {
} else {
IOActionTask(
{
cipherDatabaseDao.getByDatabaseUri(databaseUri.toString())?.let { cipherDatabaseEntity ->
cipherDatabaseDao.getByDatabaseUri(databaseUri.toString())
?.let { cipherDatabaseEntity ->
CipherEncryptDatabase().apply {
this.databaseUri = Uri.parse(cipherDatabaseEntity.databaseUri)
this.encryptedValue = Base64.decode(

View file

@ -26,28 +26,30 @@ import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
class FileDatabaseHistoryAction(private val applicationContext: Context) {
private val databaseFileHistoryDao =
AppDatabase
.getDatabase(applicationContext)
.fileDatabaseHistoryDao()
AppDatabase.getDatabase(applicationContext).fileDatabaseHistoryDao()
fun getDatabaseFile(databaseUri: Uri,
databaseFileResult: (DatabaseFile?) -> Unit) {
IOActionTask(
{
val fileDatabaseHistoryEntity = databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, databaseUri)
val fileDatabaseHistoryEntity =
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
val fileDatabaseInfo = FileDatabaseInfo(
applicationContext,
databaseUri)
DatabaseFile(
databaseUri,
UriUtil.parse(fileDatabaseHistoryEntity?.keyFileUri),
UriUtilDatabase.parse(fileDatabaseHistoryEntity?.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
UriUtil.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""),
UriUtilDatabase.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
?: ""),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
@ -68,7 +70,8 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
{
it?.let { fileHistoryEntity ->
fileHistoryEntity.keyFileUri?.let { keyFileUri ->
keyFileUriResultListener.invoke(UriUtil.parse(keyFileUri))
keyFileUriResultListener.invoke(UriUtilDatabase.parse(
keyFileUri))
}
} ?: keyFileUriResultListener.invoke(null)
}
@ -78,19 +81,24 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
fun getDatabaseFileList(databaseFileListResult: (List<DatabaseFile>) -> Unit) {
IOActionTask(
{
val hideBrokenLocations = PreferencesUtil.hideBrokenLocations(applicationContext)
val hideBrokenLocations =
PreferencesUtil.hideBrokenLocations(
applicationContext)
// Show only uri accessible
val databaseFileListLoaded = ArrayList<DatabaseFile>()
databaseFileHistoryDao.getAll().forEach { fileDatabaseHistoryEntity ->
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, fileDatabaseHistoryEntity.databaseUri)
val fileDatabaseInfo = FileDatabaseInfo(
applicationContext,
fileDatabaseHistoryEntity.databaseUri)
if (hideBrokenLocations && fileDatabaseInfo.exists
|| !hideBrokenLocations) {
|| !hideBrokenLocations
) {
databaseFileListLoaded.add(
DatabaseFile(
UriUtil.parse(fileDatabaseHistoryEntity.databaseUri),
UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri),
UriUtilDatabase.parse(fileDatabaseHistoryEntity.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistoryEntity.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
UriUtil.decode(fileDatabaseHistoryEntity.databaseUri),
UriUtilDatabase.decode(fileDatabaseHistoryEntity.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
@ -101,8 +109,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
}
databaseFileListLoaded
},
{
databaseFileList ->
{ databaseFileList ->
databaseFileList?.let {
databaseFileListResult.invoke(it)
}
@ -127,10 +134,12 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
{
databaseFileToAddOrUpdate.databaseUri?.let { databaseUri ->
// Try to get info in database first
val fileDatabaseHistoryRetrieve = databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
val fileDatabaseHistoryRetrieve =
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
// Complete alias if not exists
val fileDatabaseHistory = FileDatabaseHistoryEntity(
val fileDatabaseHistory =
FileDatabaseHistoryEntity(
databaseUri.toString(),
databaseFileToAddOrUpdate.databaseAlias
?: fileDatabaseHistoryRetrieve?.databaseAlias
@ -151,13 +160,14 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
Log.e(TAG, "Unable to add or update database history", e)
}
val fileDatabaseInfo = FileDatabaseInfo(applicationContext,
val fileDatabaseInfo =
FileDatabaseInfo(applicationContext,
fileDatabaseHistory.databaseUri)
DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri),
UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtil.decode(fileDatabaseHistory.databaseUri),
UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
@ -176,14 +186,15 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
IOActionTask(
{
databaseFileToDelete.databaseUri?.let { databaseUri ->
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())?.let { fileDatabaseHistory ->
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
?.let { fileDatabaseHistory ->
val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory)
if (returnValue > 0) {
DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri),
UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtil.decode(fileDatabaseHistory.databaseUri),
UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
databaseFileToDelete.databaseAlias
)
} else {

View file

@ -29,7 +29,6 @@ import android.os.CancellationSignal
import android.service.autofill.*
import android.util.Log
import android.view.autofill.AutofillId
import android.view.inputmethod.InlineSuggestionsRequest
import android.widget.RemoteViews
import androidx.annotation.RequiresApi
import androidx.autofill.inline.UiVersions

View file

@ -19,6 +19,7 @@
*/
package com.kunzisoft.keepass.database.action
import android.app.AlertDialog
import android.content.*
import android.content.Context.*
import android.net.Uri
@ -26,7 +27,6 @@ import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R
@ -38,7 +38,7 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
@ -584,8 +584,8 @@ class DatabaseTaskProvider(private var context: Context,
, ACTION_DATABASE_UPDATE_COLOR_TASK)
}
fun startDatabaseSaveCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm,
fun startDatabaseSaveCompression(oldCompression: NamedCompressionAlgorithm,
newCompression: NamedCompressionAlgorithm,
save: Boolean) {
start(Bundle().apply {
putSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY, oldCompression)

View file

@ -24,17 +24,18 @@ import android.net.Uri
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseInputException
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtilDatabase
class LoadDatabaseRunnable(private val context: Context,
class LoadDatabaseRunnable(
private val context: Context,
private val mDatabase: Database,
private val mDatabaseUri: Uri,
private val mMainCredential: MainCredential,
@ -43,8 +44,8 @@ class LoadDatabaseRunnable(private val context: Context,
private val mCipherEncryptDatabase: CipherEncryptDatabase?,
private val mFixDuplicateUUID: Boolean,
private val progressTaskUpdater: ProgressTaskUpdater?,
private val mLoadDatabaseResult: ((Result) -> Unit)?)
: ActionRunnable() {
private val mLoadDatabaseResult: ((Result) -> Unit)?,
) : ActionRunnable() {
override fun onStartRun() {
// Clear before we load
@ -59,15 +60,14 @@ class LoadDatabaseRunnable(private val context: Context,
mMainCredential,
mChallengeResponseRetriever,
mReadonly,
UriUtil.getBinaryDir(context),
UriUtilDatabase.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
mFixDuplicateUUID,
progressTaskUpdater
)
}
catch (e: DatabaseInputException) {
} catch (e: DatabaseInputException) {
setError(e)
}

View file

@ -31,7 +31,7 @@ import com.kunzisoft.keepass.utils.writeEnum
// Note: We can get away with using int's to store unsigned 32-bit ints
// since we won't do arithmetic on these values (also unlikely to
// reach negative ids).
enum class CompressionAlgorithm : ObjectNameResource, Parcelable {
enum class NamedCompressionAlgorithm : ObjectNameResource, Parcelable {
None,
GZip;
@ -51,14 +51,24 @@ enum class CompressionAlgorithm : ObjectNameResource, Parcelable {
}
}
companion object CREATOR : Parcelable.Creator<CompressionAlgorithm> {
override fun createFromParcel(parcel: Parcel): CompressionAlgorithm {
return parcel.readEnum<CompressionAlgorithm>() ?: None
companion object CREATOR : Parcelable.Creator<NamedCompressionAlgorithm> {
override fun createFromParcel(parcel: Parcel): NamedCompressionAlgorithm {
return parcel.readEnum<NamedCompressionAlgorithm>() ?: None
}
override fun newArray(size: Int): Array<CompressionAlgorithm?> {
override fun newArray(size: Int): Array<NamedCompressionAlgorithm?> {
return arrayOfNulls(size)
}
}
}
fun CompressionAlgorithm.toNamedCompressionAlgorithm() = when(this) {
CompressionAlgorithm.None -> NamedCompressionAlgorithm.None
CompressionAlgorithm.GZip -> NamedCompressionAlgorithm.GZip
}
fun NamedCompressionAlgorithm.toCompressionAlgorithm() = when(this) {
NamedCompressionAlgorithm.None -> CompressionAlgorithm.None
NamedCompressionAlgorithm.GZip -> CompressionAlgorithm.GZip
}

View file

@ -1,125 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.kunzisoft.keepass.database.element.template
import android.content.Context
import com.kunzisoft.keepass.R
object TemplateField {
const val LABEL_STANDARD = "Standard"
const val LABEL_TEMPLATE = "Template"
const val LABEL_VERSION = "Version"
const val LABEL_TITLE = "Title"
const val LABEL_USERNAME = "Username"
const val LABEL_PASSWORD = "Password"
const val LABEL_URL = "URL"
const val LABEL_EXPIRATION = "Expires"
const val LABEL_NOTES = "Notes"
const val LABEL_DEBIT_CREDIT_CARD = "Debit / Credit Card"
const val LABEL_HOLDER = "Holder"
const val LABEL_NUMBER = "Number"
const val LABEL_CVV = "CVV"
const val LABEL_PIN = "PIN"
const val LABEL_ID_CARD = "ID Card"
const val LABEL_NAME = "Name"
const val LABEL_PLACE_OF_ISSUE = "Place of issue"
const val LABEL_DATE_OF_ISSUE = "Date of issue"
const val LABEL_EMAIL = "Email"
const val LABEL_EMAIL_ADDRESS = "Email address"
const val LABEL_WIRELESS = "Wi-Fi"
const val LABEL_SSID = "SSID"
const val LABEL_TYPE = "Type"
const val LABEL_CRYPTOCURRENCY = "Cryptocurrency wallet"
const val LABEL_TOKEN = "Token"
const val LABEL_PUBLIC_KEY = "Public key"
const val LABEL_PRIVATE_KEY = "Private key"
const val LABEL_SEED = "Seed"
const val LABEL_ACCOUNT = "Account"
const val LABEL_BANK = "Bank"
const val LABEL_BIC = "BIC"
const val LABEL_IBAN = "IBAN"
const val LABEL_SECURE_NOTE = "Secure Note"
const val LABEL_MEMBERSHIP = "Membership"
fun isStandardPasswordName(context: Context, name: String): Boolean {
return name.equals(LABEL_PASSWORD, true)
|| name == getLocalizedName(context, LABEL_PASSWORD)
}
fun isStandardFieldName(name: String): Boolean {
return arrayOf(
LABEL_TITLE,
LABEL_USERNAME,
LABEL_PASSWORD,
LABEL_URL,
LABEL_EXPIRATION,
LABEL_NOTES
).firstOrNull { it.equals(name, true) } != null
}
fun getLocalizedName(context: Context?, name: String): String {
if (context == null
|| TemplateEngine.containsTemplateDecorator(name))
return name
return when {
LABEL_STANDARD.equals(name, true) -> context.getString(R.string.standard)
LABEL_TEMPLATE.equals(name, true) -> context.getString(R.string.template)
LABEL_VERSION.equals(name, true) -> context.getString(R.string.version)
LABEL_TITLE.equals(name, true) -> context.getString(R.string.entry_title)
LABEL_USERNAME.equals(name, true) -> context.getString(R.string.entry_user_name)
LABEL_PASSWORD.equals(name, true) -> context.getString(R.string.entry_password)
LABEL_URL.equals(name, true) -> context.getString(R.string.entry_url)
LABEL_EXPIRATION.equals(name, true) -> context.getString(R.string.entry_expires)
LABEL_NOTES.equals(name, true) -> context.getString(R.string.entry_notes)
LABEL_DEBIT_CREDIT_CARD.equals(name, true) -> context.getString(R.string.debit_credit_card)
LABEL_HOLDER.equals(name, true) -> context.getString(R.string.holder)
LABEL_NUMBER.equals(name, true) -> context.getString(R.string.number)
LABEL_CVV.equals(name, true) -> context.getString(R.string.card_verification_value)
LABEL_PIN.equals(name, true) -> context.getString(R.string.personal_identification_number)
LABEL_ID_CARD.equals(name, true) -> context.getString(R.string.id_card)
LABEL_NAME.equals(name, true) -> context.getString(R.string.name)
LABEL_PLACE_OF_ISSUE.equals(name, true) -> context.getString(R.string.place_of_issue)
LABEL_DATE_OF_ISSUE.equals(name, true) -> context.getString(R.string.date_of_issue)
LABEL_EMAIL.equals(name, true) -> context.getString(R.string.email)
LABEL_EMAIL_ADDRESS.equals(name, true) -> context.getString(R.string.email_address)
LABEL_WIRELESS.equals(name, true) -> context.getString(R.string.wireless)
LABEL_SSID.equals(name, true) -> context.getString(R.string.ssid)
LABEL_TYPE.equals(name, true) -> context.getString(R.string.type)
LABEL_CRYPTOCURRENCY.equals(name, true) -> context.getString(R.string.cryptocurrency)
LABEL_TOKEN.equals(name, false) -> context.getString(R.string.token)
LABEL_PUBLIC_KEY.equals(name, true) -> context.getString(R.string.public_key)
LABEL_PRIVATE_KEY.equals(name, true) -> context.getString(R.string.private_key)
LABEL_SEED.equals(name, true) -> context.getString(R.string.seed)
LABEL_ACCOUNT.equals(name, true) -> context.getString(R.string.account)
LABEL_BANK.equals(name, true) -> context.getString(R.string.bank)
LABEL_BIC.equals(name, true) -> context.getString(R.string.bank_identifier_code)
LABEL_IBAN.equals(name, true) -> context.getString(R.string.international_bank_account_number)
LABEL_SECURE_NOTE.equals(name, true) -> context.getString(R.string.secure_note)
LABEL_MEMBERSHIP.equals(name, true) -> context.getString(R.string.membership)
else -> name
}
}
}

View file

@ -0,0 +1,75 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.kunzisoft.keepass.database.element.template
import android.content.Context
import com.kunzisoft.keepass.R
fun TemplateField.isStandardPasswordName(context: Context, name: String): Boolean {
return name.equals(LABEL_PASSWORD, true)
|| name == getLocalizedName(context, LABEL_PASSWORD)
}
fun TemplateField.getLocalizedName(context: Context?, name: String): String {
if (context == null
|| TemplateEngine.containsTemplateDecorator(name))
return name
return when {
LABEL_STANDARD.equals(name, true) -> context.getString(R.string.standard)
LABEL_TEMPLATE.equals(name, true) -> context.getString(R.string.template)
LABEL_VERSION.equals(name, true) -> context.getString(R.string.version)
LABEL_TITLE.equals(name, true) -> context.getString(R.string.entry_title)
LABEL_USERNAME.equals(name, true) -> context.getString(R.string.entry_user_name)
LABEL_PASSWORD.equals(name, true) -> context.getString(R.string.entry_password)
LABEL_URL.equals(name, true) -> context.getString(R.string.entry_url)
LABEL_EXPIRATION.equals(name, true) -> context.getString(R.string.entry_expires)
LABEL_NOTES.equals(name, true) -> context.getString(R.string.entry_notes)
LABEL_DEBIT_CREDIT_CARD.equals(name, true) -> context.getString(R.string.debit_credit_card)
LABEL_HOLDER.equals(name, true) -> context.getString(R.string.holder)
LABEL_NUMBER.equals(name, true) -> context.getString(R.string.number)
LABEL_CVV.equals(name, true) -> context.getString(R.string.card_verification_value)
LABEL_PIN.equals(name, true) -> context.getString(R.string.personal_identification_number)
LABEL_ID_CARD.equals(name, true) -> context.getString(R.string.id_card)
LABEL_NAME.equals(name, true) -> context.getString(R.string.name)
LABEL_PLACE_OF_ISSUE.equals(name, true) -> context.getString(R.string.place_of_issue)
LABEL_DATE_OF_ISSUE.equals(name, true) -> context.getString(R.string.date_of_issue)
LABEL_EMAIL.equals(name, true) -> context.getString(R.string.email)
LABEL_EMAIL_ADDRESS.equals(name, true) -> context.getString(R.string.email_address)
LABEL_WIRELESS.equals(name, true) -> context.getString(R.string.wireless)
LABEL_SSID.equals(name, true) -> context.getString(R.string.ssid)
LABEL_TYPE.equals(name, true) -> context.getString(R.string.type)
LABEL_CRYPTOCURRENCY.equals(name, true) -> context.getString(R.string.cryptocurrency)
LABEL_TOKEN.equals(name, false) -> context.getString(R.string.token)
LABEL_PUBLIC_KEY.equals(name, true) -> context.getString(R.string.public_key)
LABEL_PRIVATE_KEY.equals(name, true) -> context.getString(R.string.private_key)
LABEL_SEED.equals(name, true) -> context.getString(R.string.seed)
LABEL_ACCOUNT.equals(name, true) -> context.getString(R.string.account)
LABEL_BANK.equals(name, true) -> context.getString(R.string.bank)
LABEL_BIC.equals(name, true) -> context.getString(R.string.bank_identifier_code)
LABEL_IBAN.equals(name, true) -> context.getString(R.string.international_bank_account_number)
LABEL_SECURE_NOTE.equals(name, true) -> context.getString(R.string.secure_note)
LABEL_MEMBERSHIP.equals(name, true) -> context.getString(R.string.membership)
else -> name
}
}

View file

@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.kunzisoft.keepass.database.exception
import android.content.res.Resources
import com.kunzisoft.keepass.R
fun DatabaseException.getLocalizedMessage(resources: Resources): String = parameters?.let {
when (this) {
is FileNotFoundDatabaseException -> resources.getString(R.string.file_not_found_content)
is CorruptedDatabaseException -> resources.getString(R.string.corrupted_file)
is InvalidAlgorithmDatabaseException -> resources.getString(R.string.invalid_algorithm)
is UnknownDatabaseLocationException -> resources.getString(R.string.error_location_unknown)
is HardwareKeyDatabaseException -> resources.getString(R.string.error_hardware_key_unsupported)
is EmptyKeyDatabaseException -> resources.getString(R.string.error_empty_key)
is SignatureDatabaseException -> resources.getString(R.string.invalid_db_sig)
is VersionDatabaseException -> resources.getString(R.string.unsupported_db_version)
is InvalidCredentialsDatabaseException -> resources.getString(R.string.invalid_credentials)
is KDFMemoryDatabaseException -> resources.getString(R.string.error_load_database_KDF_memory)
is NoMemoryDatabaseException -> resources.getString(R.string.error_out_of_memory)
is DuplicateUuidDatabaseException -> resources.getString(R.string.invalid_db_same_uuid)
is XMLMalformedDatabaseException -> resources.getString(R.string.error_XML_malformed)
is MergeDatabaseKDBException -> resources.getString(R.string.error_unable_merge_database_kdb)
is MoveEntryDatabaseException -> resources.getString(R.string.error_move_entry_here)
is MoveGroupDatabaseException -> resources.getString(R.string.error_move_group_here)
is CopyEntryDatabaseException -> resources.getString(R.string.error_copy_entry_here)
is CopyGroupDatabaseException -> resources.getString(R.string.error_copy_group_here)
is DatabaseInputException -> resources.getString(R.string.error_load_database)
is DatabaseOutputException -> resources.getString(R.string.error_save_database)
else -> (mThrowable as? DatabaseException)?.getLocalizedMessage(resources)
}
} ?: resources.getString(R.string.error_load_database)

View file

@ -0,0 +1,144 @@
package com.kunzisoft.keepass.hardware
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import kotlinx.coroutines.launch
class HardwareKeyResponseHelper {
private var activity: FragmentActivity? = null
private var fragment: Fragment? = null
private var getChallengeResponseResultLauncher: ActivityResultLauncher<Intent>? = null
constructor(context: FragmentActivity) {
this.activity = context
this.fragment = null
}
constructor(context: Fragment) {
this.activity = context.activity
this.fragment = context
}
fun buildHardwareKeyResponse(onChallengeResponded: (challengeResponse: ByteArray?,
extra: Bundle?) -> Unit) {
val resultCallback = ActivityResultCallback<ActivityResult> { result ->
if (result.resultCode == Activity.RESULT_OK) {
val challengeResponse: ByteArray? = result.data?.getByteArrayExtra(HARDWARE_KEY_RESPONSE_KEY)
Log.d(TAG, "Response form challenge")
onChallengeResponded.invoke(challengeResponse,
result.data?.getBundleExtra(EXTRA_BUNDLE_KEY))
} else {
Log.e(TAG, "Response from challenge error")
onChallengeResponded.invoke(null,
result.data?.getBundleExtra(EXTRA_BUNDLE_KEY))
}
}
getChallengeResponseResultLauncher = if (fragment != null) {
fragment?.registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
resultCallback
)
} else {
activity?.registerForActivityResult(
ActivityResultContracts.StartActivityForResult(),
resultCallback
)
}
}
fun launchChallengeForResponse(hardwareKey: HardwareKey, seed: ByteArray?) {
when (hardwareKey) {
/*
HardwareKey.FIDO2_SECRET -> {
// TODO FIDO2 under development
throw Exception("FIDO2 not implemented")
}
*/
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
// Transform the seed before sending
var challenge: ByteArray? = null
if (seed != null) {
challenge = ByteArray(64)
seed.copyInto(challenge, 0, 0, 32)
challenge.fill(32, 32, 64)
}
// Send to the driver
getChallengeResponseResultLauncher!!.launch(
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT).apply {
putExtra(HARDWARE_KEY_CHALLENGE_KEY, challenge)
}
)
Log.d(TAG, "Challenge sent")
}
}
}
companion object {
private val TAG = HardwareKeyResponseHelper::class.java.simpleName
private const val YUBIKEY_CHALLENGE_RESPONSE_INTENT = "android.yubikey.intent.action.CHALLENGE_RESPONSE"
private const val HARDWARE_KEY_CHALLENGE_KEY = "challenge"
private const val HARDWARE_KEY_RESPONSE_KEY = "response"
private const val EXTRA_BUNDLE_KEY = "EXTRA_BUNDLE_KEY"
fun isHardwareKeyAvailable(
activity: FragmentActivity,
hardwareKey: HardwareKey,
showDialog: Boolean = true
): Boolean {
return when (hardwareKey) {
/*
HardwareKey.FIDO2_SECRET -> {
// TODO FIDO2 under development
if (showDialog)
UnderDevelopmentFeatureDialogFragment()
.show(activity.supportFragmentManager, "underDevFeatureDialog")
false
}
*/
HardwareKey.CHALLENGE_RESPONSE_YUBIKEY -> {
// Check available intent
val yubikeyDriverAvailable =
Intent(YUBIKEY_CHALLENGE_RESPONSE_INTENT)
.resolveActivity(activity.packageManager) != null
if (showDialog && !yubikeyDriverAvailable)
showHardwareKeyDriverNeeded(activity, hardwareKey)
yubikeyDriverAvailable
}
}
}
private fun showHardwareKeyDriverNeeded(
activity: FragmentActivity,
hardwareKey: HardwareKey
) {
activity.lifecycleScope.launch {
val builder = AlertDialog.Builder(activity)
builder
.setMessage(
activity.getString(R.string.error_driver_required, hardwareKey.toString())
)
.setPositiveButton(R.string.download) { _, _ ->
UriUtil.openExternalApp(activity, activity.getString(R.string.key_driver_app_id))
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show()
}
}
}
}

View file

@ -22,20 +22,22 @@ package com.kunzisoft.keepass.icons
import android.content.Context
import android.util.Log
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.settings.PreferencesUtil
import java.util.*
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import java.util.ArrayList
/**
* Utility class to built and select an IconPack dynamically by libraries importation
*
* @author J-Jamet
*/
object IconPackChooser {
object IconPackChooser : InterfaceIconPackChooser {
private val TAG = IconPackChooser::class.java.name
private val iconPackList = ArrayList<IconPack>()
private var iconPackSelected: IconPack? = null
private var defaultIconSize: Int? = null
private var isIconPackChooserBuilt: Boolean = false
@ -50,7 +52,7 @@ object IconPackChooser {
* @param context Context to construct each pack with the resources
* @return An unique instance of [IconPackChooser], recall [.build] provide the same instance
*/
fun build(context: Context) {
override fun build(context: Context) {
synchronized(IconPackChooser::class.java) {
if (!isIconPackChooserBuilt) {
isIconPackChooserBuilt = true
@ -62,6 +64,9 @@ object IconPackChooser {
Log.e(TAG, "Icon packs can't be load, retry with one by default")
addDefaultIconPack(context)
}
if(defaultIconSize == null) {
setDefaultIconSize(context.resources.getDimension(R.dimen.icon_size).toInt())
}
}
}
}
@ -69,7 +74,7 @@ object IconPackChooser {
/**
* Construct dynamically the icon pack provide by the default string resource "resource_id"
*/
private fun addDefaultIconPack(context: Context) {
override fun addDefaultIconPack(context: Context) {
val resourceId = context.resources.getIdentifier("resource_id", "string", context.packageName)
iconPackList.add(IconPack(context.packageName, context.resources, resourceId))
}
@ -77,9 +82,11 @@ object IconPackChooser {
/**
* Utility method to add new icon pack or catch exception if not retrieve
*/
private fun addOrCatchNewIconPack(context: Context, iconPackString: String) {
override fun addOrCatchNewIconPack(context: Context, iconPackString: String) {
try {
iconPackList.add(IconPack(context.packageName, context.resources, context.resources.getIdentifier(
iconPackList.add(IconPack(context.packageName,
context.resources,
context.resources.getIdentifier(
iconPackString + "_resource_id",
"string",
context.packageName)))
@ -89,7 +96,7 @@ object IconPackChooser {
}
fun setSelectedIconPack(iconPackIdString: String?) {
override fun setSelectedIconPack(iconPackIdString: String?) {
for (iconPack in iconPackList) {
if (iconPack.id == iconPackIdString) {
iconPackSelected = iconPack
@ -104,10 +111,10 @@ object IconPackChooser {
* @param context Context to build the icon pack if not already build
* @return IconPack currently in usage
*/
fun getSelectedIconPack(context: Context): IconPack? {
override fun getSelectedIconPack(context: Context): IconPack? {
build(context)
if (iconPackSelected == null) {
setSelectedIconPack(PreferencesUtil.getIconPackSelectedId(context))
setSelectedIconPack(DatabasePreferencesUtil.getIconPackSelectedId(context))
}
return iconPackSelected
}
@ -118,8 +125,16 @@ object IconPackChooser {
* @param context Context to build the icon pack if not already build
* @return IconPack available
*/
fun getIconPackList(context: Context): List<IconPack> {
override fun getIconPackList(context: Context): List<IconPack> {
build(context)
return iconPackList
}
override fun setDefaultIconSize(size: Int) {
defaultIconSize = size
}
override fun getDefaultIconSize(): Int {
return defaultIconSize!!
}
}

View file

@ -26,7 +26,7 @@ import android.os.Parcelable
import android.text.format.Formatter
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
import java.text.DateFormat
import java.util.*
import java.util.Date
/**
* Utility data class to get FileDatabaseInfo at a `t` time

View file

@ -2,7 +2,10 @@ package com.kunzisoft.keepass.services
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.net.Uri
import android.os.Binder
import android.os.Build

View file

@ -39,12 +39,14 @@ import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Entry
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.toCompressionAlgorithm
import com.kunzisoft.keepass.database.element.node.Node
import com.kunzisoft.keepass.database.element.node.NodeId
import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.hardware.HardwareKeyActivity
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
@ -76,7 +78,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mDatabaseInfoListeners = mutableListOf<DatabaseInfoListener>()
private var mActionTaskBinder = ActionTaskBinder()
private var mActionTaskListeners = mutableListOf<ActionTaskListener>()
// Channel to connect asynchronously a response
// Channel to connect asynchronously a listener or a response
private var mRequestChallengeListenerChannel: Channel<RequestChallengeListener>? = null
private var mResponseChallengeChannel: Channel<ByteArray?>? = null
private var mActionRunning = 0
@ -93,7 +97,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
return getString(R.string.database)
}
inner class ActionTaskBinder: Binder() {
inner class ActionTaskBinder : Binder() {
fun getService(): DatabaseTaskNotificationService = this@DatabaseTaskNotificationService
@ -130,8 +134,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
interface DatabaseInfoListener {
fun onDatabaseInfoChanged(previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo)
fun onDatabaseInfoChanged(
previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
)
}
interface ActionTaskListener {
@ -145,6 +151,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
result: ActionRunnable.Result)
}
interface RequestChallengeListener {
fun onChallengeResponseRequested(hardwareKey: HardwareKey, seed: ByteArray?)
}
fun checkDatabase() {
mDatabaseListeners.forEach { databaseListener ->
databaseListener.onDatabaseRetrieved(mDatabase)
@ -181,7 +191,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Call listener to indicate a change in database info
if (!mSaveState && previousDatabaseInfo != null) {
mDatabaseInfoListeners.forEach { listener ->
listener.onDatabaseInfoChanged(previousDatabaseInfo, lastFileDatabaseInfo)
listener.onDatabaseInfoChanged(previousDatabaseInfo,
lastFileDatabaseInfo)
}
}
mSnapFileDatabaseInfo = lastFileDatabaseInfo
@ -268,7 +279,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val database = Database.getInstance()
val database = Database.getInstance(IconPackChooser)
if (mDatabase != database) {
mDatabase = database
mDatabaseListeners.forEach { listener ->
@ -302,18 +313,31 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent, database)
ACTION_DATABASE_MERGE_TASK -> buildDatabaseMergeActionTask(intent, database)
ACTION_DATABASE_RELOAD_TASK -> buildDatabaseReloadActionTask(database)
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent, database)
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent, database)
ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent, database)
ACTION_DATABASE_CREATE_ENTRY_TASK -> buildDatabaseCreateEntryActionTask(intent, database)
ACTION_DATABASE_UPDATE_ENTRY_TASK -> buildDatabaseUpdateEntryActionTask(intent, database)
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent,
database)
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent,
database)
ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent,
database)
ACTION_DATABASE_CREATE_ENTRY_TASK -> buildDatabaseCreateEntryActionTask(intent,
database)
ACTION_DATABASE_UPDATE_ENTRY_TASK -> buildDatabaseUpdateEntryActionTask(intent,
database)
ACTION_DATABASE_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent, database)
ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent, database)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent, database)
ACTION_DATABASE_RESTORE_ENTRY_HISTORY -> buildDatabaseRestoreEntryHistoryActionTask(intent, database)
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> buildDatabaseDeleteEntryHistoryActionTask(intent, database)
ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> buildDatabaseUpdateCompressionActionTask(intent, database)
ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK -> buildDatabaseRemoveUnlinkedDataActionTask(intent, database)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent,
database)
ACTION_DATABASE_RESTORE_ENTRY_HISTORY -> buildDatabaseRestoreEntryHistoryActionTask(
intent,
database)
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> buildDatabaseDeleteEntryHistoryActionTask(intent,
database)
ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> buildDatabaseUpdateCompressionActionTask(
intent,
database)
ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK -> buildDatabaseRemoveUnlinkedDataActionTask(
intent,
database)
ACTION_DATABASE_UPDATE_NAME_TASK,
ACTION_DATABASE_UPDATE_DESCRIPTION_TASK,
ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK,
@ -426,7 +450,6 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
mTaskRemovedRequested = false
}
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
}
mActionRunning--
@ -439,7 +462,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK,
null -> {
null,
-> {
START_STICKY
}
else -> {
@ -467,9 +491,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK -> R.string.loading_database
ACTION_DATABASE_RELOAD_TASK, -> R.string.loading_database
ACTION_DATABASE_ASSIGN_PASSWORD_TASK,
ACTION_DATABASE_SAVE -> R.string.saving_database
ACTION_DATABASE_SAVE, -> R.string.saving_database
else -> {
if (mSaveState)
R.string.saving_database
@ -580,10 +604,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
/**
* Execute action with a coroutine
*/
private suspend fun executeAction(progressTaskUpdater: ProgressTaskUpdater,
private suspend fun executeAction(
progressTaskUpdater: ProgressTaskUpdater,
onPreExecute: () -> Unit,
onExecute: (ProgressTaskUpdater?) -> ActionRunnable?,
onPostExecute: (result: ActionRunnable.Result) -> Unit) {
onPostExecute: (result: ActionRunnable.Result) -> Unit,
) {
onPreExecute.invoke()
withContext(Dispatchers.IO) {
onExecute.invoke(progressTaskUpdater)?.apply {
@ -609,11 +635,19 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
override fun updateMessage(resId: Int) {
fun updateMessage(resId: Int) {
mProgressMessage.messageId = resId
notifyProgressMessage()
}
override fun updateMessageRetrievingDBKey() {
updateMessage(R.string.retrieving_db_key)
}
override fun updateMessageDecryptingDB() {
updateMessage(R.string.decrypting_db)
}
override fun actionOnLock() {
if (!TimeoutHelper.temporarilyDisableLock) {
closeDatabase(mDatabase)
@ -631,8 +665,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
super.onTaskRemoved(rootIntent)
}
private fun retrieveResponseFromChallenge(hardwareKey: HardwareKey,
seed: ByteArray?): ByteArray {
private fun retrieveResponseFromChallenge(
hardwareKey: HardwareKey,
seed: ByteArray?,
): ByteArray {
// Request a challenge - response
var response: ByteArray
runBlocking {
@ -674,7 +710,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
&& intent.hasExtra(MAIN_CREDENTIAL_KEY)
) {
val databaseUri: Uri? = intent.getParcelableExtra(DATABASE_URI_KEY)
val mainCredential: MainCredential = intent.getParcelableExtra(MAIN_CREDENTIAL_KEY) ?: MainCredential()
val mainCredential: MainCredential =
intent.getParcelableExtra(MAIN_CREDENTIAL_KEY) ?: MainCredential()
if (databaseUri == null)
return null
@ -709,9 +746,11 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
&& intent.hasExtra(FIX_DUPLICATE_UUID_KEY)
) {
val databaseUri: Uri? = intent.getParcelableExtra(DATABASE_URI_KEY)
val mainCredential: MainCredential = intent.getParcelableExtra(MAIN_CREDENTIAL_KEY) ?: MainCredential()
val mainCredential: MainCredential =
intent.getParcelableExtra(MAIN_CREDENTIAL_KEY) ?: MainCredential()
val readOnly: Boolean = intent.getBooleanExtra(READ_ONLY_KEY, true)
val cipherEncryptDatabase: CipherEncryptDatabase? = intent.getParcelableExtra(CIPHER_DATABASE_KEY)
val cipherEncryptDatabase: CipherEncryptDatabase? =
intent.getParcelableExtra(CIPHER_DATABASE_KEY)
if (databaseUri == null)
return null
@ -782,7 +821,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseAssignPasswordActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseAssignPasswordActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MAIN_CREDENTIAL_KEY)
) {
@ -800,8 +842,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
private inner class AfterActionNodesRunnable : AfterActionNodesFinish() {
override fun onActionNodesFinish(result: ActionRunnable.Result,
actionNodesValues: ActionNodesValues) {
override fun onActionNodesFinish(
result: ActionRunnable.Result,
actionNodesValues: ActionNodesValues,
) {
val bundle = result.data ?: Bundle()
bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodesValues.oldNodes))
bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodesValues.newNodes))
@ -809,7 +853,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseCreateGroupActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseCreateGroupActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -818,7 +865,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY)
if (parentId == null
|| newGroup == null)
|| newGroup == null
)
return null
database.getGroupById(parentId)?.let { parent ->
@ -837,7 +885,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseUpdateGroupActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseUpdateGroupActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(GROUP_ID_KEY)
&& intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -846,7 +897,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY)
if (groupId == null
|| newGroup == null)
|| newGroup == null
)
return null
database.getGroupById(groupId)?.let { oldGroup ->
@ -865,7 +917,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseCreateEntryActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseCreateEntryActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -874,7 +929,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY)
if (parentId == null
|| newEntry == null)
|| newEntry == null
)
return null
database.getGroupById(parentId)?.let { parent ->
@ -893,7 +949,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseUpdateEntryActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseUpdateEntryActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -902,7 +961,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY)
if (entryId == null
|| newEntry == null)
|| newEntry == null
)
return null
database.getEntryById(entryId)?.let { oldEntry ->
@ -921,7 +981,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseCopyNodesActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseCopyNodesActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
@ -945,7 +1008,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseMoveNodesActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseMoveNodesActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY)
@ -969,7 +1035,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseDeleteNodesActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseDeleteNodesActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -977,6 +1046,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
DeleteNodesRunnable(this,
database,
getListNodesFromBundle(database, intent.extras!!),
resources.getString(R.string.recycle_bin),
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodesRunnable()
) { hardwareKey, seed ->
@ -987,7 +1057,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseRestoreEntryHistoryActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseRestoreEntryHistoryActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_HISTORY_POSITION_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -1009,7 +1082,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseDeleteEntryHistoryActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseDeleteEntryHistoryActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_HISTORY_POSITION_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)
@ -1031,22 +1107,27 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseUpdateCompressionActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseUpdateCompressionActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(OLD_ELEMENT_KEY)
&& intent.hasExtra(NEW_ELEMENT_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY)) {
&& intent.hasExtra(SAVE_DATABASE_KEY)
) {
val oldElement: CompressionAlgorithm? = intent.getParcelableExtra(OLD_ELEMENT_KEY)
val newElement: CompressionAlgorithm? = intent.getParcelableExtra(NEW_ELEMENT_KEY)
val oldElement: NamedCompressionAlgorithm? = intent.getParcelableExtra(OLD_ELEMENT_KEY)
val newElement: NamedCompressionAlgorithm? = intent.getParcelableExtra(NEW_ELEMENT_KEY)
if (oldElement == null
|| newElement == null)
|| newElement == null
)
return null
return UpdateCompressionBinariesDatabaseRunnable(this,
database,
oldElement,
newElement,
oldElement.toCompressionAlgorithm(),
newElement.toCompressionAlgorithm(),
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false)
) { hardwareKey, seed ->
retrieveResponseFromChallenge(hardwareKey, seed)
@ -1060,7 +1141,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseRemoveUnlinkedDataActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseRemoveUnlinkedDataActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return RemoveUnlinkedDataDatabaseRunnable(this,
@ -1078,7 +1162,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
}
private fun buildDatabaseUpdateElementActionTask(intent: Intent, database: Database): ActionRunnable? {
private fun buildDatabaseUpdateElementActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return SaveDatabaseRunnable(this,
database,
@ -1168,11 +1255,11 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK = "ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK"
const val ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK = "ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK"
const val ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK"
const val ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK"
const val ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK ="ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK"
const val ACTION_DATABASE_UPDATE_ENCRYPTION_TASK = "ACTION_DATABASE_UPDATE_ENCRYPTION_TASK"
const val ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK = "ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK"
const val ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK = "ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK"
const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK = "ACTION_DATABASE_UPDATE_PARALLELISM_TASK"
const val ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK ="ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK"
const val ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK ="ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK"
const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK ="ACTION_DATABASE_UPDATE_PARALLELISM_TASK"
const val ACTION_DATABASE_UPDATE_ITERATIONS_TASK = "ACTION_DATABASE_UPDATE_ITERATIONS_TASK"
const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE"
const val ACTION_CHALLENGE_RESPONDED = "ACTION_CHALLENGE_RESPONDED"

View file

@ -37,7 +37,9 @@ import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.toCompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.toNamedCompressionAlgorithm
import com.kunzisoft.keepass.database.element.template.TemplateEngine
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.preference.*
@ -198,8 +200,8 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
// Database compression
dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
if (database.allowDataCompression) {
dbDataCompressionPref?.summary = (database.compressionAlgorithm
?: CompressionAlgorithm.None).getName(resources)
dbDataCompressionPref?.summary = (database.compressionAlgorithm?.toNamedCompressionAlgorithm()
?: NamedCompressionAlgorithm.None).getName(resources)
} else {
dbCompressionPrefCategory?.isVisible = false
}
@ -215,7 +217,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
isEnabled = if (!mDatabaseReadOnly) {
setOnPreferenceChangeListener { _, newValue ->
val recycleBinEnabled = newValue as Boolean
database.enableRecycleBin(recycleBinEnabled, resources)
database.enableRecycleBin(recycleBinEnabled, resources.getString(R.string.recycle_bin))
refreshRecycleBinGroup(database)
// Save the database if not in readonly mode
saveDatabase(mDatabaseAutoSaveEnabled)
@ -249,7 +251,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
setOnPreferenceChangeListener { _, newValue ->
val templatesEnabled = newValue as Boolean
database.enableTemplates(templatesEnabled,
TemplateEngine.getDefaultTemplateGroupName(resources)
resources.getString(R.string.templates)
)
refreshTemplatesGroup(database)
// Save the database if not in readonly mode
@ -433,13 +435,13 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetriev
dbCustomColorPref?.summary = defaultColorToShow
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> {
val oldCompression = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as CompressionAlgorithm
val newCompression = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as CompressionAlgorithm
val oldCompression = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as NamedCompressionAlgorithm
val newCompression = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as NamedCompressionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newCompression
} else {
mDatabase?.compressionAlgorithm = oldCompression
mDatabase?.compressionAlgorithm = oldCompression.toCompressionAlgorithm()
oldCompression
}
dbDataCompressionPref?.summary = algorithmToShow.getName(resources)

View file

@ -35,9 +35,15 @@ import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
import com.kunzisoft.keepass.password.PassphraseGenerator
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.APP_TIMEOUT_KEY
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.HIDE_EXPIRED_ENTRIES_KEY
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.SETTING_ICON_PACK_CHOOSE_KEY
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.SUBDOMAIN_SEARCH_KEY
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.TIMEOUT_BACKUP_KEY
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.TIMEOUT_DEFAULT
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil
import java.util.*
import java.util.Properties
object PreferencesUtil {
@ -61,7 +67,8 @@ object PreferencesUtil {
fun saveNodeSort(context: Context,
sortNodeEnum: SortNodeEnum,
sortNodeParameters: SortNodeEnum.SortNodeParameters) {
sortNodeParameters: SortNodeEnum.SortNodeParameters
) {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
prefs?.edit()?.apply {
putString(context.getString(R.string.sort_node_key), sortNodeEnum.name)
@ -108,12 +115,6 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.auto_focus_search_default))
}
fun searchSubdomains(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.subdomain_search_key),
context.resources.getBoolean(R.bool.subdomain_search_default))
}
fun showEntryColors(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.show_entry_colors_key),
@ -156,12 +157,6 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.show_uuid_default))
}
fun showExpiredEntries(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return ! prefs.getBoolean(context.getString(R.string.hide_expired_entries_key),
context.resources.getBoolean(R.bool.hide_expired_entries_default))
}
fun getStyle(context: Context): String {
val defaultStyleString = Stylish.defaultStyle(context)
val styleString = PreferenceManager.getDefaultSharedPreferences(context)
@ -199,8 +194,7 @@ object PreferencesUtil {
fun getListTextSize(context: Context): Float {
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))
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
@ -213,8 +207,7 @@ object PreferencesUtil {
fun getDefaultPasswordLength(context: Context): Int {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getInt(context.getString(R.string.password_generator_length_key),
context.resources.getInteger(R.integer.password_generator_length_default))
return prefs.getInt(context.getString(R.string.password_generator_length_key), context.resources.getInteger(R.integer.password_generator_length_default))
}
fun setDefaultPasswordLength(context: Context, passwordLength: Int) {
@ -246,8 +239,7 @@ object PreferencesUtil {
fun getDefaultPasswordConsiderChars(context: Context): String {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.password_generator_consider_chars_key),
context.getString(R.string.password_generator_consider_chars_default)) ?: ""
return prefs.getString(context.getString(R.string.password_generator_consider_chars_key), context.getString(R.string.password_generator_consider_chars_default)) ?: ""
}
fun setDefaultPasswordConsiderChars(context: Context, considerChars: String) {
@ -262,8 +254,7 @@ object PreferencesUtil {
fun getDefaultPasswordIgnoreChars(context: Context): String {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.password_generator_ignore_chars_key),
context.getString(R.string.password_generator_ignore_chars_default)) ?: ""
return prefs.getString(context.getString(R.string.password_generator_ignore_chars_key), context.getString(R.string.password_generator_ignore_chars_default)) ?: ""
}
fun setDefaultPasswordIgnoreChars(context: Context, ignoreChars: String) {
@ -278,8 +269,7 @@ object PreferencesUtil {
fun getDefaultPassphraseWordCount(context: Context): Int {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getInt(context.getString(R.string.passphrase_generator_word_count_key),
context.resources.getInteger(R.integer.passphrase_generator_word_count_default))
return prefs.getInt(context.getString(R.string.passphrase_generator_word_count_key), context.resources.getInteger(R.integer.passphrase_generator_word_count_default))
}
fun setDefaultPassphraseWordCount(context: Context, passphraseWordCount: Int) {
@ -294,8 +284,8 @@ object PreferencesUtil {
fun getDefaultPassphraseWordCase(context: Context): PassphraseGenerator.WordCase {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return PassphraseGenerator.WordCase
.getByOrdinal(prefs.getInt(context
return PassphraseGenerator.WordCase.getByOrdinal(
prefs.getInt(context
.getString(R.string.passphrase_generator_word_case_key),
0)
)
@ -414,75 +404,44 @@ object PreferencesUtil {
*/
fun saveCurrentTime(context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
putLong(context.getString(R.string.timeout_backup_key), System.currentTimeMillis())
putLong(TIMEOUT_BACKUP_KEY, System.currentTimeMillis())
apply()
}
}
/**
* Time previously saved in milliseconds (commonly used to compare with current time and check timeout)
*/
fun getTimeSaved(context: Context): Long {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getLong(context.getString(R.string.timeout_backup_key),
TimeoutHelper.NEVER)
}
/**
* App timeout selected in milliseconds
*/
fun getAppTimeout(context: Context): Long {
return try {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
(prefs.getString(context.getString(R.string.app_timeout_key),
context.getString(R.string.timeout_default)) ?: "300000").toLong()
} catch (e: NumberFormatException) {
TimeoutHelper.DEFAULT_TIMEOUT
}
}
fun getClipboardTimeout(context: Context): Long {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.clipboard_timeout_key),
context.getString(R.string.clipboard_timeout_default))?.toLong()
?: TimeoutHelper.DEFAULT_TIMEOUT
return prefs.getString(context.getString(R.string.clipboard_timeout_key), TIMEOUT_DEFAULT)?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT
}
fun getAdvancedUnlockTimeout(context: Context): Long {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.temp_advanced_unlock_timeout_key),
context.getString(R.string.temp_advanced_unlock_timeout_default))?.toLong()
?: TimeoutHelper.DEFAULT_TIMEOUT
return prefs.getString(context.getString(R.string.temp_advanced_unlock_timeout_key), context.getString(R.string.temp_advanced_unlock_timeout_default))?.toLong() ?: TimeoutHelper.DEFAULT_TIMEOUT
}
fun isLockDatabaseWhenScreenShutOffEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.lock_database_screen_off_key),
context.resources.getBoolean(R.bool.lock_database_screen_off_default))
return prefs.getBoolean(context.getString(R.string.lock_database_screen_off_key), context.resources.getBoolean(R.bool.lock_database_screen_off_default))
}
fun isLockDatabaseWhenBackButtonOnRootClicked(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.lock_database_back_root_key),
context.resources.getBoolean(R.bool.lock_database_back_root_default))
return prefs.getBoolean(context.getString(R.string.lock_database_back_root_key), context.resources.getBoolean(R.bool.lock_database_back_root_default))
}
fun showLockDatabaseButton(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.lock_database_show_button_key),
context.resources.getBoolean(R.bool.lock_database_show_button_default))
return prefs.getBoolean(context.getString(R.string.lock_database_show_button_key), context.resources.getBoolean(R.bool.lock_database_show_button_default))
}
fun isAutoSaveDatabaseEnabled(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.enable_auto_save_database_key),
context.resources.getBoolean(R.bool.enable_auto_save_database_default))
return prefs.getBoolean(context.getString(R.string.enable_auto_save_database_key), context.resources.getBoolean(R.bool.enable_auto_save_database_default))
}
fun isKeepScreenOnEnabled(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.enable_keep_screen_on_key),
context.resources.getBoolean(R.bool.enable_keep_screen_on_default))
return prefs.getBoolean(context.getString(R.string.enable_keep_screen_on_key), context.resources.getBoolean(R.bool.enable_keep_screen_on_default))
}
fun isScreenshotModeEnabled(context: Context): Boolean {
@ -593,13 +552,6 @@ object PreferencesUtil {
.apply()
}
fun getIconPackSelectedId(context: Context): String? {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(
context.getString(R.string.setting_icon_pack_choose_key),
context.getString(R.string.setting_icon_pack_choose_default))
}
fun emptyPasswordAllowed(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.allow_no_password_key),
@ -754,7 +706,8 @@ object PreferencesUtil {
fun getAppProperties(context: Context): Properties {
val properties = Properties()
for ((name, value) in PreferenceManager.getDefaultSharedPreferences(context).all) {
for ((name, value) in PreferenceManager.getDefaultSharedPreferences(
context).all) {
properties[name] = value.toString()
}
for ((name, value) in Education.getEducationSharedPreferences(context).all) {
@ -779,7 +732,7 @@ object PreferencesUtil {
for ((name, value) in properties) {
try {
putProperty(this, name as String, value as String)
} catch (e:Exception) {
} catch (e: Exception) {
Log.e("PreferencesUtil", "Error when trying to parse app property $name=$value", e)
}
}
@ -796,8 +749,8 @@ object PreferencesUtil {
context.getString(R.string.enable_auto_save_database_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.enable_keep_screen_on_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())
SUBDOMAIN_SEARCH_KEY -> editor.putBoolean(name, value.toBoolean())
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())
@ -839,7 +792,7 @@ object PreferencesUtil {
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)
SETTING_ICON_PACK_CHOOSE_KEY -> editor.putString(name, value)
context.getString(R.string.show_entry_colors_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.hide_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.colorize_password_key) -> editor.putBoolean(name, value.toBoolean())
@ -849,7 +802,7 @@ object PreferencesUtil {
context.getString(R.string.show_uuid_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())
HIDE_EXPIRED_ENTRIES_KEY -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.password_generator_length_key) -> editor.putInt(name, value.toInt())
@ -869,8 +822,13 @@ object PreferencesUtil {
}
putPropertiesInPreferences(properties,
Education.getEducationSharedPreferences(context)) { editor, name, value ->
Education.putPropertiesInEducationPreferences(context, editor, name, value)
Education.getEducationSharedPreferences(
context)) { editor, name, value ->
Education.putPropertiesInEducationPreferences(
context,
editor,
name,
value)
}
}
}

View file

@ -25,16 +25,18 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.toCompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.toNamedCompressionAlgorithm
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
class DatabaseDataCompressionPreferenceDialogFragmentCompat
: DatabaseSavePreferenceDialogFragmentCompat(),
ListRadioItemAdapter.RadioItemSelectedCallback<CompressionAlgorithm> {
ListRadioItemAdapter.RadioItemSelectedCallback<NamedCompressionAlgorithm> {
private var mRecyclerView: RecyclerView? = null
private var mCompressionAdapter: ListRadioItemAdapter<CompressionAlgorithm>? = null
private var compressionSelected: CompressionAlgorithm? = null
private var mCompressionAdapter: ListRadioItemAdapter<NamedCompressionAlgorithm>? = null
private var compressionSelected: NamedCompressionAlgorithm? = null
override fun onBindDialogView(view: View) {
super.onBindDialogView(view)
@ -45,7 +47,7 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
mRecyclerView?.layoutManager = LinearLayoutManager(context)
activity?.let { activity ->
mCompressionAdapter = ListRadioItemAdapter<CompressionAlgorithm>(activity)
mCompressionAdapter = ListRadioItemAdapter<NamedCompressionAlgorithm>(activity)
mCompressionAdapter?.setRadioItemSelectedCallback(this)
}
}
@ -57,8 +59,8 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
mRecyclerView?.adapter = mCompressionAdapter
database?.let {
compressionSelected = it.compressionAlgorithm
mCompressionAdapter?.setItems(it.availableCompressionAlgorithms, compressionSelected)
compressionSelected = it.compressionAlgorithm?.toNamedCompressionAlgorithm()
mCompressionAdapter?.setItems(it.availableCompressionAlgorithms.map { it.toNamedCompressionAlgorithm() }, compressionSelected)
}
}
@ -69,16 +71,16 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
if (compressionSelected != null) {
val newCompression = compressionSelected
val oldCompression = database.compressionAlgorithm
database.compressionAlgorithm = newCompression
database.compressionAlgorithm = newCompression?.toCompressionAlgorithm()
if (oldCompression != null && newCompression != null)
saveCompression(oldCompression, newCompression)
saveCompression(oldCompression.toNamedCompressionAlgorithm(), newCompression)
}
}
}
}
override fun onItemSelected(item: CompressionAlgorithm) {
override fun onItemSelected(item: NamedCompressionAlgorithm) {
this.compressionSelected = item
}

View file

@ -28,7 +28,7 @@ import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
@ -90,8 +90,9 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat
mDatabaseViewModel.saveColor(oldColorString, newColorString, mDatabaseAutoSaveEnable)
}
protected fun saveCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm) {
protected fun saveCompression(oldCompression: NamedCompressionAlgorithm,
newCompression: NamedCompressionAlgorithm
) {
mDatabaseViewModel.saveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable)
}

View file

@ -40,7 +40,6 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
const val DATABASE_START_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_START_TASK_ACTION"
const val DATABASE_STOP_TASK_ACTION = "com.kunzisoft.keepass.DATABASE_STOP_TASK_ACTION"
const val LOCK_ACTION = "com.kunzisoft.keepass.LOCK"
const val REMOVE_ENTRY_MAGIKEYBOARD_ACTION = "com.kunzisoft.keepass.REMOVE_ENTRY_MAGIKEYBOARD"
const val BACK_PREVIOUS_KEYBOARD_ACTION = "com.kunzisoft.keepass.BACK_PREVIOUS_KEYBOARD"

View file

@ -18,12 +18,9 @@
*
*/
package com.kunzisoft.keepass.utils
open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) {
@Volatile
private var instance: T? = null
fun getInstance(arg: A): T {
return when {
instance != null -> instance!!
@ -35,18 +32,3 @@ open class SingletonHolderParameter<out T, in A>(private val constructor: (A) ->
}
}
open class SingletonHolder<out T>(private val constructor: () -> T) {
@Volatile
private var instance: T? = null
fun getInstance(): T {
return when {
instance != null -> instance!!
else -> synchronized(this) {
if (instance == null) instance = constructor()
instance!!
}
}
}
}

View file

@ -16,6 +16,7 @@ import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.template.TemplateAttribute
import com.kunzisoft.keepass.database.element.template.TemplateAttributeAction
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.getLocalizedName
import com.kunzisoft.keepass.otp.OtpEntryFields
import org.joda.time.DateTime

View file

@ -9,6 +9,7 @@ import com.kunzisoft.keepass.database.element.Field
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.database.element.template.TemplateAttribute
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.getLocalizedName
import com.kunzisoft.keepass.model.OtpModel
import com.kunzisoft.keepass.otp.OtpElement
import com.kunzisoft.keepass.otp.OtpEntryFields.OTP_TOKEN_FIELD

View file

@ -21,6 +21,7 @@ import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.isStandardPasswordName
import com.kunzisoft.keepass.password.PasswordGenerator
import com.kunzisoft.keepass.settings.PreferencesUtil

View file

@ -39,6 +39,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.element.template.TemplateField
import com.kunzisoft.keepass.database.element.template.isStandardPasswordName
import com.kunzisoft.keepass.model.EntryInfo.Companion.APPLICATION_ID_FIELD_NAME
import com.kunzisoft.keepass.password.PasswordGenerator
import com.kunzisoft.keepass.settings.PreferencesUtil

View file

@ -24,6 +24,7 @@ import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.text.Selection
import android.text.Spannable
import android.text.SpannableString
@ -32,28 +33,25 @@ import android.text.method.LinkMovementMethod
import android.text.method.PasswordTransformationMethod
import android.text.style.ClickableSpan
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.appcompat.widget.ActionMenuView
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.snackbar.Snackbar
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.exception.getLocalizedMessage
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import androidx.appcompat.view.menu.ActionMenuItemView
import android.widget.ImageView
import androidx.appcompat.widget.ActionMenuView
import androidx.core.graphics.drawable.DrawableCompat
import android.graphics.drawable.Drawable
import android.view.ViewGroup
import android.widget.LinearLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
/**

View file

@ -7,7 +7,7 @@ import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.element.database.NamedCompressionAlgorithm
import com.kunzisoft.keepass.tasks.ActionRunnable
class DatabaseViewModel: ViewModel() {
@ -119,8 +119,8 @@ class DatabaseViewModel: ViewModel() {
_saveColor.value = SuperString(oldValue, newValue, save)
}
fun saveCompression(oldValue: CompressionAlgorithm,
newValue: CompressionAlgorithm,
fun saveCompression(oldValue: NamedCompressionAlgorithm,
newValue: NamedCompressionAlgorithm,
save: Boolean) {
_saveCompression.value = SuperCompression(oldValue, newValue, save)
}
@ -198,8 +198,8 @@ class DatabaseViewModel: ViewModel() {
val save: Boolean)
data class SuperMerge(val fixDuplicateUuid: Boolean,
val save: Boolean)
data class SuperCompression(val oldValue: CompressionAlgorithm,
val newValue: CompressionAlgorithm,
data class SuperCompression(val oldValue: NamedCompressionAlgorithm,
val newValue: NamedCompressionAlgorithm,
val save: Boolean)
data class SuperEncryption(val oldValue: EncryptionAlgorithm,
val newValue: EncryptionAlgorithm,

View file

@ -26,7 +26,7 @@ import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.utils.UriUtil
import java.io.Serializable
import java.text.DateFormat
import java.util.*
import java.util.Date
class FileDatabaseInfo : Serializable {

1
database/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

62
database/build.gradle Normal file
View file

@ -0,0 +1,62 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 32
buildToolsVersion "32.0.0"
ndkVersion "21.4.7075529"
defaultConfig {
minSdkVersion 15
targetSdk 32
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
def room_version = "2.4.3"
dependencies {
implementation "androidx.core:core-ktx:$android_core_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
// Color
implementation 'com.github.Kunzisoft:AndroidClearChroma:2.6'
// Time
implementation 'joda-time:joda-time:2.10.13'
// Apache Commons
implementation 'commons-io:commons-io:2.8.0'
implementation 'commons-codec:commons-codec:1.15'
// Database
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation project(path: ':crypto')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
}

View file

21
database/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.kunzisoft.keepass.database" />

View file

@ -26,7 +26,8 @@ import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseException
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
class MergeDatabaseRunnable(
@ -64,7 +65,7 @@ class MergeDatabaseRunnable(
if (result.isSuccess) {
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
DatabasePreferencesUtil.saveCurrentTime(context)
}
super.onActionRun()
}

View file

@ -23,20 +23,22 @@ import android.content.Context
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseException
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.utils.UriUtilDatabase
class ReloadDatabaseRunnable(private val context: Context,
class ReloadDatabaseRunnable(
private val context: Context,
private val mDatabase: Database,
private val progressTaskUpdater: ProgressTaskUpdater?,
private val mLoadDatabaseResult: ((Result) -> Unit)?)
: ActionRunnable() {
private val mLoadDatabaseResult: ((Result) -> Unit)?,
) : ActionRunnable() {
override fun onStartRun() {
// Clear before we load
mDatabase.clearIndexesAndBinaries(UriUtil.getBinaryDir(context))
mDatabase.clearIndexesAndBinaries(UriUtilDatabase.getBinaryDir(
context))
mDatabase.wasReloaded = true
}
@ -53,7 +55,7 @@ class ReloadDatabaseRunnable(private val context: Context,
if (result.isSuccess) {
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
DatabasePreferencesUtil.saveCurrentTime(context)
} else {
mDatabase.clearAndClose(context)
}

View file

@ -30,6 +30,7 @@ import com.kunzisoft.keepass.hardware.HardwareKey
class DeleteNodesRunnable(context: Context,
database: Database,
private val mNodesToDelete: List<Node>,
private val recyclerBinTitle: String,
save: Boolean,
afterActionNodesFinish: AfterActionNodesFinish,
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray)
@ -54,7 +55,7 @@ class DeleteNodesRunnable(context: Context,
// Remove Node from parent
mCanRecycle = database.canRecycle(groupToDelete)
if (mCanRecycle) {
database.recycle(groupToDelete, context.resources)
database.recycle(groupToDelete, recyclerBinTitle)
groupToDelete.setPreviousParentGroup(mOldParent)
groupToDelete.touch(modified = true, touchParents = true)
} else {
@ -68,7 +69,7 @@ class DeleteNodesRunnable(context: Context,
// Remove Node from parent
mCanRecycle = database.canRecycle(entryToDelete)
if (mCanRecycle) {
database.recycle(entryToDelete, context.resources)
database.recycle(entryToDelete, recyclerBinTitle)
entryToDelete.setPreviousParentGroup(mOldParent)
entryToDelete.touch(modified = true, touchParents = true)
} else {

View file

@ -56,13 +56,14 @@ import com.kunzisoft.keepass.database.search.SearchHelper
import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.icons.InterfaceIconPackChooser
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.*
import java.io.*
import java.util.*
class Database {
class Database(private val iconPackChooser: InterfaceIconPackChooser) {
// To keep a reference for specific methods provided by version
private var mDatabaseKDB: DatabaseKDB? = null
@ -76,8 +77,9 @@ class Database {
var isReadOnly = false
val iconDrawableFactory = IconDrawableFactory(
{ binaryCache },
{ iconId -> iconsManager.getBinaryForCustomIcon(iconId) }
iconPackChooser = iconPackChooser,
retrieveBinaryCache = { binaryCache },
retrieveCustomIconBinary = { iconId -> iconsManager.getBinaryForCustomIcon(iconId) }
)
var loaded = false
@ -306,7 +308,8 @@ class Database {
}
fun updateDataBinaryCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm) {
newCompression: CompressionAlgorithm
) {
mDatabaseKDBX?.changeBinaryCompression(oldCompression, newCompression)
dataModifiedSinceLastLoading = true
}
@ -463,10 +466,10 @@ class Database {
// Backup is always enabled in KDB database
get() = mDatabaseKDB != null || mDatabaseKDBX?.isRecycleBinEnabled ?: false
fun enableRecycleBin(enable: Boolean, resources: Resources) {
fun enableRecycleBin(enable: Boolean, recyclerBinTitle: String) {
mDatabaseKDBX?.isRecycleBinEnabled = enable
if (enable) {
ensureRecycleBinExists(resources)
ensureRecycleBinExists(recyclerBinTitle)
} else {
mDatabaseKDBX?.removeRecycleBin()
}
@ -645,7 +648,7 @@ class Database {
}
// New database instance to get new changes
val databaseToMerge = Database()
val databaseToMerge = Database(iconPackChooser)
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
try {
@ -784,7 +787,7 @@ class Database {
openDatabaseKDBX: (InputStream) -> Unit) {
try {
// Load Data, pass Uris as InputStreams
val databaseStream = UriUtil.getUriInputStream(contentResolver, databaseUri)
val databaseStream = UriUtilDatabase.getUriInputStream(contentResolver, databaseUri)
?: throw UnknownDatabaseLocationException()
BufferedInputStream(databaseStream).use { databaseInputStream ->
@ -865,7 +868,7 @@ class Database {
}
}
// Copy from the cache to the final stream
UriUtil.getUriOutputStream(contentResolver, saveUri)?.use { outputStream ->
UriUtilDatabase.getUriOutputStream(contentResolver, saveUri)?.use { outputStream ->
cacheFile.inputStream().use { inputStream ->
inputStream.readAllBytes { buffer ->
outputStream.write(buffer)
@ -1004,7 +1007,7 @@ class Database {
}
fun clearAndClose(context: Context? = null) {
clearIndexesAndBinaries(context?.let { UriUtil.getBinaryDir(context) })
clearIndexesAndBinaries(context?.let { UriUtilDatabase.getBinaryDir(context) })
this.mDatabaseKDB = null
this.mDatabaseKDBX = null
this.fileUri = null
@ -1217,9 +1220,9 @@ class Database {
})
}
fun ensureRecycleBinExists(resources: Resources) {
fun ensureRecycleBinExists(recyclerBinTitle: String) {
mDatabaseKDB?.ensureBackupExists()
mDatabaseKDBX?.ensureRecycleBinExists(resources)
mDatabaseKDBX?.ensureRecycleBinExists(recyclerBinTitle)
}
fun canRecycle(entry: Entry): Boolean {
@ -1244,8 +1247,8 @@ class Database {
return canRecycle ?: false
}
fun recycle(entry: Entry, resources: Resources) {
ensureRecycleBinExists(resources)
fun recycle(entry: Entry, recyclerBinTitle: String) {
ensureRecycleBinExists(recyclerBinTitle)
entry.parent?.let { parent ->
removeEntryFrom(entry, parent)
}
@ -1255,8 +1258,8 @@ class Database {
entry.afterAssignNewParent()
}
fun recycle(group: Group, resources: Resources) {
ensureRecycleBinExists(resources)
fun recycle(group: Group, recyclerBinTitle: String) {
ensureRecycleBinExists(recyclerBinTitle)
group.parent?.let { parent ->
removeGroupFrom(group, parent)
}

View file

@ -30,7 +30,7 @@ import com.kunzisoft.keepass.database.element.icon.IconImage
import com.kunzisoft.keepass.database.element.node.*
import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.GroupInfo
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import java.util.*
import kotlin.collections.ArrayList
@ -87,7 +87,7 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
companion object {
fun getDefaults(context: Context): Array<ChildFilter> {
return if (PreferencesUtil.showExpiredEntries(context)) {
return if (DatabasePreferencesUtil.showExpiredEntries(context)) {
arrayOf(META_STREAM)
} else {
arrayOf(META_STREAM, EXPIRED)

View file

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

Some files were not shown because too many files have changed in this diff Show more