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\"}" buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
manifestPlaceholders = [ googleAndroidBackupAPIKey:"unused" ] manifestPlaceholders = [ googleAndroidBackupAPIKey:"unused" ]
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
} }
buildTypes { buildTypes {

View File

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

View File

@@ -32,7 +32,6 @@ import android.view.inputmethod.EditorInfo
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.model.OtpModel import com.kunzisoft.keepass.model.OtpModel
import com.kunzisoft.keepass.otp.OtpElement import com.kunzisoft.keepass.otp.OtpElement
@@ -470,4 +469,4 @@ class SetOTPDialogFragment : DatabaseDialogFragment() {
} }
} }
} }
} }

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 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.net.Uri
import android.os.IBinder import android.os.IBinder
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.services.AdvancedUnlockNotificationService
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter import com.kunzisoft.keepass.utils.SingletonHolderParameter
import java.util.* import java.util.LinkedList
class CipherDatabaseAction(context: Context) { class CipherDatabaseAction(context: Context) {
private val applicationContext = context.applicationContext private val applicationContext = context.applicationContext
private val cipherDatabaseDao = private val cipherDatabaseDao =
AppDatabase AppDatabase.getDatabase(applicationContext)
.getDatabase(applicationContext)
.cipherDatabaseDao() .cipherDatabaseDao()
// Temp DAO to easily remove content if object no longer in memory // Temp DAO to easily remove content if object no longer in memory
private var useTempDao = PreferencesUtil.isTempAdvancedUnlockEnable(applicationContext) 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 mServiceConnection: ServiceConnection? = null
private var mDatabaseListeners = LinkedList<CipherDatabaseListener>() private var mDatabaseListeners = LinkedList<CipherDatabaseListener>()
private var mAdvancedUnlockBroadcastReceiver = AdvancedUnlockNotificationService.AdvancedUnlockReceiver { private var mAdvancedUnlockBroadcastReceiver = com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.AdvancedUnlockReceiver {
deleteAll() deleteAll()
removeAllDataAndDetach() removeAllDataAndDetach()
} }
@@ -67,12 +49,12 @@ class CipherDatabaseAction(context: Context) {
@Synchronized @Synchronized
private fun attachService(performedAction: () -> Unit) { private fun attachService(performedAction: () -> Unit) {
applicationContext.registerReceiver(mAdvancedUnlockBroadcastReceiver, IntentFilter().apply { 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 { mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) { override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
mBinder = (serviceBinder as AdvancedUnlockNotificationService.AdvancedUnlockBinder) mBinder = (serviceBinder as com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.AdvancedUnlockBinder)
performedAction.invoke() performedAction.invoke()
} }
@@ -81,9 +63,9 @@ class CipherDatabaseAction(context: Context) {
} }
} }
try { try {
AdvancedUnlockNotificationService.bindService(applicationContext, com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.bindService(applicationContext,
mServiceConnection!!, mServiceConnection!!,
Context.BIND_AUTO_CREATE) Context.BIND_AUTO_CREATE)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Unable to start cipher action", e) Log.e(TAG, "Unable to start cipher action", e)
performedAction.invoke() performedAction.invoke()
@@ -97,7 +79,7 @@ class CipherDatabaseAction(context: Context) {
} catch (e: Exception) {} } catch (e: Exception) {}
mServiceConnection?.let { mServiceConnection?.let {
AdvancedUnlockNotificationService.unbindService(applicationContext, it) com.kunzisoft.keepass.services.AdvancedUnlockNotificationService.unbindService(applicationContext, it)
} }
} }
@@ -148,8 +130,9 @@ class CipherDatabaseAction(context: Context) {
} }
} else { } else {
IOActionTask( IOActionTask(
{ {
cipherDatabaseDao.getByDatabaseUri(databaseUri.toString())?.let { cipherDatabaseEntity -> cipherDatabaseDao.getByDatabaseUri(databaseUri.toString())
?.let { cipherDatabaseEntity ->
CipherEncryptDatabase().apply { CipherEncryptDatabase().apply {
this.databaseUri = Uri.parse(cipherDatabaseEntity.databaseUri) this.databaseUri = Uri.parse(cipherDatabaseEntity.databaseUri)
this.encryptedValue = Base64.decode( this.encryptedValue = Base64.decode(
@@ -162,10 +145,10 @@ class CipherDatabaseAction(context: Context) {
) )
} }
} }
}, },
{ {
cipherDatabaseResultListener.invoke(it) cipherDatabaseResultListener.invoke(it)
} }
).execute() ).execute()
} }
} }
@@ -222,12 +205,12 @@ class CipherDatabaseAction(context: Context) {
} }
} else { } else {
IOActionTask( IOActionTask(
{ {
cipherDatabaseDao.deleteByDatabaseUri(databaseUri.toString()) cipherDatabaseDao.deleteByDatabaseUri(databaseUri.toString())
}, },
{ {
cipherDatabaseResultListener?.invoke() cipherDatabaseResultListener?.invoke()
} }
).execute() ).execute()
} }
} }
@@ -240,9 +223,9 @@ class CipherDatabaseAction(context: Context) {
} }
// To erase the residues // To erase the residues
IOActionTask( IOActionTask(
{ {
cipherDatabaseDao.deleteAll() cipherDatabaseDao.deleteAll()
} }
).execute() ).execute()
// Unbind // Unbind
removeAllDataAndDetach() removeAllDataAndDetach()
@@ -251,4 +234,4 @@ class CipherDatabaseAction(context: Context) {
companion object : SingletonHolderParameter<CipherDatabaseAction, Context>(::CipherDatabaseAction) { companion object : SingletonHolderParameter<CipherDatabaseAction, Context>(::CipherDatabaseAction) {
private val TAG = CipherDatabaseAction::class.java.name private val TAG = CipherDatabaseAction::class.java.name
} }
} }

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 package com.kunzisoft.keepass.app.database
import android.content.Context import android.content.Context
@@ -25,214 +6,225 @@ import android.util.Log
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.DatabaseFile import com.kunzisoft.keepass.model.DatabaseFile
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.SingletonHolderParameter import com.kunzisoft.keepass.utils.UriUtilDatabase
import com.kunzisoft.keepass.utils.UriUtil
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
class FileDatabaseHistoryAction(private val applicationContext: Context) { class FileDatabaseHistoryAction(private val applicationContext: Context) {
private val databaseFileHistoryDao = private val databaseFileHistoryDao =
AppDatabase AppDatabase.getDatabase(applicationContext)
.getDatabase(applicationContext)
.fileDatabaseHistoryDao() .fileDatabaseHistoryDao()
fun getDatabaseFile(databaseUri: Uri, fun getDatabaseFile(databaseUri: Uri,
databaseFileResult: (DatabaseFile?) -> Unit) { databaseFileResult: (com.kunzisoft.keepass.model.DatabaseFile?) -> Unit) {
IOActionTask( IOActionTask(
{ {
val fileDatabaseHistoryEntity = databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString()) val fileDatabaseHistoryEntity =
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, databaseUri) databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
DatabaseFile( val fileDatabaseInfo = FileDatabaseInfo(
databaseUri, applicationContext,
UriUtil.parse(fileDatabaseHistoryEntity?.keyFileUri), databaseUri)
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey), DatabaseFile(
UriUtil.decode(fileDatabaseHistoryEntity?.databaseUri), databaseUri,
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""), UriUtilDatabase.parse(fileDatabaseHistoryEntity?.keyFileUri),
fileDatabaseInfo.exists, HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity?.hardwareKey),
fileDatabaseInfo.getLastModificationString(), UriUtilDatabase.decode(fileDatabaseHistoryEntity?.databaseUri),
fileDatabaseInfo.getSizeString() fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias
) ?: ""),
}, fileDatabaseInfo.exists,
{ fileDatabaseInfo.getLastModificationString(),
databaseFileResult.invoke(it) fileDatabaseInfo.getSizeString()
} )
},
{
databaseFileResult.invoke(it)
}
).execute() ).execute()
} }
fun getKeyFileUriByDatabaseUri(databaseUri: Uri, fun getKeyFileUriByDatabaseUri(databaseUri: Uri,
keyFileUriResultListener: (Uri?) -> Unit) { keyFileUriResultListener: (Uri?) -> Unit) {
IOActionTask( IOActionTask(
{ {
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString()) databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
}, },
{ {
it?.let { fileHistoryEntity -> it?.let { fileHistoryEntity ->
fileHistoryEntity.keyFileUri?.let { keyFileUri -> fileHistoryEntity.keyFileUri?.let { keyFileUri ->
keyFileUriResultListener.invoke(UriUtil.parse(keyFileUri)) keyFileUriResultListener.invoke(UriUtilDatabase.parse(
} keyFileUri))
} ?: keyFileUriResultListener.invoke(null) }
} } ?: keyFileUriResultListener.invoke(null)
}
).execute() ).execute()
} }
fun getDatabaseFileList(databaseFileListResult: (List<DatabaseFile>) -> Unit) { fun getDatabaseFileList(databaseFileListResult: (List<com.kunzisoft.keepass.model.DatabaseFile>) -> Unit) {
IOActionTask( IOActionTask(
{ {
val hideBrokenLocations = PreferencesUtil.hideBrokenLocations(applicationContext) val hideBrokenLocations =
// Show only uri accessible PreferencesUtil.hideBrokenLocations(
val databaseFileListLoaded = ArrayList<DatabaseFile>() applicationContext)
databaseFileHistoryDao.getAll().forEach { fileDatabaseHistoryEntity -> // Show only uri accessible
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, fileDatabaseHistoryEntity.databaseUri) val databaseFileListLoaded = ArrayList<DatabaseFile>()
if (hideBrokenLocations && fileDatabaseInfo.exists databaseFileHistoryDao.getAll().forEach { fileDatabaseHistoryEntity ->
|| !hideBrokenLocations) { val fileDatabaseInfo = FileDatabaseInfo(
databaseFileListLoaded.add( applicationContext,
DatabaseFile( fileDatabaseHistoryEntity.databaseUri)
UriUtil.parse(fileDatabaseHistoryEntity.databaseUri), if (hideBrokenLocations && fileDatabaseInfo.exists
UriUtil.parse(fileDatabaseHistoryEntity.keyFileUri), || !hideBrokenLocations
HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey), ) {
UriUtil.decode(fileDatabaseHistoryEntity.databaseUri), databaseFileListLoaded.add(
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias), DatabaseFile(
fileDatabaseInfo.exists, UriUtilDatabase.parse(fileDatabaseHistoryEntity.databaseUri),
fileDatabaseInfo.getLastModificationString(), UriUtilDatabase.parse(fileDatabaseHistoryEntity.keyFileUri),
fileDatabaseInfo.getSizeString() HardwareKey.getHardwareKeyFromString(fileDatabaseHistoryEntity.hardwareKey),
) UriUtilDatabase.decode(fileDatabaseHistoryEntity.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistoryEntity.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
) )
} )
}
databaseFileListLoaded
},
{
databaseFileList ->
databaseFileList?.let {
databaseFileListResult.invoke(it)
} }
} }
databaseFileListLoaded
},
{ databaseFileList ->
databaseFileList?.let {
databaseFileListResult.invoke(it)
}
}
).execute() ).execute()
} }
fun addOrUpdateDatabaseUri(databaseUri: Uri, fun addOrUpdateDatabaseUri(databaseUri: Uri,
keyFileUri: Uri? = null, keyFileUri: Uri? = null,
hardwareKey: HardwareKey? = null, hardwareKey: HardwareKey? = null,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) { databaseFileAddedOrUpdatedResult: ((com.kunzisoft.keepass.model.DatabaseFile?) -> Unit)? = null) {
addOrUpdateDatabaseFile(DatabaseFile( addOrUpdateDatabaseFile(com.kunzisoft.keepass.model.DatabaseFile(
databaseUri, databaseUri,
keyFileUri, keyFileUri,
hardwareKey hardwareKey
), databaseFileAddedOrUpdatedResult) ), databaseFileAddedOrUpdatedResult)
} }
fun addOrUpdateDatabaseFile(databaseFileToAddOrUpdate: DatabaseFile, fun addOrUpdateDatabaseFile(databaseFileToAddOrUpdate: com.kunzisoft.keepass.model.DatabaseFile,
databaseFileAddedOrUpdatedResult: ((DatabaseFile?) -> Unit)? = null) { databaseFileAddedOrUpdatedResult: ((com.kunzisoft.keepass.model.DatabaseFile?) -> Unit)? = null) {
IOActionTask( IOActionTask(
{ {
databaseFileToAddOrUpdate.databaseUri?.let { databaseUri -> databaseFileToAddOrUpdate.databaseUri?.let { databaseUri ->
// Try to get info in database first // Try to get info in database first
val fileDatabaseHistoryRetrieve = databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString()) val fileDatabaseHistoryRetrieve =
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
// Complete alias if not exists // Complete alias if not exists
val fileDatabaseHistory = FileDatabaseHistoryEntity( val fileDatabaseHistory =
databaseUri.toString(), FileDatabaseHistoryEntity(
databaseFileToAddOrUpdate.databaseAlias databaseUri.toString(),
?: fileDatabaseHistoryRetrieve?.databaseAlias databaseFileToAddOrUpdate.databaseAlias
?: "", ?: fileDatabaseHistoryRetrieve?.databaseAlias
databaseFileToAddOrUpdate.keyFileUri?.toString(), ?: "",
databaseFileToAddOrUpdate.hardwareKey?.value, databaseFileToAddOrUpdate.keyFileUri?.toString(),
System.currentTimeMillis() databaseFileToAddOrUpdate.hardwareKey?.value,
System.currentTimeMillis()
) )
// Update values if history element not yet in the database // Update values if history element not yet in the database
try { try {
if (fileDatabaseHistoryRetrieve == null) { if (fileDatabaseHistoryRetrieve == null) {
databaseFileHistoryDao.add(fileDatabaseHistory) databaseFileHistoryDao.add(fileDatabaseHistory)
} else { } else {
databaseFileHistoryDao.update(fileDatabaseHistory) databaseFileHistoryDao.update(fileDatabaseHistory)
}
} catch (e: Exception) {
Log.e(TAG, "Unable to add or update database history", e)
} }
} catch (e: Exception) {
val fileDatabaseInfo = FileDatabaseInfo(applicationContext, Log.e(TAG, "Unable to add or update database history", e)
fileDatabaseHistory.databaseUri)
DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtil.decode(fileDatabaseHistory.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
)
} }
},
{ val fileDatabaseInfo =
databaseFileAddedOrUpdatedResult?.invoke(it) FileDatabaseInfo(applicationContext,
fileDatabaseHistory.databaseUri)
DatabaseFile(
UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
fileDatabaseInfo.retrieveDatabaseAlias(fileDatabaseHistory.databaseAlias),
fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModificationString(),
fileDatabaseInfo.getSizeString()
)
} }
},
{
databaseFileAddedOrUpdatedResult?.invoke(it)
}
).execute() ).execute()
} }
fun deleteDatabaseFile(databaseFileToDelete: DatabaseFile, fun deleteDatabaseFile(databaseFileToDelete: com.kunzisoft.keepass.model.DatabaseFile,
databaseFileDeletedResult: (DatabaseFile?) -> Unit) { databaseFileDeletedResult: (com.kunzisoft.keepass.model.DatabaseFile?) -> Unit) {
IOActionTask( IOActionTask(
{ {
databaseFileToDelete.databaseUri?.let { databaseUri -> databaseFileToDelete.databaseUri?.let { databaseUri ->
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())?.let { fileDatabaseHistory -> databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
?.let { fileDatabaseHistory ->
val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory) val returnValue = databaseFileHistoryDao.delete(fileDatabaseHistory)
if (returnValue > 0) { if (returnValue > 0) {
DatabaseFile( DatabaseFile(
UriUtil.parse(fileDatabaseHistory.databaseUri), UriUtilDatabase.parse(fileDatabaseHistory.databaseUri),
UriUtil.parse(fileDatabaseHistory.keyFileUri), UriUtilDatabase.parse(fileDatabaseHistory.keyFileUri),
HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey), HardwareKey.getHardwareKeyFromString(fileDatabaseHistory.hardwareKey),
UriUtil.decode(fileDatabaseHistory.databaseUri), UriUtilDatabase.decode(fileDatabaseHistory.databaseUri),
databaseFileToDelete.databaseAlias databaseFileToDelete.databaseAlias
) )
} else { } else {
null null
} }
} }
}
},
{
databaseFileDeletedResult.invoke(it)
} }
},
{
databaseFileDeletedResult.invoke(it)
}
).execute() ).execute()
} }
fun deleteKeyFileByDatabaseUri(databaseUri: Uri, fun deleteKeyFileByDatabaseUri(databaseUri: Uri,
result: (() ->Unit)? = null) { result: (() ->Unit)? = null) {
IOActionTask( IOActionTask(
{ {
databaseFileHistoryDao.deleteKeyFileByDatabaseUri(databaseUri.toString()) databaseFileHistoryDao.deleteKeyFileByDatabaseUri(databaseUri.toString())
}, },
{ {
result?.invoke() result?.invoke()
} }
).execute() ).execute()
} }
fun deleteAllKeyFiles(result: (() ->Unit)? = null) { fun deleteAllKeyFiles(result: (() ->Unit)? = null) {
IOActionTask( IOActionTask(
{ {
databaseFileHistoryDao.deleteAllKeyFiles() databaseFileHistoryDao.deleteAllKeyFiles()
}, },
{ {
result?.invoke() result?.invoke()
} }
).execute() ).execute()
} }
fun deleteAll(result: (() ->Unit)? = null) { fun deleteAll(result: (() ->Unit)? = null) {
IOActionTask( IOActionTask(
{ {
databaseFileHistoryDao.deleteAll() databaseFileHistoryDao.deleteAll()
}, },
{ {
result?.invoke() result?.invoke()
} }
).execute() ).execute()
} }
companion object : SingletonHolderParameter<FileDatabaseHistoryAction, Context>(::FileDatabaseHistoryAction) { companion object : com.kunzisoft.keepass.utils.SingletonHolderParameter<FileDatabaseHistoryAction, Context>(::FileDatabaseHistoryAction) {
private val TAG = FileDatabaseHistoryAction::class.java.name private val TAG = FileDatabaseHistoryAction::class.java.name
} }
} }

