mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Implementation fo "Merge from" and "Save a copy to"
This commit is contained in:
@@ -51,6 +51,7 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.*
|
||||
import com.kunzisoft.keepass.activities.fragments.GroupFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||
import com.kunzisoft.keepass.adapters.BreadcrumbAdapter
|
||||
@@ -123,6 +124,9 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
|
||||
private var actionNodeMode: ActionMode? = null
|
||||
|
||||
// Manage merge
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
// Manage group
|
||||
private var mSearchState: SearchState? = null
|
||||
private var mMainGroupState: GroupState? = null // Group state, not a search
|
||||
@@ -253,6 +257,19 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
drawerLayout?.addDrawerListener(toggle)
|
||||
toggle.syncState()
|
||||
|
||||
// Manage 'merge from" and "save to"
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
mExternalFileHelper?.buildOpenDocument { uri ->
|
||||
uri?.let {
|
||||
mergeDatabaseFrom(it)
|
||||
}
|
||||
}
|
||||
mExternalFileHelper?.buildCreateDocument("application/x-keepass") { uri ->
|
||||
uri?.let {
|
||||
saveDatabaseTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Menu in drawer
|
||||
databaseNavView?.apply {
|
||||
inflateMenu(R.menu.settings)
|
||||
@@ -264,6 +281,15 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
// To avoid flickering when launch settings in a LockingActivity
|
||||
SettingsActivity.launch(this@GroupActivity, true)
|
||||
}
|
||||
R.id.menu_merge_from -> {
|
||||
mExternalFileHelper?.openDocument()
|
||||
}
|
||||
R.id.menu_save_copy_to -> {
|
||||
mExternalFileHelper?.createDocument(
|
||||
getString(R.string.database_file_name_default) +
|
||||
getString(R.string.database_file_name_copy) +
|
||||
getString(R.string.database_file_extension_default))
|
||||
}
|
||||
R.id.menu_contribute -> {
|
||||
UriUtil.gotoUrl(this@GroupActivity, R.string.contribution_url)
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
||||
mDatabaseTaskProvider?.startDatabaseSave(save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.mergeDatabase.observe(this) { fixDuplicateUuid ->
|
||||
mDatabaseTaskProvider?.startDatabaseMerge(fixDuplicateUuid)
|
||||
mDatabaseViewModel.mergeDatabase.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseMerge()
|
||||
}
|
||||
|
||||
mDatabaseViewModel.reloadDatabase.observe(this) { fixDuplicateUuid ->
|
||||
@@ -263,8 +263,16 @@ abstract class DatabaseLockActivity : DatabaseModeActivity(),
|
||||
mDatabaseTaskProvider?.startDatabaseSave(true)
|
||||
}
|
||||
|
||||
fun saveDatabaseTo(uri: Uri) {
|
||||
mDatabaseTaskProvider?.startDatabaseSave(true, uri)
|
||||
}
|
||||
|
||||
fun mergeDatabase() {
|
||||
mDatabaseTaskProvider?.startDatabaseMerge(false)
|
||||
mDatabaseTaskProvider?.startDatabaseMerge()
|
||||
}
|
||||
|
||||
fun mergeDatabaseFrom(uri: Uri) {
|
||||
mDatabaseTaskProvider?.startDatabaseMerge(uri)
|
||||
}
|
||||
|
||||
fun reloadDatabase() {
|
||||
|
||||
@@ -84,7 +84,6 @@ import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
|
||||
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Utility class to connect an activity or a service to the DatabaseTaskNotificationService,
|
||||
@@ -356,9 +355,11 @@ class DatabaseTaskProvider {
|
||||
, ACTION_DATABASE_LOAD_TASK)
|
||||
}
|
||||
|
||||
fun startDatabaseMerge(fixDuplicateUuid: Boolean) {
|
||||
fun startDatabaseMerge(fromDatabaseUri: Uri? = null) {
|
||||
start(Bundle().apply {
|
||||
putBoolean(DatabaseTaskNotificationService.FIX_DUPLICATE_UUID_KEY, fixDuplicateUuid)
|
||||
if (fromDatabaseUri != null) {
|
||||
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, fromDatabaseUri)
|
||||
}
|
||||
}
|
||||
, ACTION_DATABASE_MERGE_TASK)
|
||||
}
|
||||
@@ -693,9 +694,12 @@ class DatabaseTaskProvider {
|
||||
/**
|
||||
* Save Database without parameter
|
||||
*/
|
||||
fun startDatabaseSave(save: Boolean) {
|
||||
fun startDatabaseSave(save: Boolean, saveToUri: Uri? = null) {
|
||||
start(Bundle().apply {
|
||||
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
|
||||
if (saveToUri != null) {
|
||||
putParcelable(DatabaseTaskNotificationService.DATABASE_URI_KEY, saveToUri)
|
||||
}
|
||||
}
|
||||
, ACTION_DATABASE_SAVE)
|
||||
}
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.binary.BinaryData
|
||||
import com.kunzisoft.keepass.database.element.binary.LoadedKey
|
||||
import com.kunzisoft.keepass.database.exception.LoadDatabaseException
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
class MergeDatabaseRunnable(private val context: Context,
|
||||
private val mDatabase: Database,
|
||||
private val mDatabaseToMergeUri: Uri?,
|
||||
private val progressTaskUpdater: ProgressTaskUpdater?,
|
||||
private val mLoadDatabaseResult: ((Result) -> Unit)?)
|
||||
: ActionRunnable() {
|
||||
@@ -41,7 +41,7 @@ class MergeDatabaseRunnable(private val context: Context,
|
||||
|
||||
override fun onActionRun() {
|
||||
try {
|
||||
mDatabase.mergeData(context.contentResolver,
|
||||
mDatabase.mergeData(mDatabaseToMergeUri, context.contentResolver,
|
||||
{ memoryWanted ->
|
||||
BinaryData.canMemoryBeAllocatedInRAM(context, memoryWanted)
|
||||
},
|
||||
|
||||
@@ -20,13 +20,15 @@
|
||||
package com.kunzisoft.keepass.database.action
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.DatabaseException
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
open class SaveDatabaseRunnable(protected var context: Context,
|
||||
protected var database: Database,
|
||||
private var saveDatabase: Boolean)
|
||||
private var saveDatabase: Boolean,
|
||||
private var databaseCopyUri: Uri? = null)
|
||||
: ActionRunnable() {
|
||||
|
||||
var mAfterSaveDatabase: ((Result) -> Unit)? = null
|
||||
@@ -37,7 +39,7 @@ open class SaveDatabaseRunnable(protected var context: Context,
|
||||
database.checkVersion()
|
||||
if (saveDatabase && result.isSuccess) {
|
||||
try {
|
||||
database.saveData(context.contentResolver)
|
||||
database.saveData(databaseCopyUri, context.contentResolver)
|
||||
} catch (e: DatabaseException) {
|
||||
setError(e)
|
||||
}
|
||||
|
||||
@@ -671,7 +671,8 @@ class Database {
|
||||
}
|
||||
|
||||
@Throws(LoadDatabaseException::class)
|
||||
fun mergeData(contentResolver: ContentResolver,
|
||||
fun mergeData(databaseToMergeUri: Uri?,
|
||||
contentResolver: ContentResolver,
|
||||
isRAMSufficient: (memoryWanted: Long) -> Boolean,
|
||||
progressTaskUpdater: ProgressTaskUpdater?) {
|
||||
|
||||
@@ -681,7 +682,7 @@ class Database {
|
||||
|
||||
// New database instance to get new changes
|
||||
val databaseToMerge = Database()
|
||||
databaseToMerge.fileUri = this.fileUri
|
||||
databaseToMerge.fileUri = databaseToMergeUri ?: this.fileUri
|
||||
|
||||
try {
|
||||
databaseToMerge.fileUri?.let { databaseUri ->
|
||||
@@ -867,10 +868,58 @@ class Database {
|
||||
}
|
||||
|
||||
@Throws(DatabaseOutputException::class)
|
||||
fun saveData(contentResolver: ContentResolver) {
|
||||
fun saveData(uri: Uri?, contentResolver: ContentResolver) {
|
||||
try {
|
||||
this.fileUri?.let {
|
||||
saveData(contentResolver, it)
|
||||
(uri ?: this.fileUri)?.let { saveUri ->
|
||||
if (saveUri.scheme == "file") {
|
||||
saveUri.path?.let { filename ->
|
||||
val tempFile = File("$filename.tmp")
|
||||
|
||||
var fileOutputStream: FileOutputStream? = null
|
||||
try {
|
||||
fileOutputStream = FileOutputStream(tempFile)
|
||||
val pmo = mDatabaseKDB?.let { DatabaseOutputKDB(it, fileOutputStream) }
|
||||
?: mDatabaseKDBX?.let { DatabaseOutputKDBX(it, fileOutputStream) }
|
||||
pmo?.output()
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
fileOutputStream?.close()
|
||||
}
|
||||
|
||||
// Force data to disk before continuing
|
||||
try {
|
||||
fileOutputStream?.fd?.sync()
|
||||
} catch (e: SyncFailedException) {
|
||||
// Ignore if fsync fails. We tried.
|
||||
}
|
||||
|
||||
if (!tempFile.renameTo(File(filename))) {
|
||||
throw IOException()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
outputStream = contentResolver.openOutputStream(saveUri, "rwt")
|
||||
outputStream?.let { definedOutputStream ->
|
||||
val databaseOutput =
|
||||
mDatabaseKDB?.let { DatabaseOutputKDB(it, definedOutputStream) }
|
||||
?: mDatabaseKDBX?.let {
|
||||
DatabaseOutputKDBX(
|
||||
it,
|
||||
definedOutputStream
|
||||
)
|
||||
}
|
||||
databaseOutput?.output()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
outputStream?.close()
|
||||
}
|
||||
}
|
||||
this.dataModifiedSinceLastLoading = false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to save database", e)
|
||||
@@ -878,55 +927,6 @@ class Database {
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, DatabaseOutputException::class)
|
||||
private fun saveData(contentResolver: ContentResolver, uri: Uri) {
|
||||
|
||||
if (uri.scheme == "file") {
|
||||
uri.path?.let { filename ->
|
||||
val tempFile = File("$filename.tmp")
|
||||
|
||||
var fileOutputStream: FileOutputStream? = null
|
||||
try {
|
||||
fileOutputStream = FileOutputStream(tempFile)
|
||||
val pmo = mDatabaseKDB?.let { DatabaseOutputKDB(it, fileOutputStream) }
|
||||
?: mDatabaseKDBX?.let { DatabaseOutputKDBX(it, fileOutputStream) }
|
||||
pmo?.output()
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
fileOutputStream?.close()
|
||||
}
|
||||
|
||||
// Force data to disk before continuing
|
||||
try {
|
||||
fileOutputStream?.fd?.sync()
|
||||
} catch (e: SyncFailedException) {
|
||||
// Ignore if fsync fails. We tried.
|
||||
}
|
||||
|
||||
if (!tempFile.renameTo(File(filename))) {
|
||||
throw IOException()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var outputStream: OutputStream? = null
|
||||
try {
|
||||
outputStream = contentResolver.openOutputStream(uri, "rwt")
|
||||
outputStream?.let { definedOutputStream ->
|
||||
val databaseOutput = mDatabaseKDB?.let { DatabaseOutputKDB(it, definedOutputStream) }
|
||||
?: mDatabaseKDBX?.let { DatabaseOutputKDBX(it, definedOutputStream) }
|
||||
databaseOutput?.output()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e)
|
||||
} finally {
|
||||
outputStream?.close()
|
||||
}
|
||||
}
|
||||
this.fileUri = uri
|
||||
this.dataModifiedSinceLastLoading = false
|
||||
}
|
||||
|
||||
fun clearIndexesAndBinaries(filesDirectory: File? = null) {
|
||||
this.mDatabaseKDB?.clearIndexes()
|
||||
this.mDatabaseKDBX?.clearIndexes()
|
||||
|
||||
@@ -22,7 +22,10 @@ package com.kunzisoft.keepass.services
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.media.app.NotificationCompat
|
||||
import com.kunzisoft.keepass.R
|
||||
@@ -44,11 +47,13 @@ import com.kunzisoft.keepass.model.SnapFileDatabaseInfo
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import com.kunzisoft.keepass.utils.DATABASE_START_TASK_ACTION
|
||||
import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
import com.kunzisoft.keepass.utils.closeDatabase
|
||||
import com.kunzisoft.keepass.viewmodels.FileDatabaseInfo
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
open class DatabaseTaskNotificationService : LockNotificationService(), ProgressTaskUpdater {
|
||||
|
||||
@@ -226,7 +231,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
val actionRunnable: ActionRunnable? = when (intentAction) {
|
||||
ACTION_DATABASE_CREATE_TASK -> buildDatabaseCreateActionTask(intent, database)
|
||||
ACTION_DATABASE_LOAD_TASK -> buildDatabaseLoadActionTask(intent, database)
|
||||
ACTION_DATABASE_MERGE_TASK -> buildDatabaseMergeActionTask(database)
|
||||
ACTION_DATABASE_MERGE_TASK -> buildDatabaseMergeActionTask(intent, database)
|
||||
ACTION_DATABASE_RELOAD_TASK -> buildDatabaseReloadActionTask(database)
|
||||
ACTION_DATABASE_ASSIGN_PASSWORD_TASK -> buildDatabaseAssignPasswordActionTask(intent, database)
|
||||
ACTION_DATABASE_CREATE_GROUP_TASK -> buildDatabaseCreateGroupActionTask(intent, database)
|
||||
@@ -606,10 +611,16 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDatabaseMergeActionTask(database: Database): ActionRunnable {
|
||||
private fun buildDatabaseMergeActionTask(intent: Intent, database: Database): ActionRunnable {
|
||||
var databaseToMergeUri: Uri? = null
|
||||
if (intent.hasExtra(DATABASE_URI_KEY)) {
|
||||
databaseToMergeUri = intent.getParcelableExtra(DATABASE_URI_KEY)
|
||||
}
|
||||
|
||||
return MergeDatabaseRunnable(
|
||||
this,
|
||||
database,
|
||||
databaseToMergeUri,
|
||||
this
|
||||
) { result ->
|
||||
// No need to add each info to reload database
|
||||
@@ -911,9 +922,16 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
*/
|
||||
private fun buildDatabaseSave(intent: Intent, database: Database): ActionRunnable? {
|
||||
return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
|
||||
|
||||
var databaseCopyUri: Uri? = null
|
||||
if (intent.hasExtra(DATABASE_URI_KEY)) {
|
||||
databaseCopyUri = intent.getParcelableExtra(DATABASE_URI_KEY)
|
||||
}
|
||||
|
||||
SaveDatabaseRunnable(this,
|
||||
database,
|
||||
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false))
|
||||
!database.isReadOnly && intent.getBooleanExtra(SAVE_DATABASE_KEY, false),
|
||||
databaseCopyUri)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
<!-- File Path -->
|
||||
<string name="database_file_name_default" translatable="false">keepass</string>
|
||||
<string name="database_file_name_copy" translatable="false">_copy</string>
|
||||
<string name="database_file_extension_default" translatable="false">.kdbx</string>
|
||||
<string name="database_default_name" translatable="false">KeePassDX Database</string>
|
||||
<string name="app_properties_file_name" translatable="false">keepassdx_%1$s.properties</string>
|
||||
|
||||
Reference in New Issue
Block a user