extract database module

This commit is contained in:
GianpaMX
2022-10-25 18:00:31 +01:00
parent 04eae1ae3d
commit 7d7e3f4ad6
188 changed files with 1154 additions and 1000 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 {

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

@@ -1,51 +1,33 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.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
import android.util.Log
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)
AppDatabase.getDatabase(applicationContext)
.cipherDatabaseDao()
// Temp DAO to easily remove content if object no longer in memory
private var useTempDao = PreferencesUtil.isTempAdvancedUnlockEnable(applicationContext)
private var mBinder: AdvancedUnlockNotificationService.AdvancedUnlockBinder? = null
private var mBinder: com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.AdvancedUnlockBinder? = null
private var mServiceConnection: ServiceConnection? = null
private var mDatabaseListeners = LinkedList<CipherDatabaseListener>()
private var mAdvancedUnlockBroadcastReceiver = AdvancedUnlockNotificationService.AdvancedUnlockReceiver {
private var mAdvancedUnlockBroadcastReceiver = com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.AdvancedUnlockReceiver {
deleteAll()
removeAllDataAndDetach()
}
@@ -67,12 +49,12 @@ class CipherDatabaseAction(context: Context) {
@Synchronized
private fun attachService(performedAction: () -> Unit) {
applicationContext.registerReceiver(mAdvancedUnlockBroadcastReceiver, IntentFilter().apply {
addAction(AdvancedUnlockNotificationService.REMOVE_ADVANCED_UNLOCK_KEY_ACTION)
addAction(com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.REMOVE_ADVANCED_UNLOCK_KEY_ACTION)
})
mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
mBinder = (serviceBinder as AdvancedUnlockNotificationService.AdvancedUnlockBinder)
mBinder = (serviceBinder as com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.AdvancedUnlockBinder)
performedAction.invoke()
}
@@ -81,7 +63,7 @@ class CipherDatabaseAction(context: Context) {
}
}
try {
AdvancedUnlockNotificationService.bindService(applicationContext,
com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.bindService(applicationContext,
mServiceConnection!!,
Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
@@ -97,7 +79,7 @@ class CipherDatabaseAction(context: Context) {
} catch (e: Exception) {}
mServiceConnection?.let {
AdvancedUnlockNotificationService.unbindService(applicationContext, it)
com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.unbindService(applicationContext, it)
}
}
@@ -149,7 +131,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

@@ -1,22 +1,3 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.app.database
import android.content.Context
@@ -25,29 +6,31 @@ import android.util.Log
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)
AppDatabase.getDatabase(applicationContext)
.fileDatabaseHistoryDao()
fun getDatabaseFile(databaseUri: Uri,
databaseFileResult: (DatabaseFile?) -> Unit) {
databaseFileResult: (com.kunzisoft.keepass.model.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,29 +51,35 @@ 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)
}
).execute()
}
fun getDatabaseFileList(databaseFileListResult: (List<DatabaseFile>) -> Unit) {
fun getDatabaseFileList(databaseFileListResult: (List<com.kunzisoft.keepass.model.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 +90,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
}
databaseFileListLoaded
},
{
databaseFileList ->
{ databaseFileList ->
databaseFileList?.let {
databaseFileListResult.invoke(it)
}
@@ -113,24 +101,26 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
fun addOrUpdateDatabaseUri(databaseUri: Uri,
keyFileUri: Uri? = null,
hardwareKey: HardwareKey? = null,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) {
addOrUpdateDatabaseFile(DatabaseFile(
databaseFileAddedOrUpdatedResult: ((com.kunzisoft.keepass.model.DatabaseFile?) -> Unit)? = null) {
addOrUpdateDatabaseFile(com.kunzisoft.keepass.model.DatabaseFile(
databaseUri,
keyFileUri,
hardwareKey
), databaseFileAddedOrUpdatedResult)
}
fun addOrUpdateDatabaseFile(databaseFileToAddOrUpdate: DatabaseFile,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) {
fun addOrUpdateDatabaseFile(databaseFileToAddOrUpdate: com.kunzisoft.keepass.model.DatabaseFile,
databaseFileAddedOrUpdatedResult: ((com.kunzisoft.keepass.model.DatabaseFile?) -> Unit)? = null) {
IOActionTask(
{
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 +141,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(),
@@ -171,19 +162,20 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
).execute()
}
fun deleteDatabaseFile(databaseFileToDelete: DatabaseFile,
databaseFileDeletedResult: (DatabaseFile?) -> Unit) {
fun deleteDatabaseFile(databaseFileToDelete: com.kunzisoft.keepass.model.DatabaseFile,
databaseFileDeletedResult: (com.kunzisoft.keepass.model.DatabaseFile?) -> Unit) {
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 {
@@ -232,7 +224,7 @@ class FileDatabaseHistoryAction(private val applicationContext: Context) {
).execute()
}
companion object : SingletonHolderParameter<FileDatabaseHistoryAction, Context>(::FileDatabaseHistoryAction) {
companion object : com.kunzisoft.keepass.utils.SingletonHolderParameter<FileDatabaseHistoryAction, Context>(::FileDatabaseHistoryAction) {
private val TAG = FileDatabaseHistoryAction::class.java.name
}
}

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

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
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.hardware.HardwareKey
@@ -54,7 +53,7 @@ open class AssignMainCredentialInDatabaseRunnable (
super.onFinishRun()
// Erase the biometric
CipherDatabaseAction.getInstance(context)
com.kunzisoft.keepass.app.database.CipherDatabaseAction.getInstance(context)
.deleteByDatabaseUri(mDatabaseUri)
// Erase the register keyfile
FileDatabaseHistoryAction.getInstance(context)

View File

@@ -1,33 +1,18 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.database.action
import android.app.AlertDialog
import android.app.Service
import android.content.*
import android.content.Context.*
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.net.Uri
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
@@ -87,8 +72,9 @@ import com.kunzisoft.keepass.tasks.ProgressTaskDialogFragment.Companion.PROGRESS
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import com.kunzisoft.keepass.viewmodels.ChallengeResponseViewModel
import java.util.ArrayList
import java.util.UUID
import kotlinx.coroutines.launch
import java.util.*
/**
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
@@ -103,7 +89,8 @@ class DatabaseTaskProvider {
var onActionFinish: ((database: Database,
actionTask: String,
result: ActionRunnable.Result) -> Unit)? = null
result: ActionRunnable.Result
) -> Unit)? = null
private var intentDatabaseTask: Intent
@@ -177,7 +164,8 @@ class DatabaseTaskProvider {
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
override fun onStartAction(database: Database,
progressMessage: ProgressMessage) {
progressMessage: ProgressMessage
) {
startDialog(progressMessage)
}
@@ -188,7 +176,8 @@ class DatabaseTaskProvider {
override fun onStopAction(database: Database,
actionTask: String,
result: ActionRunnable.Result) {
result: ActionRunnable.Result
) {
onActionFinish?.invoke(database, actionTask, result)
// Remove the progress task
stopDialog()
@@ -310,7 +299,7 @@ class DatabaseTaskProvider {
private fun bindService() {
initServiceConnection()
serviceConnection?.let {
context.bindService(intentDatabaseTask, it, BIND_AUTO_CREATE or BIND_IMPORTANT or BIND_ABOVE_CLIENT)
context.bindService(intentDatabaseTask, it, Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT or Context.BIND_ABOVE_CLIENT)
}
}

View File

@@ -21,20 +21,19 @@ package com.kunzisoft.keepass.database.action
import android.content.Context
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
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 +42,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 +58,14 @@ class LoadDatabaseRunnable(private val context: Context,
mMainCredential,
mChallengeResponseRetriever,
mReadonly,
UriUtil.getBinaryDir(context),
com.kunzisoft.keepass.utils.UriUtilDatabase.getBinaryDir(context),
{ memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
},
mFixDuplicateUUID,
progressTaskUpdater
)
}
catch (e: DatabaseInputException) {
} catch (e: DatabaseInputException) {
setError(e)
}
@@ -84,7 +82,7 @@ class LoadDatabaseRunnable(private val context: Context,
// Register the biometric
mCipherEncryptDatabase?.let { cipherDatabase ->
CipherDatabaseAction.getInstance(context)
com.kunzisoft.keepass.app.database.CipherDatabaseAction.getInstance(context)
.addOrUpdateCipherDatabase(cipherDatabase) // return value not called
}

View File

@@ -13,7 +13,6 @@ 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 {
@@ -134,7 +133,7 @@ class HardwareKeyResponseHelper {
activity.getString(R.string.error_driver_required, hardwareKey.toString())
)
.setPositiveButton(R.string.download) { _, _ ->
UriUtil.openExternalApp(activity, activity.getString(R.string.key_driver_app_id))
com.kunzisoft.keepass.utils.UriUtil.openExternalApp(activity, activity.getString(R.string.key_driver_app_id))
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show()

View File

@@ -1,36 +1,17 @@
/*
* Copyright 2018 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.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.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
@@ -50,7 +31,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
@@ -69,7 +50,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 +58,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 +72,7 @@ object IconPackChooser {
}
fun setSelectedIconPack(iconPackIdString: String?) {
override fun setSelectedIconPack(iconPackIdString: String?) {
for (iconPack in iconPackList) {
if (iconPack.id == iconPackIdString) {
iconPackSelected = iconPack
@@ -104,10 +87,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,7 +101,7 @@ 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
}

View File

@@ -1,22 +1,3 @@
/*
* 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.model
import android.content.Context
@@ -24,9 +5,8 @@ import android.net.Uri
import android.os.Parcel
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
@@ -90,7 +70,7 @@ data class SnapFileDatabaseInfo(var fileUri: Uri?,
return arrayOfNulls(size)
}
fun fromFileDatabaseInfo(fileDatabaseInfo: FileDatabaseInfo): SnapFileDatabaseInfo {
fun fromFileDatabaseInfo(fileDatabaseInfo: com.kunzisoft.keepass.viewmodels.FileDatabaseInfo): SnapFileDatabaseInfo {
return SnapFileDatabaseInfo(
fileDatabaseInfo.fileUri,
fileDatabaseInfo.exists,

View File

@@ -2,15 +2,16 @@ 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
import android.os.IBinder
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
class AdvancedUnlockNotificationService : NotificationService() {
@@ -44,7 +45,7 @@ class AdvancedUnlockNotificationService : NotificationService() {
}
override fun retrieveChannelName(): String {
return getString(R.string.advanced_unlock)
return getString(com.kunzisoft.keepass.R.string.advanced_unlock)
}
override fun onCreate() {
@@ -69,12 +70,12 @@ class AdvancedUnlockNotificationService : NotificationService() {
val biometricUnlockEnabled = PreferencesUtil.isBiometricUnlockEnable(this)
val notificationBuilder = buildNewNotification().apply {
setSmallIcon(if (biometricUnlockEnabled) {
R.drawable.notification_ic_fingerprint_unlock_24dp
com.kunzisoft.keepass.R.drawable.notification_ic_fingerprint_unlock_24dp
} else {
R.drawable.notification_ic_device_unlock_24dp
com.kunzisoft.keepass.R.drawable.notification_ic_device_unlock_24dp
})
setContentTitle(getString(R.string.advanced_unlock))
setContentText(getString(R.string.advanced_unlock_tap_delete))
setContentTitle(getString(com.kunzisoft.keepass.R.string.advanced_unlock))
setContentText(getString(com.kunzisoft.keepass.R.string.advanced_unlock_tap_delete))
setContentIntent(pendingDeleteIntent)
// Unfortunately swipe is disabled in lollipop+
setDeleteIntent(pendingDeleteIntent)
@@ -82,7 +83,7 @@ class AdvancedUnlockNotificationService : NotificationService() {
val notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this)
// Not necessarily a foreground service
if (mTimerJob == null && notificationTimeoutMilliSecs != TimeoutHelper.NEVER) {
if (mTimerJob == null && notificationTimeoutMilliSecs != com.kunzisoft.keepass.timeout.TimeoutHelper.NEVER) {
defineTimerJob(notificationBuilder, notificationTimeoutMilliSecs) {
sendBroadcast(Intent(REMOVE_ADVANCED_UNLOCK_KEY_ACTION))
}

View File

@@ -44,6 +44,7 @@ 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.icons.IconPackChooser
import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
@@ -55,9 +56,9 @@ import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import com.kunzisoft.keepass.utils.LOCK_ACTION
import com.kunzisoft.keepass.utils.closeDatabase
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
import java.util.UUID
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import java.util.*
open class DatabaseTaskNotificationService : LockNotificationService(), ProgressTaskUpdater {
@@ -75,6 +76,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mDatabaseInfoListeners = mutableListOf<DatabaseInfoListener>()
private var mActionTaskBinder = ActionTaskBinder()
private var mActionTaskListeners = mutableListOf<ActionTaskListener>()
// Channel to connect asynchronously a listener or a response
private var mRequestChallengeListenerChannel: Channel<RequestChallengeListener>? = null
private var mResponseChallengeChannel: Channel<ByteArray?>? = null
@@ -154,18 +156,28 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
}
interface DatabaseInfoListener {
fun onDatabaseInfoChanged(previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo)
fun onDatabaseInfoChanged(
previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
)
}
interface ActionTaskListener {
fun onStartAction(database: Database,
progressMessage: ProgressMessage)
fun onUpdateAction(database: Database,
progressMessage: ProgressMessage)
fun onStopAction(database: Database,
fun onStartAction(
database: Database,
progressMessage: ProgressMessage,
)
fun onUpdateAction(
database: Database,
progressMessage: ProgressMessage,
)
fun onStopAction(
database: Database,
actionTask: String,
result: ActionRunnable.Result)
result: ActionRunnable.Result,
)
}
interface RequestChallengeListener {
@@ -208,7 +220,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
@@ -294,7 +307,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 ->
@@ -328,18 +341,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,
@@ -352,7 +378,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK,
ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK,
ACTION_DATABASE_UPDATE_PARALLELISM_TASK,
ACTION_DATABASE_UPDATE_ITERATIONS_TASK -> buildDatabaseUpdateElementActionTask(intent, database)
ACTION_DATABASE_UPDATE_ITERATIONS_TASK,
-> buildDatabaseUpdateElementActionTask(intent, database)
ACTION_DATABASE_SAVE -> buildDatabaseSave(intent, database)
else -> null
}
@@ -393,7 +420,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
when (intentAction) {
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK -> {
ACTION_DATABASE_RELOAD_TASK,
-> {
saveDatabaseInfo()
}
}
@@ -403,7 +431,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Save the database info after performing save action
if (save) {
database.fileUri?.let {
val newSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
val newSnapFileDatabaseInfo =
SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it))
mLastLocalSaveTime = System.currentTimeMillis()
mSnapFileDatabaseInfo = newSnapFileDatabaseInfo
@@ -442,7 +471,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK,
null -> {
null,
-> {
START_STICKY
}
else -> {
@@ -470,9 +500,11 @@ 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
@@ -583,10 +615,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 {
@@ -634,8 +668,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 {
@@ -673,7 +709,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
@@ -708,9 +745,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
@@ -777,7 +816,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)
) {
@@ -795,8 +837,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))
@@ -804,7 +848,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)
@@ -813,7 +860,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 ->
@@ -832,7 +880,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)
@@ -841,7 +892,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 ->
@@ -860,7 +912,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)
@@ -869,7 +924,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 ->
@@ -888,7 +944,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)
@@ -897,7 +956,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 ->
@@ -916,7 +976,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)
@@ -940,7 +1003,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)
@@ -964,7 +1030,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)
@@ -982,7 +1051,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)
@@ -1004,7 +1076,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)
@@ -1026,16 +1101,21 @@ 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)
if (oldElement == null
|| newElement == null)
|| newElement == null
)
return null
return UpdateCompressionBinariesDatabaseRunnable(this,
@@ -1055,7 +1135,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,
@@ -1073,7 +1156,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,
@@ -1120,7 +1206,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private val TAG = DatabaseTaskNotificationService::class.java.name
private const val CHANNEL_DATABASE_ID = "com.kunzisoft.keepass.notification.channel.database"
private const val CHANNEL_DATABASE_ID =
"com.kunzisoft.keepass.notification.channel.database"
const val ACTION_DATABASE_CREATE_TASK = "ACTION_DATABASE_CREATE_TASK"
const val ACTION_DATABASE_LOAD_TASK = "ACTION_DATABASE_LOAD_TASK"
@@ -1137,19 +1224,30 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val ACTION_DATABASE_RESTORE_ENTRY_HISTORY = "ACTION_DATABASE_RESTORE_ENTRY_HISTORY"
const val ACTION_DATABASE_DELETE_ENTRY_HISTORY = "ACTION_DATABASE_DELETE_ENTRY_HISTORY"
const val ACTION_DATABASE_UPDATE_NAME_TASK = "ACTION_DATABASE_UPDATE_NAME_TASK"
const val ACTION_DATABASE_UPDATE_DESCRIPTION_TASK = "ACTION_DATABASE_UPDATE_DESCRIPTION_TASK"
const val ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK = "ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK"
const val ACTION_DATABASE_UPDATE_DESCRIPTION_TASK =
"ACTION_DATABASE_UPDATE_DESCRIPTION_TASK"
const val ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK =
"ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK"
const val ACTION_DATABASE_UPDATE_COLOR_TASK = "ACTION_DATABASE_UPDATE_COLOR_TASK"
const val ACTION_DATABASE_UPDATE_COMPRESSION_TASK = "ACTION_DATABASE_UPDATE_COMPRESSION_TASK"
const val ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK = "ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK"
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_COMPRESSION_TASK =
"ACTION_DATABASE_UPDATE_COMPRESSION_TASK"
const val ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK =
"ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK"
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_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"
@@ -1173,8 +1271,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
const val SAVE_DATABASE_KEY = "SAVE_DATABASE_KEY"
const val OLD_NODES_KEY = "OLD_NODES_KEY"
const val NEW_NODES_KEY = "NEW_NODES_KEY"
const val OLD_ELEMENT_KEY = "OLD_ELEMENT_KEY" // Warning type of this thing change every time
const val NEW_ELEMENT_KEY = "NEW_ELEMENT_KEY" // Warning type of this thing change every time
const val OLD_ELEMENT_KEY =
"OLD_ELEMENT_KEY" // Warning type of this thing change every time
const val NEW_ELEMENT_KEY =
"NEW_ELEMENT_KEY" // Warning type of this thing change every time
fun getListNodesFromBundle(database: Database, bundle: Bundle): List<Node> {
val nodesAction = ArrayList<Node>()

View File

@@ -1,22 +1,3 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.settings
import android.app.backup.BackupManager
@@ -26,18 +7,18 @@ import android.content.res.Resources
import android.net.Uri
import android.util.Log
import androidx.preference.PreferenceManager
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.Stylish
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
import com.kunzisoft.keepass.database.element.SortNodeEnum
import com.kunzisoft.keepass.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.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.UriUtil
import java.util.*
import java.util.HashSet
import java.util.Properties
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
object PreferencesUtil {
@@ -61,7 +42,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 +90,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,35 +132,35 @@ 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 defaultStyleString =
com.kunzisoft.keepass.activities.stylish.Stylish.defaultStyle(context)
val styleString = PreferenceManager.getDefaultSharedPreferences(context)
.getString(context.getString(R.string.setting_style_key), defaultStyleString)
?: defaultStyleString
// Return the system style
return Stylish.retrieveEquivalentSystemStyle(context, styleString)
return com.kunzisoft.keepass.activities.stylish.Stylish.retrieveEquivalentSystemStyle(
context,
styleString)
}
fun setStyle(context: Context, styleString: String) {
var tempThemeString = styleString
if (!UriUtil.contributingUser(context)) {
if (tempThemeString in BuildConfig.STYLES_DISABLED) {
tempThemeString = Stylish.defaultStyle(context)
if (!com.kunzisoft.keepass.utils.UriUtil.contributingUser(context)) {
if (tempThemeString in com.kunzisoft.keepass.BuildConfig.STYLES_DISABLED) {
tempThemeString =
com.kunzisoft.keepass.activities.stylish.Stylish.defaultStyle(context)
}
}
// Store light style to show selection in array list
tempThemeString = Stylish.retrieveEquivalentLightStyle(context, tempThemeString)
tempThemeString =
com.kunzisoft.keepass.activities.stylish.Stylish.retrieveEquivalentLightStyle(context,
tempThemeString)
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putString(context.getString(R.string.setting_style_key), tempThemeString)
.apply()
Stylish.load(context)
com.kunzisoft.keepass.activities.stylish.Stylish.load(context)
}
fun getStyleBrightness(context: Context): String? {
@@ -292,16 +268,16 @@ object PreferencesUtil {
}
}
fun getDefaultPassphraseWordCase(context: Context): PassphraseGenerator.WordCase {
fun getDefaultPassphraseWordCase(context: Context): com.kunzisoft.keepass.password.PassphraseGenerator.WordCase {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return PassphraseGenerator.WordCase
.getByOrdinal(prefs.getInt(context
return com.kunzisoft.keepass.password.PassphraseGenerator.WordCase.Companion.getByOrdinal(
prefs.getInt(context
.getString(R.string.passphrase_generator_word_case_key),
0)
)
}
fun setDefaultPassphraseWordCase(context: Context, wordCase: PassphraseGenerator.WordCase) {
fun setDefaultPassphraseWordCase(context: Context, wordCase: com.kunzisoft.keepass.password.PassphraseGenerator.WordCase) {
PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
putInt(
context.getString(R.string.passphrase_generator_word_case_key),
@@ -414,45 +390,23 @@ 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
TIMEOUT_DEFAULT)?.toLong()
?: com.kunzisoft.keepass.timeout.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
?: com.kunzisoft.keepass.timeout.TimeoutHelper.DEFAULT_TIMEOUT
}
fun isLockDatabaseWhenScreenShutOffEnable(context: Context): Boolean {
@@ -500,7 +454,8 @@ object PreferencesUtil {
return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key),
context.resources.getBoolean(R.bool.biometric_unlock_enable_default))
&& (if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
AdvancedUnlockManager.biometricUnlockSupported(context)
com.kunzisoft.keepass.biometric.AdvancedUnlockManager.Companion.biometricUnlockSupported(
context)
} else {
false
})
@@ -593,13 +548,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),
@@ -631,7 +579,9 @@ object PreferencesUtil {
}
fun isKeyboardSaveSearchInfoEnable(context: Context): Boolean {
if (!MagikeyboardService.activatedInSettings(context))
if (!com.kunzisoft.keepass.magikeyboard.MagikeyboardService.Companion.activatedInSettings(
context)
)
return false
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_save_search_info_key),
@@ -716,7 +666,7 @@ object PreferencesUtil {
fun getDefaultApplicationIdBlocklist(resources: Resources?): Set<String> {
return resources?.getStringArray(R.array.autofill_application_id_blocklist_default)
?.toMutableSet()?.apply {
add(BuildConfig.APPLICATION_ID)
add(com.kunzisoft.keepass.BuildConfig.APPLICATION_ID)
} ?: emptySet()
}
@@ -754,10 +704,12 @@ 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) {
for ((name, value) in com.kunzisoft.keepass.education.Education.Companion.getEducationSharedPreferences(
context).all) {
properties[name] = value.toString()
}
return properties
@@ -780,7 +732,9 @@ object PreferencesUtil {
try {
putProperty(this, name as String, value as String)
} catch (e:Exception) {
Log.e("PreferencesUtil", "Error when trying to parse app property $name=$value", e)
Log.e("PreferencesUtil",
"Error when trying to parse app property $name=$value",
e)
}
}
}.apply()
@@ -796,8 +750,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 +793,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 +803,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 +823,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

@@ -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

@@ -1,37 +1,16 @@
/*
* Copyright 2019 Jeremy Jamet / Kunzisoft.
*
* This file is part of KeePassDX.
*
* KeePassDX is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeePassDX is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.viewmodels
import android.content.Context
import android.net.Uri
import android.text.format.Formatter
import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.utils.UriUtil
import java.io.Serializable
import java.text.DateFormat
import java.util.*
import java.util.Date
class FileDatabaseInfo : Serializable {
private var context: Context
private var documentFile: DocumentFile? = null
private var documentFile: androidx.documentfile.provider.DocumentFile? = null
var fileUri: Uri?
private set
@@ -43,16 +22,16 @@ class FileDatabaseInfo : Serializable {
constructor(context: Context, filePath: String) {
this.context = context
this.fileUri = UriUtil.parse(filePath)
this.fileUri = com.kunzisoft.keepass.utils.UriUtil.parse(filePath)
init()
}
fun init() {
// Check permission
fileUri?.let { uri ->
UriUtil.takeUriPermission(context.contentResolver, uri)
com.kunzisoft.keepass.utils.UriUtil.takeUriPermission(context.contentResolver, uri)
}
documentFile = UriUtil.getFileData(context, fileUri)
documentFile = com.kunzisoft.keepass.utils.UriUtil.getFileData(context, fileUri)
}
var exists: Boolean = false

View File

@@ -29,7 +29,6 @@
<dimen name="card_view_margin_vertical">5dp</dimen>
<dimen name="card_view_padding">14dp</dimen>
<dimen name="item_file_info_height">148dp</dimen>
<dimen name="icon_size">32dp</dimen>
<dimen name="lock_button_size">48dp</dimen>
<dimen name="hidden_lock_button_size">0dp</dimen>
<dimen name="content_percent">1</dimen>

View File

@@ -78,12 +78,10 @@
<string name="content_description_remove_from_list">Remove</string>
<string name="content_description_keyboard_close_fields">Close fields</string>
<string name="select_to_copy">Select to copy %1$s to clipboard</string>
<string name="retrieving_db_key">Retrieving database key…</string>
<string name="waiting_challenge_request">Waiting for the challenge request…</string>
<string name="waiting_challenge_response">Waiting for the challenge response…</string>
<string name="database">Database</string>
<string name="template_group_name">Templates</string>
<string name="decrypting_db">Decrypting database content…</string>
<string name="default_checkbox">Use as default database</string>
<string name="digits">Digits</string>
<string name="html_about_licence">KeePassDX © %1$d Kunzisoft is &lt;strong&gt;open source&lt;/strong&gt; and &lt;strong&gt;without advertising&lt;/strong&gt;.
@@ -92,10 +90,8 @@
<string name="html_about_contribution">In order to &lt;strong&gt;keep our freedom&lt;/strong&gt;, &lt;strong&gt;fix bugs&lt;/strong&gt;, &lt;strong&gt;add features&lt;/strong&gt; and &lt;strong&gt;to be always active&lt;/strong&gt;, we count on your &lt;strong&gt;contribution&lt;/strong&gt;.</string>
<string name="entry_accessed">Accessed</string>
<string name="entry_cancel">Cancel</string>
<string name="entry_notes">Notes</string>
<string name="entry_confpassword">Confirm password</string>
<string name="entry_created">Created</string>
<string name="entry_expires">Expires</string>
<string name="expired">Expired</string>
<string name="entry_UUID">UUID</string>
<string name="entry_history">History</string>
@@ -108,11 +104,9 @@
<string name="auto_type">Auto-Type</string>
<string name="auto_type_sequence">Auto-Type sequence</string>
<string name="entry_not_found">Could not find entry data.</string>
<string name="entry_password">Password</string>
<string name="tags">Tags</string>
<string name="custom_data">Custom data</string>
<string name="save">Save</string>
<string name="entry_title">Title</string>
<string name="entry_setup_otp">Set up one-time password</string>
<string name="otp_type">OTP type</string>
<string name="otp_secret">Secret</string>
@@ -124,38 +118,8 @@
<string name="current_group">Current group</string>
<string name="case_sensitive">Case sensitive</string>
<string name="regex">Regular expression</string>
<string name="debit_credit_card">Debit / Credit Card</string>
<string name="holder">Holder</string>
<string name="number">Number</string>
<string name="card_verification_value">CVV</string>
<string name="personal_identification_number">PIN</string>
<string name="id_card">ID Card</string>
<string name="name">Name</string>
<string name="place_of_issue">Place of issue</string>
<string name="date_of_issue">Date of issue</string>
<string name="email">Email</string>
<string name="email_address">Email address</string>
<string name="wireless">Wi-Fi</string>
<string name="ssid">SSID</string>
<string name="type">Type</string>
<string name="cryptocurrency">Cryptocurrency wallet</string>
<string name="token">Token</string>
<string name="public_key">Public key</string>
<string name="private_key">Private key</string>
<string name="seed">Seed</string>
<string name="account">Account</string>
<string name="bank">Bank</string>
<string name="bank_name">Bank name</string>
<string name="bank_identifier_code">SWIFT / BIC</string>
<string name="international_bank_account_number">IBAN</string>
<string name="secure_note">Secure Note</string>
<string name="membership">Membership</string>
<string name="standard">Standard</string>
<string name="template">Template</string>
<string name="version">Version</string>
<string name="entry_otp">OTP</string>
<string name="entry_url">URL</string>
<string name="entry_user_name">Username</string>
<string name="error_arc4">The Arcfour stream cipher is not supported.</string>
<string name="error_can_not_handle_uri">Could not handle this URI in KeePassDX.</string>
<string name="error_file_not_create">Could not create file</string>
@@ -166,10 +130,6 @@
<string name="error_word_reserved">This word is reserved and cannot be used.</string>
<string name="error_nokeyfile">Select a keyfile.</string>
<string name="error_no_hardware_key">Select a hardware key.</string>
<string name="error_out_of_memory">No memory to load your entire database.</string>
<string name="error_XML_malformed">XML malformed.</string>
<string name="error_load_database">Could not load the database.</string>
<string name="error_load_database_KDF_memory">Could not load the key. Try to lower the KDF \"Memory Usage\".</string>
<string name="error_pass_gen_type">At least one password generation type must be selected.</string>
<string name="error_disallow_no_credentials">At least one credential must be set.</string>
<string name="error_pass_match">The passwords do not match.</string>
@@ -178,13 +138,8 @@
<string name="error_label_exists">This label already exists.</string>
<string name="error_wrong_length">Enter a positive integer number in the \"Length\" field.</string>
<string name="error_autofill_enable_service">Could not enable autofill service.</string>
<string name="error_move_group_here">You cannot move a group here.</string>
<string name="error_move_entry_here">You cannot move an entry here.</string>
<string name="error_copy_entry_here">You cannot copy an entry here.</string>
<string name="error_copy_group_here">You cannot copy a group here.</string>
<string name="error_create_database">Unable to create database file.</string>
<string name="error_create_database_file">Unable to create database with this password and keyfile.</string>
<string name="error_save_database">Could not save the database.</string>
<string name="error_otp_secret_key">Secret key must be in Base32 format.</string>
<string name="error_otp_counter">Counter must be between %1$d and %2$d.</string>
<string name="error_otp_period">Period must be between %1$d and %2$d seconds.</string>
@@ -205,14 +160,8 @@
<string name="error_no_response_from_challenge">Unable to get the response from the challenge.</string>
<string name="error_cancel_by_user">Cancelled by user.</string>
<string name="error_driver_required">Driver for %1$s is required.</string>
<string name="error_unable_merge_database_kdb">Unable to merge from a database V1.</string>
<string name="error_location_unknown">Database location is unknown, database action cannot be performed.</string>
<string name="error_hardware_key_unsupported">Hardware key is not supported.</string>
<string name="error_empty_key">"Key cannot be empty."</string>
<string name="field_name">Field name</string>
<string name="field_value">Field value</string>
<string name="file_not_found_content">Could not find file. Try reopening it from your file browser.</string>
<string name="corrupted_file">Corrupted file.</string>
<string name="file_browser">File manager</string>
<string name="generate_password">Generate password</string>
<string name="hint_conf_pass">Confirm password</string>
@@ -224,10 +173,6 @@
<string name="hint_pass">Password</string>
<string name="password">Password</string>
<string name="passphrase">Passphrase</string>
<string name="invalid_credentials">Could not read credentials.</string>
<string name="invalid_algorithm">Wrong algorithm.</string>
<string name="invalid_db_same_uuid">%1$s with the same UUID %2$s already exists.</string>
<string name="invalid_db_sig">Could not recognize the database format.</string>
<string name="keyfile_is_empty">The keyfile is empty.</string>
<string name="length">Length</string>
<string name="hide_password_title">Hide passwords</string>
@@ -349,7 +294,6 @@
<string name="special">Special</string>
<string name="search">Search</string>
<string name="underline">Underline</string>
<string name="unsupported_db_version">Unsupported database version.</string>
<string name="uppercase">Upper-case</string>
<string name="warning">Warning</string>
<string name="warning_password_encoding">Avoid password characters outside of text encoding format in database file (unrecognized chars are converted to the same letter).</string>
@@ -487,10 +431,6 @@
<string name="application_appearance">Interface</string>
<string name="other">Other</string>
<string name="compression">Compression</string>
<string name="compression_none">None</string>
<string name="compression_gzip">Gzip</string>
<string name="recycle_bin">Recycle bin</string>
<string name="templates">Templates</string>
<string name="keyboard">Keyboard</string>
<string name="magic_keyboard_title">Magikeyboard</string>
<string name="magic_keyboard_explanation_summary">Activate a custom keyboard populating your passwords and all identity fields</string>

View File

@@ -1,7 +1,7 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 32
@@ -16,6 +16,13 @@ android {
consumerProguardFiles "consumer-rules.pro"
buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
}
buildTypes {
@@ -33,6 +40,8 @@ android {
}
}
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'
@@ -47,6 +56,10 @@ dependencies {
implementation project(path: ':icon-pack-classic')
implementation project(path: ':icon-pack-material')
// 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'

View File

@@ -26,7 +26,7 @@ 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
@@ -61,7 +61,7 @@ class MergeDatabaseRunnable(private val context: Context,
if (result.isSuccess) {
// Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context)
DatabasePreferencesUtil.saveCurrentTime(context)
}
}

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

@@ -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
@@ -646,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 {
@@ -785,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 ->
@@ -866,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)
@@ -1005,7 +1007,7 @@ class Database {
}
fun clearAndClose(context: Context? = null) {
clearIndexesAndBinaries(context?.let { UriUtil.getBinaryDir(context) })
clearIndexesAndBinaries(context?.let { com.kunzisoft.keepass.utils.UriUtilDatabase.getBinaryDir(context) })
this.mDatabaseKDB = null
this.mDatabaseKDBX = null
this.fileUri = null

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

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.database
import android.content.res.Resources
import android.os.Parcel
import android.os.Parcelable
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.R
import com.kunzisoft.keepass.utils.ObjectNameResource
import com.kunzisoft.keepass.utils.readEnum
import com.kunzisoft.keepass.utils.writeEnum

View File

@@ -24,7 +24,7 @@ import android.content.res.Resources
import android.util.Base64
import android.util.Log
import com.kunzisoft.encrypt.HashManager
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.R
import com.kunzisoft.keepass.database.action.node.NodeHandler
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.VariantDictionary
@@ -32,7 +32,12 @@ import com.kunzisoft.keepass.database.crypto.kdf.AesKdf
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.crypto.kdf.KdfFactory
import com.kunzisoft.keepass.database.crypto.kdf.KdfParameters
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.CompositeKey
import com.kunzisoft.keepass.database.element.CustomData
import com.kunzisoft.keepass.database.element.DateInstant
import com.kunzisoft.keepass.database.element.DeletedObject
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.database.element.Tags
import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.element.database.DatabaseKDB.Companion.BACKUP_FOLDER_TITLE
import com.kunzisoft.keepass.database.element.entry.EntryKDBX
@@ -58,7 +63,8 @@ import java.io.IOException
import java.nio.charset.Charset
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import java.util.Arrays
import java.util.UUID
import javax.crypto.Mac
import kotlin.math.min
@@ -141,6 +147,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
var maintenanceHistoryDays = UnsignedInt(365)
var color = ""
/**
* Determine if RecycleBin is enable or not
* @return true if RecycleBin enable, false if is not available or not enable
@@ -168,9 +175,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
/**
* Create a new database with a root group
*/
constructor(databaseName: String,
constructor(
databaseName: String,
rootName: String,
templatesGroupName: String? = null) {
templatesGroupName: String? = null,
) {
name = databaseName
kdbxVersion = FILE_VERSION_31
val group = createGroup().apply {
@@ -231,7 +240,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
fun deriveMasterKey(
contentResolver: ContentResolver,
mainCredential: MainCredential,
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
) {
// Retrieve each plain credential
val password = mainCredential.password
@@ -263,7 +272,7 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
@Throws(DatabaseOutputException::class)
fun deriveCompositeKey(
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray
challengeResponseRetriever: (HardwareKey, ByteArray?) -> ByteArray,
) {
val passwordBytes = mCompositeKey.passwordData
val keyFileBytes = mCompositeKey.keyFileData
@@ -286,9 +295,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
}
}
private fun composedKeyToMasterKey(passwordData: ByteArray?,
private fun composedKeyToMasterKey(
passwordData: ByteArray?,
keyFileData: ByteArray?,
hardwareKeyData: ByteArray? = null): ByteArray {
hardwareKeyData: ByteArray? = null,
): ByteArray {
return HashManager.hashSha256(
passwordData,
keyFileData,
@@ -308,26 +319,33 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
// https://keepass.info/help/kb/kdbx_4.1.html
val containsGroupWithTag = groupHandler.containsTags
val containsEntryWithPasswordQualityEstimationDisabled = entryHandler.passwordQualityEstimationDisabled
val containsCustomIconWithNameOrLastModificationTime = iconsManager.containsCustomIconWithNameOrLastModificationTime()
val containsHeaderCustomDataWithLastModificationTime = customData.containsItemWithLastModificationTime()
val containsEntryWithPasswordQualityEstimationDisabled =
entryHandler.passwordQualityEstimationDisabled
val containsCustomIconWithNameOrLastModificationTime =
iconsManager.containsCustomIconWithNameOrLastModificationTime()
val containsHeaderCustomDataWithLastModificationTime =
customData.containsItemWithLastModificationTime()
// https://keepass.info/help/kb/kdbx_4.html
// If AES is not use, it's at least 4.0
val keyDerivationFunction = kdfEngine
val kdfIsNotAes = keyDerivationFunction != null && keyDerivationFunction.uuid != AesKdf.CIPHER_UUID
val kdfIsNotAes =
keyDerivationFunction != null && keyDerivationFunction.uuid != AesKdf.CIPHER_UUID
val containsHeaderCustomData = customData.isNotEmpty()
val containsNodeCustomData = entryHandler.containsCustomData || groupHandler.containsCustomData
val containsNodeCustomData =
entryHandler.containsCustomData || groupHandler.containsCustomData
// Check each condition to determine version
return if (containsGroupWithTag
|| containsEntryWithPasswordQualityEstimationDisabled
|| containsCustomIconWithNameOrLastModificationTime
|| containsHeaderCustomDataWithLastModificationTime) {
|| containsHeaderCustomDataWithLastModificationTime
) {
FILE_VERSION_41
} else if (kdfIsNotAes
|| containsHeaderCustomData
|| containsNodeCustomData) {
|| containsNodeCustomData
) {
FILE_VERSION_40
} else {
FILE_VERSION_31
@@ -339,8 +357,10 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
CompressionAlgorithm.GZip
)
fun changeBinaryCompression(oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm) {
fun changeBinaryCompression(
oldCompression: CompressionAlgorithm,
newCompression: CompressionAlgorithm,
) {
when (oldCompression) {
CompressionAlgorithm.None -> {
when (newCompression) {
@@ -455,17 +475,21 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
return this.iconsManager.getIcon(iconId)
}
fun buildNewCustomIcon(customIconId: UUID? = null,
result: (IconImageCustom, BinaryData?) -> Unit) {
fun buildNewCustomIcon(
customIconId: UUID? = null,
result: (IconImageCustom, BinaryData?) -> Unit,
) {
// Create a binary file for a brand new custom icon
addCustomIcon(customIconId, "", null, false, result)
}
fun addCustomIcon(customIconId: UUID? = null,
fun addCustomIcon(
customIconId: UUID? = null,
name: String,
lastModificationTime: DateInstant?,
smallSize: Boolean,
result: (IconImageCustom, BinaryData?) -> Unit) {
result: (IconImageCustom, BinaryData?) -> Unit,
) {
iconsManager.addCustomIcon(customIconId, name, lastModificationTime, { uniqueBinaryId ->
// Create a byte array for better performance with small data
binaryCache.getBinaryData(uniqueBinaryId, smallSize)
@@ -525,7 +549,10 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
return mTemplateEngine.getTemplate(entry)
}
fun decodeEntryWithTemplateConfiguration(entryKDBX: EntryKDBX, entryIsTemplate: Boolean): EntryKDBX {
fun decodeEntryWithTemplateConfiguration(
entryKDBX: EntryKDBX,
entryIsTemplate: Boolean,
): EntryKDBX {
return if (entryIsTemplate) {
mTemplateEngine.decodeTemplateEntry(entryKDBX)
} else {
@@ -533,7 +560,11 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
}
}
fun encodeEntryWithTemplateConfiguration(entryKDBX: EntryKDBX, entryIsTemplate: Boolean, template: Template): EntryKDBX {
fun encodeEntryWithTemplateConfiguration(
entryKDBX: EntryKDBX,
entryIsTemplate: Boolean,
template: Template,
): EntryKDBX {
return if (entryIsTemplate) {
mTemplateEngine.encodeTemplateEntry(entryKDBX)
} else {
@@ -603,7 +634,8 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
val kdfEngine = getKdfEngineFromParameters(keyDerivationFunctionParameters)
?: throw IOException("Unknown key derivation function")
var transformedMasterKey = kdfEngine.transform(masterKey, keyDerivationFunctionParameters)
var transformedMasterKey =
kdfEngine.transform(masterKey, keyDerivationFunctionParameters)
if (transformedMasterKey.size != 32) {
transformedMasterKey = HashManager.hashSha256(transformedMasterKey)
}
@@ -702,7 +734,8 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
var currentGroup: GroupKDBX? = group
while (currentGroup != null) {
if (currentGroup.parent == rootGroup
&& currentGroup.title.equals(BACKUP_FOLDER_TITLE, ignoreCase = true)) {
&& currentGroup.title.equals(BACKUP_FOLDER_TITLE, ignoreCase = true)
) {
return true
}
currentGroup = currentGroup.parent
@@ -761,7 +794,8 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
if (recycleBin == null)
return false
if (node is GroupKDBX
&& recycleBin!!.isContainedIn(node))
&& recycleBin!!.isContainedIn(node)
)
return false
if (!node.isContainedIn(recycleBin!!))
return true
@@ -798,10 +832,12 @@ class DatabaseKDBX : DatabaseVersioned<UUID, UUID, GroupKDBX, EntryKDBX> {
mFieldReferenceEngine.clear()
}
fun buildNewBinaryAttachment(smallSize: Boolean,
fun buildNewBinaryAttachment(
smallSize: Boolean,
compression: Boolean,
protection: Boolean,
binaryPoolId: Int? = null): BinaryData {
binaryPoolId: Int? = null,
): BinaryData {
return attachmentPool.put(binaryPoolId) { uniqueBinaryId ->
binaryCache.getBinaryData(uniqueBinaryId, smallSize, compression, protection)
}.binary

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