View File

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

View File

@@ -21,7 +21,6 @@ package com.kunzisoft.keepass.database.action
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
@@ -54,7 +53,7 @@ open class AssignMainCredentialInDatabaseRunnable (
super.onFinishRun() super.onFinishRun()
// Erase the biometric // Erase the biometric
CipherDatabaseAction.getInstance(context) com.kunzisoft.keepass.app.database.CipherDatabaseAction.getInstance(context)
.deleteByDatabaseUri(mDatabaseUri) .deleteByDatabaseUri(mDatabaseUri)
// Erase the register keyfile // Erase the register keyfile
FileDatabaseHistoryAction.getInstance(context) 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 package com.kunzisoft.keepass.database.action
import android.app.AlertDialog
import android.app.Service import android.app.Service
import android.content.* import android.content.BroadcastReceiver
import android.content.Context.* 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.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R 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_START_TASK_ACTION
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import com.kunzisoft.keepass.viewmodels.ChallengeResponseViewModel import com.kunzisoft.keepass.viewmodels.ChallengeResponseViewModel
import java.util.ArrayList
import java.util.UUID
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.*
/** /**
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService, * Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
@@ -103,7 +89,8 @@ class DatabaseTaskProvider {
var onActionFinish: ((database: Database, var onActionFinish: ((database: Database,
actionTask: String, actionTask: String,
result: ActionRunnable.Result) -> Unit)? = null result: ActionRunnable.Result
) -> Unit)? = null
private var intentDatabaseTask: Intent private var intentDatabaseTask: Intent
@@ -177,7 +164,8 @@ class DatabaseTaskProvider {
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener { private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
override fun onStartAction(database: Database, override fun onStartAction(database: Database,
progressMessage: ProgressMessage) { progressMessage: ProgressMessage
) {
startDialog(progressMessage) startDialog(progressMessage)
} }
@@ -188,7 +176,8 @@ class DatabaseTaskProvider {
override fun onStopAction(database: Database, override fun onStopAction(database: Database,
actionTask: String, actionTask: String,
result: ActionRunnable.Result) { result: ActionRunnable.Result
) {
onActionFinish?.invoke(database, actionTask, result) onActionFinish?.invoke(database, actionTask, result)
// Remove the progress task // Remove the progress task
stopDialog() stopDialog()
@@ -310,7 +299,7 @@ class DatabaseTaskProvider {
private fun bindService() { private fun bindService() {
initServiceConnection() initServiceConnection()
serviceConnection?.let { 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)
} }
} }
@@ -763,4 +752,4 @@ class DatabaseTaskProvider {
companion object { companion object {
private val TAG = DatabaseTaskProvider::class.java.name private val TAG = DatabaseTaskProvider::class.java.name
} }
} }

View File

@@ -21,30 +21,29 @@ package com.kunzisoft.keepass.database.action
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
import com.kunzisoft.keepass.database.element.Database 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.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseInputException import com.kunzisoft.keepass.database.exception.DatabaseInputException
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.database.element.MainCredential
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.UriUtil
class LoadDatabaseRunnable(private val context: Context, class LoadDatabaseRunnable(
private val mDatabase: Database, private val context: Context,
private val mDatabaseUri: Uri, private val mDatabase: Database,
private val mMainCredential: MainCredential, private val mDatabaseUri: Uri,
private val mChallengeResponseRetriever: (hardwareKey: HardwareKey, seed: ByteArray?) -> ByteArray, private val mMainCredential: MainCredential,
private val mReadonly: Boolean, private val mChallengeResponseRetriever: (hardwareKey: HardwareKey, seed: ByteArray?) -> ByteArray,
private val mCipherEncryptDatabase: CipherEncryptDatabase?, private val mReadonly: Boolean,
private val mFixDuplicateUUID: Boolean, private val mCipherEncryptDatabase: CipherEncryptDatabase?,
private val progressTaskUpdater: ProgressTaskUpdater?, private val mFixDuplicateUUID: Boolean,
private val mLoadDatabaseResult: ((Result) -> Unit)?) private val progressTaskUpdater: ProgressTaskUpdater?,
: ActionRunnable() { private val mLoadDatabaseResult: ((Result) -> Unit)?,
) : ActionRunnable() {
override fun onStartRun() { override fun onStartRun() {
// Clear before we load // Clear before we load
@@ -59,15 +58,14 @@ class LoadDatabaseRunnable(private val context: Context,
mMainCredential, mMainCredential,
mChallengeResponseRetriever, mChallengeResponseRetriever,
mReadonly, mReadonly,
UriUtil.getBinaryDir(context), com.kunzisoft.keepass.utils.UriUtilDatabase.getBinaryDir(context),
{ memoryWanted -> { memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
}, },
mFixDuplicateUUID, mFixDuplicateUUID,
progressTaskUpdater progressTaskUpdater
) )
} } catch (e: DatabaseInputException) {
catch (e: DatabaseInputException) {
setError(e) setError(e)
} }
@@ -75,17 +73,17 @@ class LoadDatabaseRunnable(private val context: Context,
// Save keyFile in app database // Save keyFile in app database
if (PreferencesUtil.rememberDatabaseLocations(context)) { if (PreferencesUtil.rememberDatabaseLocations(context)) {
FileDatabaseHistoryAction.getInstance(context) FileDatabaseHistoryAction.getInstance(context)
.addOrUpdateDatabaseUri( .addOrUpdateDatabaseUri(
mDatabaseUri, mDatabaseUri,
if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null, if (PreferencesUtil.rememberKeyFileLocations(context)) mMainCredential.keyFileUri else null,
if (PreferencesUtil.rememberHardwareKey(context)) mMainCredential.hardwareKey else null, if (PreferencesUtil.rememberHardwareKey(context)) mMainCredential.hardwareKey else null,
) )
} }
// Register the biometric // Register the biometric
mCipherEncryptDatabase?.let { cipherDatabase -> mCipherEncryptDatabase?.let { cipherDatabase ->
CipherDatabaseAction.getInstance(context) com.kunzisoft.keepass.app.database.CipherDatabaseAction.getInstance(context)
.addOrUpdateCipherDatabase(cipherDatabase) // return value not called .addOrUpdateCipherDatabase(cipherDatabase) // return value not called
} }
// Register the current time to init the lock timer // Register the current time to init the lock timer

View File

@@ -13,7 +13,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.UriUtil
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class HardwareKeyResponseHelper { class HardwareKeyResponseHelper {
@@ -134,11 +133,11 @@ class HardwareKeyResponseHelper {
activity.getString(R.string.error_driver_required, hardwareKey.toString()) activity.getString(R.string.error_driver_required, hardwareKey.toString())
) )
.setPositiveButton(R.string.download) { _, _ -> .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) { _, _ -> } .setNegativeButton(android.R.string.cancel) { _, _ -> }
builder.create().show() 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 package com.kunzisoft.keepass.icons
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import com.kunzisoft.keepass.BuildConfig import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import java.util.* import java.util.ArrayList
/** /**
* Utility class to built and select an IconPack dynamically by libraries importation * Utility class to built and select an IconPack dynamically by libraries importation
* *
* @author J-Jamet * @author J-Jamet
*/ */
object IconPackChooser { object IconPackChooser : InterfaceIconPackChooser {
private val TAG = IconPackChooser::class.java.name private val TAG = IconPackChooser::class.java.name
@@ -50,7 +31,7 @@ object IconPackChooser {
* @param context Context to construct each pack with the resources * @param context Context to construct each pack with the resources
* @return An unique instance of [IconPackChooser], recall [.build] provide the same instance * @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) { synchronized(IconPackChooser::class.java) {
if (!isIconPackChooserBuilt) { if (!isIconPackChooserBuilt) {
isIconPackChooserBuilt = true isIconPackChooserBuilt = true
@@ -69,7 +50,7 @@ object IconPackChooser {
/** /**
* Construct dynamically the icon pack provide by the default string resource "resource_id" * 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) val resourceId = context.resources.getIdentifier("resource_id", "string", context.packageName)
iconPackList.add(IconPack(context.packageName, context.resources, resourceId)) 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 * 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 { try {
iconPackList.add(IconPack(context.packageName, context.resources, context.resources.getIdentifier( iconPackList.add(IconPack(context.packageName,
context.resources,
context.resources.getIdentifier(
iconPackString + "_resource_id", iconPackString + "_resource_id",
"string", "string",
context.packageName))) context.packageName)))
@@ -89,7 +72,7 @@ object IconPackChooser {
} }
fun setSelectedIconPack(iconPackIdString: String?) { override fun setSelectedIconPack(iconPackIdString: String?) {
for (iconPack in iconPackList) { for (iconPack in iconPackList) {
if (iconPack.id == iconPackIdString) { if (iconPack.id == iconPackIdString) {
iconPackSelected = iconPack iconPackSelected = iconPack
@@ -104,10 +87,10 @@ object IconPackChooser {
* @param context Context to build the icon pack if not already build * @param context Context to build the icon pack if not already build
* @return IconPack currently in usage * @return IconPack currently in usage
*/ */
fun getSelectedIconPack(context: Context): IconPack? { override fun getSelectedIconPack(context: Context): IconPack? {
build(context) build(context)
if (iconPackSelected == null) { if (iconPackSelected == null) {
setSelectedIconPack(PreferencesUtil.getIconPackSelectedId(context)) setSelectedIconPack(DatabasePreferencesUtil.getIconPackSelectedId(context))
} }
return iconPackSelected return iconPackSelected
} }
@@ -118,7 +101,7 @@ object IconPackChooser {
* @param context Context to build the icon pack if not already build * @param context Context to build the icon pack if not already build
* @return IconPack available * @return IconPack available
*/ */
fun getIconPackList(context: Context): List<IconPack> { override fun getIconPackList(context: Context): List<IconPack> {
build(context) build(context)
return iconPackList 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 package com.kunzisoft.keepass.model
import android.content.Context import android.content.Context
@@ -24,9 +5,8 @@ import android.net.Uri
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import android.text.format.Formatter import android.text.format.Formatter
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.Date
/** /**
* Utility data class to get FileDatabaseInfo at a `t` time * Utility data class to get FileDatabaseInfo at a `t` time
@@ -90,12 +70,12 @@ data class SnapFileDatabaseInfo(var fileUri: Uri?,
return arrayOfNulls(size) return arrayOfNulls(size)
} }
fun fromFileDatabaseInfo(fileDatabaseInfo: FileDatabaseInfo): SnapFileDatabaseInfo { fun fromFileDatabaseInfo(fileDatabaseInfo: com.kunzisoft.keepass.viewmodels.FileDatabaseInfo): SnapFileDatabaseInfo {
return SnapFileDatabaseInfo( return SnapFileDatabaseInfo(
fileDatabaseInfo.fileUri, fileDatabaseInfo.fileUri,
fileDatabaseInfo.exists, fileDatabaseInfo.exists,
fileDatabaseInfo.getLastModification(), fileDatabaseInfo.getLastModification(),
fileDatabaseInfo.getSize()) fileDatabaseInfo.getSize())
} }
} }
} }

View File

@@ -2,15 +2,16 @@ package com.kunzisoft.keepass.services
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.PendingIntent 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.net.Uri
import android.os.Binder import android.os.Binder
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
class AdvancedUnlockNotificationService : NotificationService() { class AdvancedUnlockNotificationService : NotificationService() {
@@ -44,7 +45,7 @@ class AdvancedUnlockNotificationService : NotificationService() {
} }
override fun retrieveChannelName(): String { override fun retrieveChannelName(): String {
return getString(R.string.advanced_unlock) return getString(com.kunzisoft.keepass.R.string.advanced_unlock)
} }
override fun onCreate() { override fun onCreate() {
@@ -69,12 +70,12 @@ class AdvancedUnlockNotificationService : NotificationService() {
val biometricUnlockEnabled = PreferencesUtil.isBiometricUnlockEnable(this) val biometricUnlockEnabled = PreferencesUtil.isBiometricUnlockEnable(this)
val notificationBuilder = buildNewNotification().apply { val notificationBuilder = buildNewNotification().apply {
setSmallIcon(if (biometricUnlockEnabled) { setSmallIcon(if (biometricUnlockEnabled) {
R.drawable.notification_ic_fingerprint_unlock_24dp com.kunzisoft.keepass.R.drawable.notification_ic_fingerprint_unlock_24dp
} else { } else {
R.drawable.notification_ic_device_unlock_24dp com.kunzisoft.keepass.R.drawable.notification_ic_device_unlock_24dp
}) })
setContentTitle(getString(R.string.advanced_unlock)) setContentTitle(getString(com.kunzisoft.keepass.R.string.advanced_unlock))
setContentText(getString(R.string.advanced_unlock_tap_delete)) setContentText(getString(com.kunzisoft.keepass.R.string.advanced_unlock_tap_delete))
setContentIntent(pendingDeleteIntent) setContentIntent(pendingDeleteIntent)
// Unfortunately swipe is disabled in lollipop+ // Unfortunately swipe is disabled in lollipop+
setDeleteIntent(pendingDeleteIntent) setDeleteIntent(pendingDeleteIntent)
@@ -82,7 +83,7 @@ class AdvancedUnlockNotificationService : NotificationService() {
val notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this) val notificationTimeoutMilliSecs = PreferencesUtil.getAdvancedUnlockTimeout(this)
// Not necessarily a foreground service // Not necessarily a foreground service
if (mTimerJob == null && notificationTimeoutMilliSecs != TimeoutHelper.NEVER) { if (mTimerJob == null && notificationTimeoutMilliSecs != com.kunzisoft.keepass.timeout.TimeoutHelper.NEVER) {
defineTimerJob(notificationBuilder, notificationTimeoutMilliSecs) { defineTimerJob(notificationBuilder, notificationTimeoutMilliSecs) {
sendBroadcast(Intent(REMOVE_ADVANCED_UNLOCK_KEY_ACTION)) sendBroadcast(Intent(REMOVE_ADVANCED_UNLOCK_KEY_ACTION))
} }
@@ -122,7 +123,7 @@ class AdvancedUnlockNotificationService : NotificationService() {
// Only one service connection // Only one service connection
fun bindService(context: Context, serviceConnection: ServiceConnection, flags: Int) { fun bindService(context: Context, serviceConnection: ServiceConnection, flags: Int) {
context.bindService(Intent(context, context.bindService(Intent(context,
AdvancedUnlockNotificationService::class.java), AdvancedUnlockNotificationService::class.java),
serviceConnection, serviceConnection,
flags) flags)
} }
@@ -131,4 +132,4 @@ class AdvancedUnlockNotificationService : NotificationService() {
context.unbindService(serviceConnection) context.unbindService(serviceConnection)
} }
} }
} }

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.NodeId
import com.kunzisoft.keepass.database.element.node.Type import com.kunzisoft.keepass.database.element.node.Type
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.model.CipherEncryptDatabase import com.kunzisoft.keepass.model.CipherEncryptDatabase
import com.kunzisoft.keepass.model.ProgressMessage import com.kunzisoft.keepass.model.ProgressMessage
import com.kunzisoft.keepass.model.SnapFileDatabaseInfo 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.LOCK_ACTION
import com.kunzisoft.keepass.utils.closeDatabase import com.kunzisoft.keepass.utils.closeDatabase
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
import java.util.UUID
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import java.util.*
open class DatabaseTaskNotificationService : LockNotificationService(), ProgressTaskUpdater { open class DatabaseTaskNotificationService : LockNotificationService(), ProgressTaskUpdater {
@@ -75,6 +76,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private var mDatabaseInfoListeners = mutableListOf<DatabaseInfoListener>() private var mDatabaseInfoListeners = mutableListOf<DatabaseInfoListener>()
private var mActionTaskBinder = ActionTaskBinder() private var mActionTaskBinder = ActionTaskBinder()
private var mActionTaskListeners = mutableListOf<ActionTaskListener>() private var mActionTaskListeners = mutableListOf<ActionTaskListener>()
// Channel to connect asynchronously a listener or a response // Channel to connect asynchronously a listener or a response
private var mRequestChallengeListenerChannel: Channel<RequestChallengeListener>? = null private var mRequestChallengeListenerChannel: Channel<RequestChallengeListener>? = null
private var mResponseChallengeChannel: Channel<ByteArray?>? = null private var mResponseChallengeChannel: Channel<ByteArray?>? = null
@@ -93,7 +95,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
return getString(R.string.database) return getString(R.string.database)
} }
inner class ActionTaskBinder: Binder() { inner class ActionTaskBinder : Binder() {
fun getService(): DatabaseTaskNotificationService = this@DatabaseTaskNotificationService fun getService(): DatabaseTaskNotificationService = this@DatabaseTaskNotificationService
@@ -154,18 +156,28 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
interface DatabaseInfoListener { interface DatabaseInfoListener {
fun onDatabaseInfoChanged(previousDatabaseInfo: SnapFileDatabaseInfo, fun onDatabaseInfoChanged(
newDatabaseInfo: SnapFileDatabaseInfo) previousDatabaseInfo: SnapFileDatabaseInfo,
newDatabaseInfo: SnapFileDatabaseInfo,
)
} }
interface ActionTaskListener { interface ActionTaskListener {
fun onStartAction(database: Database, fun onStartAction(
progressMessage: ProgressMessage) database: Database,
fun onUpdateAction(database: Database, progressMessage: ProgressMessage,
progressMessage: ProgressMessage) )
fun onStopAction(database: Database,
actionTask: String, fun onUpdateAction(
result: ActionRunnable.Result) database: Database,
progressMessage: ProgressMessage,
)
fun onStopAction(
database: Database,
actionTask: String,
result: ActionRunnable.Result,
)
} }
interface RequestChallengeListener { interface RequestChallengeListener {
@@ -183,7 +195,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
mDatabase?.fileUri?.let { mDatabase?.fileUri?.let {
val previousDatabaseInfo = mSnapFileDatabaseInfo val previousDatabaseInfo = mSnapFileDatabaseInfo
val lastFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo( val lastFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it)) FileDatabaseInfo(applicationContext, it))
val oldDatabaseModification = previousDatabaseInfo?.lastModification val oldDatabaseModification = previousDatabaseInfo?.lastModification
val newDatabaseModification = lastFileDatabaseInfo.lastModification val newDatabaseModification = lastFileDatabaseInfo.lastModification
@@ -194,12 +206,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// To prevent dialog opening too often // To prevent dialog opening too often
// Add 10 seconds delta time to prevent spamming // Add 10 seconds delta time to prevent spamming
val conditionLastModification = val conditionLastModification =
(oldDatabaseModification != null && newDatabaseModification != null (oldDatabaseModification != null && newDatabaseModification != null
&& oldDatabaseSize != null && oldDatabaseSize != null
&& oldDatabaseModification > 0 && newDatabaseModification > 0 && oldDatabaseModification > 0 && newDatabaseModification > 0
&& oldDatabaseSize > 0 && oldDatabaseSize > 0
&& oldDatabaseModification < newDatabaseModification && oldDatabaseModification < newDatabaseModification
&& mLastLocalSaveTime + 10000 < newDatabaseModification) && mLastLocalSaveTime + 10000 < newDatabaseModification)
if (conditionExists || conditionLastModification) { if (conditionExists || conditionLastModification) {
// Show the dialog only if it's real new info and not a delay after a save // Show the dialog only if it's real new info and not a delay after a save
@@ -208,7 +220,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Call listener to indicate a change in database info // Call listener to indicate a change in database info
if (!mSaveState && previousDatabaseInfo != null) { if (!mSaveState && previousDatabaseInfo != null) {
mDatabaseInfoListeners.forEach { listener -> mDatabaseInfoListeners.forEach { listener ->
listener.onDatabaseInfoChanged(previousDatabaseInfo, lastFileDatabaseInfo) listener.onDatabaseInfoChanged(previousDatabaseInfo,
lastFileDatabaseInfo)
} }
} }
mSnapFileDatabaseInfo = lastFileDatabaseInfo mSnapFileDatabaseInfo = lastFileDatabaseInfo
@@ -223,7 +236,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
try { try {
mDatabase?.fileUri?.let { mDatabase?.fileUri?.let {
mSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo( mSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it)) FileDatabaseInfo(applicationContext, it))
Log.i(TAG, "Database file saved $mSnapFileDatabaseInfo") Log.i(TAG, "Database file saved $mSnapFileDatabaseInfo")
} }
} catch (e: Exception) { } catch (e: Exception) {
@@ -294,7 +307,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
val database = Database.getInstance() val database = Database.getInstance(IconPackChooser)
if (mDatabase != database) { if (mDatabase != database) {
mDatabase = database mDatabase = database
mDatabaseListeners.forEach { listener -> mDatabaseListeners.forEach { listener ->
@@ -323,23 +336,36 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
stopSelf() stopSelf()
} }
val actionRunnable: ActionRunnable? = when (intentAction) { val actionRunnable: ActionRunnable? = when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent, database) ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent, database)
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent, database) ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent, database)
ACTION_DATABASE_MERGE_TASK -> buildDatabaseMergeActionTask(intent, database) ACTION_DATABASE_MERGE_TASK -> buildDatabaseMergeActionTask(intent, database)
ACTION_DATABASE_RELOAD_TASK -> buildDatabaseReloadActionTask(database) ACTION_DATABASE_RELOAD_TASK -> buildDatabaseReloadActionTask(database)
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent, database) ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent,
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent, database) database)
ACTION_DATABASE_UPDATE_GROUP_TASK -> buildDatabaseUpdateGroupActionTask(intent, database) ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent,
ACTION_DATABASE_CREATE_ENTRY_TASK -> buildDatabaseCreateEntryActionTask(intent, database) database)
ACTION_DATABASE_UPDATE_ENTRY_TASK -> buildDatabaseUpdateEntryActionTask(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_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent, database)
ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent, database) ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent, database)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent, database) ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent,
ACTION_DATABASE_RESTORE_ENTRY_HISTORY -> buildDatabaseRestoreEntryHistoryActionTask(intent, database) database)
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> buildDatabaseDeleteEntryHistoryActionTask(intent, database) ACTION_DATABASE_RESTORE_ENTRY_HISTORY -> buildDatabaseRestoreEntryHistoryActionTask(
ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> buildDatabaseUpdateCompressionActionTask(intent, database) intent,
ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK -> buildDatabaseRemoveUnlinkedDataActionTask(intent, database) 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_NAME_TASK,
ACTION_DATABASE_UPDATE_DESCRIPTION_TASK, ACTION_DATABASE_UPDATE_DESCRIPTION_TASK,
ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_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_KEY_DERIVATION_TASK,
ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK, ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK,
ACTION_DATABASE_UPDATE_PARALLELISM_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) ACTION_DATABASE_SAVE -> buildDatabaseSave(intent, database)
else -> null else -> null
} }
@@ -361,79 +388,81 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
if (actionRunnable != null) { if (actionRunnable != null) {
mainScope.launch { mainScope.launch {
executeAction(this@DatabaseTaskNotificationService, executeAction(this@DatabaseTaskNotificationService,
{ {
TimeoutHelper.temporarilyDisableTimeout() TimeoutHelper.temporarilyDisableTimeout()
mActionRunning = true mActionRunning = true
sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply { sendBroadcast(Intent(DATABASE_START_TASK_ACTION).apply {
putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId) putExtra(DATABASE_TASK_TITLE_KEY, mProgressMessage.titleId)
putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId) putExtra(DATABASE_TASK_MESSAGE_KEY, mProgressMessage.messageId)
putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId) putExtra(DATABASE_TASK_WARNING_KEY, mProgressMessage.warningId)
}) })
mActionTaskListeners.forEach { actionTaskListener -> mActionTaskListeners.forEach { actionTaskListener ->
actionTaskListener.onStartAction( actionTaskListener.onStartAction(
database, mProgressMessage database, mProgressMessage
) )
}
},
{
actionRunnable
},
{ result ->
try {
mActionTaskListeners.forEach { actionTaskListener ->
mTaskRemovedRequested = false
actionTaskListener.onStopAction(database, intentAction!!, result)
}
} finally {
// Save the database info before performing action
when (intentAction) {
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK -> {
saveDatabaseInfo()
}
}
val save = !database.isReadOnly
&& (intentAction == ACTION_DATABASE_SAVE
|| intent?.getBooleanExtra(SAVE_DATABASE_KEY, false) == true)
// Save the database info after performing save action
if (save) {
database.fileUri?.let {
val newSnapFileDatabaseInfo = SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it))
mLastLocalSaveTime = System.currentTimeMillis()
mSnapFileDatabaseInfo = newSnapFileDatabaseInfo
}
}
removeIntentData(intent)
TimeoutHelper.releaseTemporarilyDisableTimeout()
// Stop service after save if user remove task
if (save && mTaskRemovedRequested) {
actionOnLock()
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
if (!database.loaded) {
stopSelf()
} else {
// Restart the service to open lock notification
try {
startService(Intent(applicationContext,
DatabaseTaskNotificationService::class.java))
} catch (e: IllegalStateException) {
Log.w(TAG, "Cannot restart the database task service", e)
}
}
}
mTaskRemovedRequested = false
}
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
mActionRunning = false
} }
},
{
actionRunnable
},
{ result ->
try {
mActionTaskListeners.forEach { actionTaskListener ->
mTaskRemovedRequested = false
actionTaskListener.onStopAction(database, intentAction!!, result)
}
} finally {
// Save the database info before performing action
when (intentAction) {
ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK,
-> {
saveDatabaseInfo()
}
}
val save = !database.isReadOnly
&& (intentAction == ACTION_DATABASE_SAVE
|| intent?.getBooleanExtra(SAVE_DATABASE_KEY, false) == true)
// Save the database info after performing save action
if (save) {
database.fileUri?.let {
val newSnapFileDatabaseInfo =
SnapFileDatabaseInfo.fromFileDatabaseInfo(
FileDatabaseInfo(applicationContext, it))
mLastLocalSaveTime = System.currentTimeMillis()
mSnapFileDatabaseInfo = newSnapFileDatabaseInfo
}
}
removeIntentData(intent)
TimeoutHelper.releaseTemporarilyDisableTimeout()
// Stop service after save if user remove task
if (save && mTaskRemovedRequested) {
actionOnLock()
} else if (TimeoutHelper.checkTimeAndLockIfTimeout(this@DatabaseTaskNotificationService)) {
if (!database.loaded) {
stopSelf()
} else {
// Restart the service to open lock notification
try {
startService(Intent(applicationContext,
DatabaseTaskNotificationService::class.java))
} catch (e: IllegalStateException) {
Log.w(TAG, "Cannot restart the database task service", e)
}
}
}
mTaskRemovedRequested = false
}
sendBroadcast(Intent(DATABASE_STOP_TASK_ACTION))
mActionRunning = false
}
) )
} }
} }
@@ -442,7 +471,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_LOAD_TASK, ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_TASK, ACTION_DATABASE_MERGE_TASK,
ACTION_DATABASE_RELOAD_TASK, ACTION_DATABASE_RELOAD_TASK,
null -> { null,
-> {
START_STICKY START_STICKY
} }
else -> { else -> {
@@ -470,9 +500,11 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
ACTION_DATABASE_LOAD_TASK, ACTION_DATABASE_LOAD_TASK,
ACTION_DATABASE_MERGE_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_ASSIGN_PASSWORD_TASK,
ACTION_DATABASE_SAVE -> R.string.saving_database ACTION_DATABASE_SAVE,
-> R.string.saving_database
else -> { else -> {
if (mSaveState) if (mSaveState)
R.string.saving_database R.string.saving_database
@@ -486,12 +518,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
// Warning if data is saved // Warning if data is saved
mProgressMessage.warningId = mProgressMessage.warningId =
if (mSaveState) if (mSaveState)
R.string.do_not_kill_app R.string.do_not_kill_app
else else
null null
val notificationBuilder = buildNewNotification().apply { val notificationBuilder = buildNewNotification().apply {
setSmallIcon(iconId) setSmallIcon(iconId)
intent?.let { intent?.let {
setContentTitle(getString( setContentTitle(getString(
@@ -582,11 +614,13 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
/** /**
* Execute action with a coroutine * Execute action with a coroutine
*/ */
private suspend fun executeAction(progressTaskUpdater: ProgressTaskUpdater, private suspend fun executeAction(
onPreExecute: () -> Unit, progressTaskUpdater: ProgressTaskUpdater,
onExecute: (ProgressTaskUpdater?) -> ActionRunnable?, onPreExecute: () -> Unit,
onPostExecute: (result: ActionRunnable.Result) -> Unit) { onExecute: (ProgressTaskUpdater?) -> ActionRunnable?,
onPostExecute: (result: ActionRunnable.Result) -> Unit,
) {
onPreExecute.invoke() onPreExecute.invoke()
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
onExecute.invoke(progressTaskUpdater)?.apply { onExecute.invoke(progressTaskUpdater)?.apply {
@@ -634,8 +668,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
super.onTaskRemoved(rootIntent) super.onTaskRemoved(rootIntent)
} }
private fun retrieveResponseFromChallenge(hardwareKey: HardwareKey, private fun retrieveResponseFromChallenge(
seed: ByteArray?): ByteArray { hardwareKey: HardwareKey,
seed: ByteArray?,
): ByteArray {
// Request a challenge - response // Request a challenge - response
var response: ByteArray var response: ByteArray
runBlocking { runBlocking {
@@ -670,10 +706,11 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private fun buildDatabaseCreateActionTask(intent: Intent, database: Database): ActionRunnable? { private fun buildDatabaseCreateActionTask(intent: Intent, database: Database): ActionRunnable? {
if (intent.hasExtra(DATABASE_URI_KEY) if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MAIN_CREDENTIAL_KEY) && intent.hasExtra(MAIN_CREDENTIAL_KEY)
) { ) {
val databaseUri: Uri? = intent.getParcelableExtra(DATABASE_URI_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) if (databaseUri == null)
return null return null
@@ -702,18 +739,20 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private fun buildDatabaseLoadActionTask(intent: Intent, database: Database): ActionRunnable? { private fun buildDatabaseLoadActionTask(intent: Intent, database: Database): ActionRunnable? {
if (intent.hasExtra(DATABASE_URI_KEY) if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MAIN_CREDENTIAL_KEY) && intent.hasExtra(MAIN_CREDENTIAL_KEY)
&& intent.hasExtra(READ_ONLY_KEY) && intent.hasExtra(READ_ONLY_KEY)
&& intent.hasExtra(CIPHER_DATABASE_KEY) && intent.hasExtra(CIPHER_DATABASE_KEY)
&& intent.hasExtra(FIX_DUPLICATE_UUID_KEY) && intent.hasExtra(FIX_DUPLICATE_UUID_KEY)
) { ) {
val databaseUri: Uri? = intent.getParcelableExtra(DATABASE_URI_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 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) if (databaseUri == null)
return null return null
return LoadDatabaseRunnable( return LoadDatabaseRunnable(
this, this,
@@ -768,18 +807,21 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private fun buildDatabaseReloadActionTask(database: Database): ActionRunnable { private fun buildDatabaseReloadActionTask(database: Database): ActionRunnable {
return ReloadDatabaseRunnable( return ReloadDatabaseRunnable(
this, this,
database, database,
this this
) { result -> ) { result ->
// No need to add each info to reload database // No need to add each info to reload database
result.data = Bundle() result.data = Bundle()
} }
} }
private fun buildDatabaseAssignPasswordActionTask(intent: Intent, database: Database): ActionRunnable? { private fun buildDatabaseAssignPasswordActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(DATABASE_URI_KEY) return if (intent.hasExtra(DATABASE_URI_KEY)
&& intent.hasExtra(MAIN_CREDENTIAL_KEY) && intent.hasExtra(MAIN_CREDENTIAL_KEY)
) { ) {
val databaseUri: Uri = intent.getParcelableExtra(DATABASE_URI_KEY) ?: return null val databaseUri: Uri = intent.getParcelableExtra(DATABASE_URI_KEY) ?: return null
AssignMainCredentialInDatabaseRunnable(this, AssignMainCredentialInDatabaseRunnable(this,
@@ -795,8 +837,10 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
private inner class AfterActionNodesRunnable : AfterActionNodesFinish() { private inner class AfterActionNodesRunnable : AfterActionNodesFinish() {
override fun onActionNodesFinish(result: ActionRunnable.Result, override fun onActionNodesFinish(
actionNodesValues: ActionNodesValues) { result: ActionRunnable.Result,
actionNodesValues: ActionNodesValues,
) {
val bundle = result.data ?: Bundle() val bundle = result.data ?: Bundle()
bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodesValues.oldNodes)) bundle.putBundle(OLD_NODES_KEY, getBundleFromListNodes(actionNodesValues.oldNodes))
bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodesValues.newNodes)) bundle.putBundle(NEW_NODES_KEY, getBundleFromListNodes(actionNodesValues.newNodes))
@@ -804,16 +848,20 @@ 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) return if (intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(PARENT_ID_KEY) && intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val parentId: NodeId<*>? = intent.getParcelableExtra(PARENT_ID_KEY) val parentId: NodeId<*>? = intent.getParcelableExtra(PARENT_ID_KEY)
val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY) val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY)
if (parentId == null if (parentId == null
|| newGroup == null) || newGroup == null
)
return null return null
database.getGroupById(parentId)?.let { parent -> database.getGroupById(parentId)?.let { parent ->
@@ -832,16 +880,20 @@ 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) return if (intent.hasExtra(GROUP_ID_KEY)
&& intent.hasExtra(GROUP_KEY) && intent.hasExtra(GROUP_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val groupId: NodeId<*>? = intent.getParcelableExtra(GROUP_ID_KEY) val groupId: NodeId<*>? = intent.getParcelableExtra(GROUP_ID_KEY)
val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY) val newGroup: Group? = intent.getParcelableExtra(GROUP_KEY)
if (groupId == null if (groupId == null
|| newGroup == null) || newGroup == null
)
return null return null
database.getGroupById(groupId)?.let { oldGroup -> database.getGroupById(groupId)?.let { oldGroup ->
@@ -860,16 +912,20 @@ 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) return if (intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(PARENT_ID_KEY) && intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val parentId: NodeId<*>? = intent.getParcelableExtra(PARENT_ID_KEY) val parentId: NodeId<*>? = intent.getParcelableExtra(PARENT_ID_KEY)
val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY) val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY)
if (parentId == null if (parentId == null
|| newEntry == null) || newEntry == null
)
return null return null
database.getGroupById(parentId)?.let { parent -> database.getGroupById(parentId)?.let { parent ->
@@ -888,16 +944,20 @@ 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) return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_KEY) && intent.hasExtra(ENTRY_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val entryId: NodeId<UUID>? = intent.getParcelableExtra(ENTRY_ID_KEY) val entryId: NodeId<UUID>? = intent.getParcelableExtra(ENTRY_ID_KEY)
val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY) val newEntry: Entry? = intent.getParcelableExtra(ENTRY_KEY)
if (entryId == null if (entryId == null
|| newEntry == null) || newEntry == null
)
return null return null
database.getEntryById(entryId)?.let { oldEntry -> database.getEntryById(entryId)?.let { oldEntry ->
@@ -916,11 +976,14 @@ 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) return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY) && intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY) && intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val parentId: NodeId<*> = intent.getParcelableExtra(PARENT_ID_KEY) ?: return null val parentId: NodeId<*> = intent.getParcelableExtra(PARENT_ID_KEY) ?: return null
@@ -940,11 +1003,14 @@ 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) return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY) && intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(PARENT_ID_KEY) && intent.hasExtra(PARENT_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val parentId: NodeId<*> = intent.getParcelableExtra(PARENT_ID_KEY) ?: return null val parentId: NodeId<*> = intent.getParcelableExtra(PARENT_ID_KEY) ?: return null
@@ -964,28 +1030,34 @@ 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) return if (intent.hasExtra(GROUPS_ID_KEY)
&& intent.hasExtra(ENTRIES_ID_KEY) && intent.hasExtra(ENTRIES_ID_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
DeleteNodesRunnable(this, DeleteNodesRunnable(this,
database, database,
getListNodesFromBundle(database, intent.extras!!), getListNodesFromBundle(database, intent.extras!!),
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false), !database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
AfterActionNodesRunnable() AfterActionNodesRunnable()
) { hardwareKey, seed -> ) { hardwareKey, seed ->
retrieveResponseFromChallenge(hardwareKey, seed) retrieveResponseFromChallenge(hardwareKey, seed)
} }
} else { } else {
null null
} }
} }
private fun buildDatabaseRestoreEntryHistoryActionTask(intent: Intent, database: Database): ActionRunnable? { private fun buildDatabaseRestoreEntryHistoryActionTask(
intent: Intent,
database: Database,
): ActionRunnable? {
return if (intent.hasExtra(ENTRY_ID_KEY) return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_HISTORY_POSITION_KEY) && intent.hasExtra(ENTRY_HISTORY_POSITION_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val entryId: NodeId<UUID> = intent.getParcelableExtra(ENTRY_ID_KEY) ?: return null val entryId: NodeId<UUID> = intent.getParcelableExtra(ENTRY_ID_KEY) ?: return null
@@ -1004,10 +1076,13 @@ 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) return if (intent.hasExtra(ENTRY_ID_KEY)
&& intent.hasExtra(ENTRY_HISTORY_POSITION_KEY) && intent.hasExtra(ENTRY_HISTORY_POSITION_KEY)
&& intent.hasExtra(SAVE_DATABASE_KEY) && intent.hasExtra(SAVE_DATABASE_KEY)
) { ) {
val entryId: NodeId<UUID> = intent.getParcelableExtra(ENTRY_ID_KEY) ?: return null val entryId: NodeId<UUID> = intent.getParcelableExtra(ENTRY_ID_KEY) ?: return null
@@ -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) return if (intent.hasExtra(OLD_ELEMENT_KEY)
&& intent.hasExtra(NEW_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 oldElement: CompressionAlgorithm? = intent.getParcelableExtra(OLD_ELEMENT_KEY)
val newElement: CompressionAlgorithm? = intent.getParcelableExtra(NEW_ELEMENT_KEY) val newElement: CompressionAlgorithm? = intent.getParcelableExtra(NEW_ELEMENT_KEY)
if (oldElement == null if (oldElement == null
|| newElement == null) || newElement == null
)
return null return null
return UpdateCompressionBinariesDatabaseRunnable(this, 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 if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return RemoveUnlinkedDataDatabaseRunnable(this, 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 if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return SaveDatabaseRunnable(this, return SaveDatabaseRunnable(this,
database, database,
@@ -1120,7 +1206,8 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
private val TAG = DatabaseTaskNotificationService::class.java.name 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_CREATE_TASK = "ACTION_DATABASE_CREATE_TASK"
const val ACTION_DATABASE_LOAD_TASK = "ACTION_DATABASE_LOAD_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_RESTORE_ENTRY_HISTORY = "ACTION_DATABASE_RESTORE_ENTRY_HISTORY"
const val ACTION_DATABASE_DELETE_ENTRY_HISTORY = "ACTION_DATABASE_DELETE_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_NAME_TASK = "ACTION_DATABASE_UPDATE_NAME_TASK"
const val ACTION_DATABASE_UPDATE_DESCRIPTION_TASK = "ACTION_DATABASE_UPDATE_DESCRIPTION_TASK" const val ACTION_DATABASE_UPDATE_DESCRIPTION_TASK =
const val ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK = "ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_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_COLOR_TASK = "ACTION_DATABASE_UPDATE_COLOR_TASK"
const val ACTION_DATABASE_UPDATE_COMPRESSION_TASK = "ACTION_DATABASE_UPDATE_COMPRESSION_TASK" const val ACTION_DATABASE_UPDATE_COMPRESSION_TASK =
const val ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK = "ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK" "ACTION_DATABASE_UPDATE_COMPRESSION_TASK"
const val ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK = "ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK" const val ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK =
const val ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK = "ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK" "ACTION_DATABASE_REMOVE_UNLINKED_DATA_TASK"
const val ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK" const val ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK =
const val ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_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_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_KEY_DERIVATION_TASK =
const val ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK = "ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK" "ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK"
const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK = "ACTION_DATABASE_UPDATE_PARALLELISM_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_UPDATE_ITERATIONS_TASK = "ACTION_DATABASE_UPDATE_ITERATIONS_TASK"
const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE" 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 SAVE_DATABASE_KEY = "SAVE_DATABASE_KEY"
const val OLD_NODES_KEY = "OLD_NODES_KEY" const val OLD_NODES_KEY = "OLD_NODES_KEY"
const val NEW_NODES_KEY = "NEW_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 OLD_ELEMENT_KEY =
const val NEW_ELEMENT_KEY = "NEW_ELEMENT_KEY" // Warning type of this thing change every time "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> { fun getListNodesFromBundle(database: Database, bundle: Bundle): List<Node> {
val nodesAction = ArrayList<Node>() val nodesAction = ArrayList<Node>()
@@ -1211,4 +1311,4 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
} }
} }
} }

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 package com.kunzisoft.keepass.settings
import android.app.backup.BackupManager import android.app.backup.BackupManager
@@ -26,18 +7,18 @@ import android.content.res.Resources
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R 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.element.SortNodeEnum
import com.kunzisoft.keepass.database.search.SearchParameters import com.kunzisoft.keepass.database.search.SearchParameters
import com.kunzisoft.keepass.education.Education import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.magikeyboard.MagikeyboardService import java.util.HashSet
import com.kunzisoft.keepass.password.PassphraseGenerator import java.util.Properties
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.APP_TIMEOUT_KEY
import com.kunzisoft.keepass.utils.UriUtil import com.kunzisoft.keepass.settings.DatabasePreferencesUtil.HIDE_EXPIRED_ENTRIES_KEY
import java.util.* 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 { object PreferencesUtil {
@@ -61,7 +42,8 @@ object PreferencesUtil {
fun saveNodeSort(context: Context, fun saveNodeSort(context: Context,
sortNodeEnum: SortNodeEnum, sortNodeEnum: SortNodeEnum,
sortNodeParameters: SortNodeEnum.SortNodeParameters) { sortNodeParameters: SortNodeEnum.SortNodeParameters
) {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
prefs?.edit()?.apply { prefs?.edit()?.apply {
putString(context.getString(R.string.sort_node_key), sortNodeEnum.name) 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)) 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 { fun showEntryColors(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.show_entry_colors_key), 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)) 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 { 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) val styleString = PreferenceManager.getDefaultSharedPreferences(context)
.getString(context.getString(R.string.setting_style_key), defaultStyleString) .getString(context.getString(R.string.setting_style_key), defaultStyleString)
?: defaultStyleString ?: defaultStyleString
// Return the system style // 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) { fun setStyle(context: Context, styleString: String) {
var tempThemeString = styleString var tempThemeString = styleString
if (!UriUtil.contributingUser(context)) { if (!com.kunzisoft.keepass.utils.UriUtil.contributingUser(context)) {
if (tempThemeString in BuildConfig.STYLES_DISABLED) { if (tempThemeString in com.kunzisoft.keepass.BuildConfig.STYLES_DISABLED) {
tempThemeString = Stylish.defaultStyle(context) tempThemeString =
com.kunzisoft.keepass.activities.stylish.Stylish.defaultStyle(context)
} }
} }
// Store light style to show selection in array list // 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) PreferenceManager.getDefaultSharedPreferences(context)
.edit() .edit()
.putString(context.getString(R.string.setting_style_key), tempThemeString) .putString(context.getString(R.string.setting_style_key), tempThemeString)
.apply() .apply()
Stylish.load(context) com.kunzisoft.keepass.activities.stylish.Stylish.load(context)
} }
fun getStyleBrightness(context: Context): String? { 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) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return PassphraseGenerator.WordCase return com.kunzisoft.keepass.password.PassphraseGenerator.WordCase.Companion.getByOrdinal(
.getByOrdinal(prefs.getInt(context prefs.getInt(context
.getString(R.string.passphrase_generator_word_case_key), .getString(R.string.passphrase_generator_word_case_key),
0) 0)
) )
} }
fun setDefaultPassphraseWordCase(context: Context, wordCase: PassphraseGenerator.WordCase) { fun setDefaultPassphraseWordCase(context: Context, wordCase: com.kunzisoft.keepass.password.PassphraseGenerator.WordCase) {
PreferenceManager.getDefaultSharedPreferences(context).edit().apply { PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
putInt( putInt(
context.getString(R.string.passphrase_generator_word_case_key), context.getString(R.string.passphrase_generator_word_case_key),
@@ -414,45 +390,23 @@ object PreferencesUtil {
*/ */
fun saveCurrentTime(context: Context) { fun saveCurrentTime(context: Context) {
PreferenceManager.getDefaultSharedPreferences(context).edit().apply { PreferenceManager.getDefaultSharedPreferences(context).edit().apply {
putLong(context.getString(R.string.timeout_backup_key), System.currentTimeMillis()) putLong(TIMEOUT_BACKUP_KEY, System.currentTimeMillis())
apply() 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 { fun getClipboardTimeout(context: Context): Long {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.clipboard_timeout_key), return prefs.getString(context.getString(R.string.clipboard_timeout_key),
context.getString(R.string.clipboard_timeout_default))?.toLong() TIMEOUT_DEFAULT)?.toLong()
?: TimeoutHelper.DEFAULT_TIMEOUT ?: com.kunzisoft.keepass.timeout.TimeoutHelper.DEFAULT_TIMEOUT
} }
fun getAdvancedUnlockTimeout(context: Context): Long { fun getAdvancedUnlockTimeout(context: Context): Long {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getString(context.getString(R.string.temp_advanced_unlock_timeout_key), return prefs.getString(context.getString(R.string.temp_advanced_unlock_timeout_key),
context.getString(R.string.temp_advanced_unlock_timeout_default))?.toLong() 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 { fun isLockDatabaseWhenScreenShutOffEnable(context: Context): Boolean {
@@ -500,7 +454,8 @@ object PreferencesUtil {
return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key), return prefs.getBoolean(context.getString(R.string.biometric_unlock_enable_key),
context.resources.getBoolean(R.bool.biometric_unlock_enable_default)) context.resources.getBoolean(R.bool.biometric_unlock_enable_default))
&& (if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { && (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 { } else {
false false
}) })
@@ -593,13 +548,6 @@ object PreferencesUtil {
.apply() .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 { fun emptyPasswordAllowed(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.allow_no_password_key), return prefs.getBoolean(context.getString(R.string.allow_no_password_key),
@@ -631,7 +579,9 @@ object PreferencesUtil {
} }
fun isKeyboardSaveSearchInfoEnable(context: Context): Boolean { fun isKeyboardSaveSearchInfoEnable(context: Context): Boolean {
if (!MagikeyboardService.activatedInSettings(context)) if (!com.kunzisoft.keepass.magikeyboard.MagikeyboardService.Companion.activatedInSettings(
context)
)
return false return false
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.keyboard_save_search_info_key), return prefs.getBoolean(context.getString(R.string.keyboard_save_search_info_key),
@@ -716,7 +666,7 @@ object PreferencesUtil {
fun getDefaultApplicationIdBlocklist(resources: Resources?): Set<String> { fun getDefaultApplicationIdBlocklist(resources: Resources?): Set<String> {
return resources?.getStringArray(R.array.autofill_application_id_blocklist_default) return resources?.getStringArray(R.array.autofill_application_id_blocklist_default)
?.toMutableSet()?.apply { ?.toMutableSet()?.apply {
add(BuildConfig.APPLICATION_ID) add(com.kunzisoft.keepass.BuildConfig.APPLICATION_ID)
} ?: emptySet() } ?: emptySet()
} }
@@ -754,10 +704,12 @@ object PreferencesUtil {
fun getAppProperties(context: Context): Properties { fun getAppProperties(context: Context): Properties {
val properties = Properties() val properties = Properties()
for ((name, value) in PreferenceManager.getDefaultSharedPreferences(context).all) { for ((name, value) in PreferenceManager.getDefaultSharedPreferences(
context).all) {
properties[name] = value.toString() 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() properties[name] = value.toString()
} }
return properties return properties
@@ -780,7 +732,9 @@ object PreferencesUtil {
try { try {
putProperty(this, name as String, value as String) putProperty(this, name as String, value as String)
} catch (e:Exception) { } catch (e:Exception) {
Log.e("PreferencesUtil", "Error when trying to parse app property $name=$value", e) Log.e("PreferencesUtil",
"Error when trying to parse app property $name=$value",
e)
} }
} }
}.apply() }.apply()
@@ -788,7 +742,7 @@ object PreferencesUtil {
fun setAppProperties(context: Context, properties: Properties) { fun setAppProperties(context: Context, properties: Properties) {
putPropertiesInPreferences(properties, putPropertiesInPreferences(properties,
PreferenceManager.getDefaultSharedPreferences(context)) { editor, name, value -> PreferenceManager.getDefaultSharedPreferences(context)) { editor, name, value ->
when (name) { when (name) {
context.getString(R.string.allow_no_password_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.allow_no_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.delete_entered_password_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.delete_entered_password_key) -> editor.putBoolean(name, value.toBoolean())
@@ -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_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.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.auto_focus_search_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.subdomain_search_key) -> editor.putBoolean(name, value.toBoolean()) SUBDOMAIN_SEARCH_KEY -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.app_timeout_key) -> editor.putString(name, value.toLong().toString()) 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_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_back_root_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.lock_database_show_button_key) -> editor.putBoolean(name, value.toBoolean()) context.getString(R.string.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_key) -> setStyle(context, value)
context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value) context.getString(R.string.setting_style_brightness_key) -> editor.putString(name, value)
context.getString(R.string.setting_icon_pack_choose_key) -> editor.putString(name, value) 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.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.hide_password_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.colorize_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.show_uuid_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.list_size_key) -> editor.putString(name, value) 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.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.enable_education_screens_key) -> editor.putBoolean(name, value.toBoolean())
context.getString(R.string.password_generator_length_key) -> editor.putInt(name, value.toInt()) context.getString(R.string.password_generator_length_key) -> editor.putInt(name, value.toInt())
@@ -869,8 +823,13 @@ object PreferencesUtil {
} }
putPropertiesInPreferences(properties, putPropertiesInPreferences(properties,
Education.getEducationSharedPreferences(context)) { editor, name, value -> Education.getEducationSharedPreferences(
Education.putPropertiesInEducationPreferences(context, editor, name, value) 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_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 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 REMOVE_ENTRY_MAGIKEYBOARD_ACTION = "com.kunzisoft.keepass.REMOVE_ENTRY_MAGIKEYBOARD"
const val BACK_PREVIOUS_KEYBOARD_ACTION = "com.kunzisoft.keepass.BACK_PREVIOUS_KEYBOARD" const val BACK_PREVIOUS_KEYBOARD_ACTION = "com.kunzisoft.keepass.BACK_PREVIOUS_KEYBOARD"
@@ -165,4 +164,4 @@ fun Context.closeDatabase(database: Database?) {
// Release not useful URI permission // Release not useful URI permission
UriUtil.releaseAllUnnecessaryPermissionUris(applicationContext) UriUtil.releaseAllUnnecessaryPermissionUris(applicationContext)
} }

View File

@@ -18,12 +18,9 @@
* *
*/ */
package com.kunzisoft.keepass.utils package com.kunzisoft.keepass.utils
open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) { open class SingletonHolderParameter<out T, in A>(private val constructor: (A) -> T) {
@Volatile @Volatile
private var instance: T? = null private var instance: T? = null
fun getInstance(arg: A): T { fun getInstance(arg: A): T {
return when { return when {
instance != null -> instance!! 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 package com.kunzisoft.keepass.viewmodels
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.text.format.Formatter import android.text.format.Formatter
import androidx.documentfile.provider.DocumentFile
import com.kunzisoft.keepass.utils.UriUtil
import java.io.Serializable import java.io.Serializable
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.Date
class FileDatabaseInfo : Serializable { class FileDatabaseInfo : Serializable {
private var context: Context private var context: Context
private var documentFile: DocumentFile? = null private var documentFile: androidx.documentfile.provider.DocumentFile? = null
var fileUri: Uri? var fileUri: Uri?
private set private set
@@ -43,16 +22,16 @@ class FileDatabaseInfo : Serializable {
constructor(context: Context, filePath: String) { constructor(context: Context, filePath: String) {
this.context = context this.context = context
this.fileUri = UriUtil.parse(filePath) this.fileUri = com.kunzisoft.keepass.utils.UriUtil.parse(filePath)
init() init()
} }
fun init() { fun init() {
// Check permission // Check permission
fileUri?.let { uri -> 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 var exists: Boolean = false
@@ -92,4 +71,4 @@ class FileDatabaseInfo : Serializable {
else -> if (exists) documentFile?.name else fileUri?.path else -> if (exists) documentFile?.name else fileUri?.path
} }
} }
} }

View File

@@ -29,10 +29,9 @@
<dimen name="card_view_margin_vertical">5dp</dimen> <dimen name="card_view_margin_vertical">5dp</dimen>
<dimen name="card_view_padding">14dp</dimen> <dimen name="card_view_padding">14dp</dimen>
<dimen name="item_file_info_height">148dp</dimen> <dimen name="item_file_info_height">148dp</dimen>
<dimen name="icon_size">32dp</dimen>
<dimen name="lock_button_size">48dp</dimen> <dimen name="lock_button_size">48dp</dimen>
<dimen name="hidden_lock_button_size">0dp</dimen> <dimen name="hidden_lock_button_size">0dp</dimen>
<dimen name="content_percent">1</dimen> <dimen name="content_percent">1</dimen>
<dimen name="toolbar_parallax_height">160dp</dimen> <dimen name="toolbar_parallax_height">160dp</dimen>
<integer name="animation_duration">260</integer> <integer name="animation_duration">260</integer>
</resources> </resources>

View File

@@ -78,12 +78,10 @@
<string name="content_description_remove_from_list">Remove</string> <string name="content_description_remove_from_list">Remove</string>
<string name="content_description_keyboard_close_fields">Close fields</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="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_request">Waiting for the challenge request…</string>
<string name="waiting_challenge_response">Waiting for the challenge response…</string> <string name="waiting_challenge_response">Waiting for the challenge response…</string>
<string name="database">Database</string> <string name="database">Database</string>
<string name="template_group_name">Templates</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="default_checkbox">Use as default database</string>
<string name="digits">Digits</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;. <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="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_accessed">Accessed</string>
<string name="entry_cancel">Cancel</string> <string name="entry_cancel">Cancel</string>
<string name="entry_notes">Notes</string>
<string name="entry_confpassword">Confirm password</string> <string name="entry_confpassword">Confirm password</string>
<string name="entry_created">Created</string> <string name="entry_created">Created</string>
<string name="entry_expires">Expires</string>
<string name="expired">Expired</string> <string name="expired">Expired</string>
<string name="entry_UUID">UUID</string> <string name="entry_UUID">UUID</string>
<string name="entry_history">History</string> <string name="entry_history">History</string>
@@ -108,11 +104,9 @@
<string name="auto_type">Auto-Type</string> <string name="auto_type">Auto-Type</string>
<string name="auto_type_sequence">Auto-Type sequence</string> <string name="auto_type_sequence">Auto-Type sequence</string>
<string name="entry_not_found">Could not find entry data.</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="tags">Tags</string>
<string name="custom_data">Custom data</string> <string name="custom_data">Custom data</string>
<string name="save">Save</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="entry_setup_otp">Set up one-time password</string>
<string name="otp_type">OTP type</string> <string name="otp_type">OTP type</string>
<string name="otp_secret">Secret</string> <string name="otp_secret">Secret</string>
@@ -124,38 +118,8 @@
<string name="current_group">Current group</string> <string name="current_group">Current group</string>
<string name="case_sensitive">Case sensitive</string> <string name="case_sensitive">Case sensitive</string>
<string name="regex">Regular expression</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_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_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_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_can_not_handle_uri">Could not handle this URI in KeePassDX.</string>
<string name="error_file_not_create">Could not create file</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_word_reserved">This word is reserved and cannot be used.</string>
<string name="error_nokeyfile">Select a keyfile.</string> <string name="error_nokeyfile">Select a keyfile.</string>
<string name="error_no_hardware_key">Select a hardware key.</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_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_disallow_no_credentials">At least one credential must be set.</string>
<string name="error_pass_match">The passwords do not match.</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_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_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_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">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_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_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_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> <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_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_cancel_by_user">Cancelled by user.</string>
<string name="error_driver_required">Driver for %1$s is required.</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_name">Field name</string>
<string name="field_value">Field value</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="file_browser">File manager</string>
<string name="generate_password">Generate password</string> <string name="generate_password">Generate password</string>
<string name="hint_conf_pass">Confirm password</string> <string name="hint_conf_pass">Confirm password</string>
@@ -224,10 +173,6 @@
<string name="hint_pass">Password</string> <string name="hint_pass">Password</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="passphrase">Passphrase</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="keyfile_is_empty">The keyfile is empty.</string>
<string name="length">Length</string> <string name="length">Length</string>
<string name="hide_password_title">Hide passwords</string> <string name="hide_password_title">Hide passwords</string>
@@ -349,7 +294,6 @@
<string name="special">Special</string> <string name="special">Special</string>
<string name="search">Search</string> <string name="search">Search</string>
<string name="underline">Underline</string> <string name="underline">Underline</string>
<string name="unsupported_db_version">Unsupported database version.</string>
<string name="uppercase">Upper-case</string> <string name="uppercase">Upper-case</string>
<string name="warning">Warning</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> <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="application_appearance">Interface</string>
<string name="other">Other</string> <string name="other">Other</string>
<string name="compression">Compression</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="keyboard">Keyboard</string>
<string name="magic_keyboard_title">Magikeyboard</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> <string name="magic_keyboard_explanation_summary">Activate a custom keyboard populating your passwords and all identity fields</string>
@@ -702,4 +642,4 @@
<string name="show_entry_colors_summary">Displays foreground and background colors for an entry</string> <string name="show_entry_colors_summary">Displays foreground and background colors for an entry</string>
<string name="hide_expired_entries_title">Hide expired entries</string> <string name="hide_expired_entries_title">Hide expired entries</string>
<string name="hide_expired_entries_summary">Expired entries are not shown</string> <string name="hide_expired_entries_summary">Expired entries are not shown</string>
</resources> </resources>

View File

@@ -1,7 +1,7 @@
plugins { apply plugin: 'com.android.library'
id 'com.android.library' apply plugin: 'kotlin-android'
id 'org.jetbrains.kotlin.android' apply plugin: 'kotlin-parcelize'
} apply plugin: 'kotlin-kapt'
android { android {
compileSdkVersion 32 compileSdkVersion 32
@@ -16,6 +16,13 @@ android {
consumerProguardFiles "consumer-rules.pro" consumerProguardFiles "consumer-rules.pro"
buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}" buildConfigField "String[]", "ICON_PACKS", "{\"classic\",\"material\"}"
kapt {
arguments {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
}
} }
buildTypes { buildTypes {
@@ -33,6 +40,8 @@ android {
} }
} }
def room_version = "2.4.3"
dependencies { dependencies {
implementation "androidx.core:core-ktx:$android_core_version" implementation "androidx.core:core-ktx:$android_core_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' 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-classic')
implementation project(path: ':icon-pack-material') 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') implementation project(path: ':crypto')
testImplementation 'junit:junit:4.13.2' 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.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseException import com.kunzisoft.keepass.database.exception.DatabaseException
import com.kunzisoft.keepass.hardware.HardwareKey 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.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
@@ -61,7 +61,7 @@ class MergeDatabaseRunnable(private val context: Context,
if (result.isSuccess) { if (result.isSuccess) {
// Register the current time to init the lock timer // Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context) DatabasePreferencesUtil.saveCurrentTime(context)
} }
} }

View File

@@ -23,37 +23,39 @@ import android.content.Context
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.binary.BinaryData import com.kunzisoft.keepass.database.element.binary.BinaryData
import com.kunzisoft.keepass.database.exception.DatabaseException 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.ActionRunnable
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater 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 mDatabase: Database, private val context: Context,
private val progressTaskUpdater: ProgressTaskUpdater?, private val mDatabase: Database,
private val mLoadDatabaseResult: ((Result) -> Unit)?) private val progressTaskUpdater: ProgressTaskUpdater?,
: ActionRunnable() { private val mLoadDatabaseResult: ((Result) -> Unit)?,
) : ActionRunnable() {
override fun onStartRun() { override fun onStartRun() {
// Clear before we load // Clear before we load
mDatabase.clearIndexesAndBinaries(UriUtil.getBinaryDir(context)) mDatabase.clearIndexesAndBinaries(UriUtilDatabase.getBinaryDir(
context))
mDatabase.wasReloaded = true mDatabase.wasReloaded = true
} }
override fun onActionRun() { override fun onActionRun() {
try { try {
mDatabase.reloadData(context.contentResolver, mDatabase.reloadData(context.contentResolver,
{ memoryWanted -> { memoryWanted ->
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted) BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
}, },
progressTaskUpdater) progressTaskUpdater)
} catch (e: DatabaseException) { } catch (e: DatabaseException) {
setError(e) setError(e)
} }
if (result.isSuccess) { if (result.isSuccess) {
// Register the current time to init the lock timer // Register the current time to init the lock timer
PreferencesUtil.saveCurrentTime(context) DatabasePreferencesUtil.saveCurrentTime(context)
} else { } else {
mDatabase.clearAndClose(context) 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.database.search.SearchParameters
import com.kunzisoft.keepass.hardware.HardwareKey import com.kunzisoft.keepass.hardware.HardwareKey
import com.kunzisoft.keepass.icons.IconDrawableFactory import com.kunzisoft.keepass.icons.IconDrawableFactory
import com.kunzisoft.keepass.icons.InterfaceIconPackChooser
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
import com.kunzisoft.keepass.utils.* import com.kunzisoft.keepass.utils.*
import java.io.* import java.io.*
import java.util.* import java.util.*
class Database { class Database(private val iconPackChooser: InterfaceIconPackChooser) {
// To keep a reference for specific methods provided by version // To keep a reference for specific methods provided by version
private var mDatabaseKDB: DatabaseKDB? = null private var mDatabaseKDB: DatabaseKDB? = null
@@ -76,8 +77,9 @@ class Database {
var isReadOnly = false var isReadOnly = false
val iconDrawableFactory = IconDrawableFactory( val iconDrawableFactory = IconDrawableFactory(
{ binaryCache }, iconPackChooser = iconPackChooser,
{ iconId -> iconsManager.getBinaryForCustomIcon(iconId) } retrieveBinaryCache = { binaryCache },
retrieveCustomIconBinary = { iconId -> iconsManager.getBinaryForCustomIcon(iconId) }
) )
var loaded = false var loaded = false
@@ -646,7 +648,7 @@ class Database {
} }
// New database instance to get new changes // New database instance to get new changes
val databaseToMerge = Database() val databaseToMerge = Database(iconPackChooser)
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
try { try {
@@ -785,7 +787,7 @@ class Database {
openDatabaseKDBX: (InputStream) -> Unit) { openDatabaseKDBX: (InputStream) -> Unit) {
try { try {
// Load Data, pass Uris as InputStreams // Load Data, pass Uris as InputStreams
val databaseStream = UriUtil.getUriInputStream(contentResolver, databaseUri) val databaseStream = UriUtilDatabase.getUriInputStream(contentResolver, databaseUri)
?: throw UnknownDatabaseLocationException() ?: throw UnknownDatabaseLocationException()
BufferedInputStream(databaseStream).use { databaseInputStream -> BufferedInputStream(databaseStream).use { databaseInputStream ->
@@ -866,7 +868,7 @@ class Database {
} }
} }
// Copy from the cache to the final stream // Copy from the cache to the final stream
UriUtil.getUriOutputStream(contentResolver, saveUri)?.use { outputStream -> UriUtilDatabase.getUriOutputStream(contentResolver, saveUri)?.use { outputStream ->
cacheFile.inputStream().use { inputStream -> cacheFile.inputStream().use { inputStream ->
inputStream.readAllBytes { buffer -> inputStream.readAllBytes { buffer ->
outputStream.write(buffer) outputStream.write(buffer)
@@ -1005,7 +1007,7 @@ class Database {
} }
fun clearAndClose(context: Context? = null) { 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.mDatabaseKDB = null
this.mDatabaseKDBX = null this.mDatabaseKDBX = null
this.fileUri = 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.database.element.node.*
import com.kunzisoft.keepass.model.EntryInfo import com.kunzisoft.keepass.model.EntryInfo
import com.kunzisoft.keepass.model.GroupInfo import com.kunzisoft.keepass.model.GroupInfo
import com.kunzisoft.keepass.settings.PreferencesUtil import com.kunzisoft.keepass.settings.DatabasePreferencesUtil
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -87,7 +87,7 @@ class Group : Node, GroupVersionedInterface<Group, Entry> {
companion object { companion object {
fun getDefaults(context: Context): Array<ChildFilter> { fun getDefaults(context: Context): Array<ChildFilter> {
return if (PreferencesUtil.showExpiredEntries(context)) { return if (DatabasePreferencesUtil.showExpiredEntries(context)) {
arrayOf(META_STREAM) arrayOf(META_STREAM)
} else { } else {
arrayOf(META_STREAM, EXPIRED) 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.hardware.HardwareKey
import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars import com.kunzisoft.keepass.utils.StringUtil.removeSpaceChars
import com.kunzisoft.keepass.utils.StringUtil.toHexString 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.readEnum
import com.kunzisoft.keepass.utils.writeEnum import com.kunzisoft.keepass.utils.writeEnum
import org.apache.commons.codec.binary.Hex import org.apache.commons.codec.binary.Hex
@@ -148,7 +148,7 @@ data class MainCredential(var password: String? = null,
@Throws(Exception::class) @Throws(Exception::class)
private fun getKeyFileData(contentResolver: ContentResolver, private fun getKeyFileData(contentResolver: ContentResolver,
keyFileUri: Uri): ByteArray? { keyFileUri: Uri): ByteArray? {
UriUtil.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream -> UriUtilDatabase.getUriInputStream(contentResolver, keyFileUri)?.use { keyFileInputStream ->
return keyFileInputStream.readBytes() return keyFileInputStream.readBytes()
} }
return null return null

View File

@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.database.element.database
import android.content.res.Resources import android.content.res.Resources
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable 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.ObjectNameResource
import com.kunzisoft.keepass.utils.readEnum import com.kunzisoft.keepass.utils.readEnum
import com.kunzisoft.keepass.utils.writeEnum import com.kunzisoft.keepass.utils.writeEnum

View File

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

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