mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
First refactoring pass
This commit is contained in:
@@ -25,14 +25,13 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentSender
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.inputmethod.InlineSuggestionsRequest
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.selection.DatabaseActivity
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper.EXTRA_INLINE_SUGGESTIONS_REQUEST
|
||||
import com.kunzisoft.keepass.autofill.KeeAutofillService
|
||||
@@ -44,14 +43,14 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.utils.LOCK_ACTION
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
class AutofillLauncherActivity : AppCompatActivity() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
class AutofillLauncherActivity : DatabaseActivity() {
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
// End activity if database not loaded
|
||||
if (database?.loaded != true) {
|
||||
finish()
|
||||
}
|
||||
// Retrieve selection mode
|
||||
EntrySelectionHelper.retrieveSpecialModeFromIntent(intent).let { specialMode ->
|
||||
when (specialMode) {
|
||||
@@ -64,7 +63,7 @@ class AutofillLauncherActivity : AppCompatActivity() {
|
||||
}
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
mDatabase?.let { database ->
|
||||
database?.let { database ->
|
||||
launchSelection(database, searchInfo)
|
||||
}
|
||||
}
|
||||
@@ -75,7 +74,7 @@ class AutofillLauncherActivity : AppCompatActivity() {
|
||||
val searchInfo = SearchInfo(registerInfo?.searchInfo)
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
mDatabase?.let { database ->
|
||||
database?.let { database ->
|
||||
launchRegistration(database, searchInfo, registerInfo)
|
||||
}
|
||||
}
|
||||
@@ -87,8 +86,6 @@ class AutofillLauncherActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
private fun launchSelection(database: Database,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
interface DatabaseRetrieval {
|
||||
fun onDatabaseRetrieved(database: Database?)
|
||||
fun onDatabaseActionFinished(database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result)
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import android.widget.ProgressBar
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.fragments.EntryFragment
|
||||
@@ -45,6 +44,7 @@ import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.education.EntryActivityEducation
|
||||
@@ -57,6 +57,7 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RELOAD_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_RESTORE_ENTRY_HISTORY
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
@@ -79,6 +80,9 @@ class EntryActivity : LockingActivity() {
|
||||
|
||||
private val mEntryViewModel: EntryViewModel by viewModels()
|
||||
|
||||
private var mEntryId: NodeId<UUID>? = null
|
||||
private var mHistoryPosition: Int = -1
|
||||
|
||||
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
||||
private var mAttachmentsToDownload: HashMap<Int, Attachment> = HashMap()
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
@@ -108,23 +112,18 @@ class EntryActivity : LockingActivity() {
|
||||
collapsingToolbarLayout?.title = " "
|
||||
toolbar?.title = " "
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, mDatabase)
|
||||
|
||||
// Retrieve the textColor to tint the icon
|
||||
val taIconColor = theme.obtainStyledAttributes(intArrayOf(R.attr.colorAccent))
|
||||
iconColor = taIconColor.getColor(0, Color.BLACK)
|
||||
taIconColor.recycle()
|
||||
|
||||
mReadOnly = mDatabase?.isReadOnly != false || mReadOnly
|
||||
|
||||
// Get Entry from UUID
|
||||
try {
|
||||
intent.getParcelableExtra<NodeId<UUID>?>(KEY_ENTRY)?.let { entryId ->
|
||||
mEntryId = entryId
|
||||
intent.removeExtra(KEY_ENTRY)
|
||||
val historyPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, -1)
|
||||
mHistoryPosition = intent.getIntExtra(KEY_ENTRY_HISTORY_POSITION, -1)
|
||||
intent.removeExtra(KEY_ENTRY_HISTORY_POSITION)
|
||||
mEntryViewModel.loadEntry(entryId, historyPosition)
|
||||
}
|
||||
} catch (e: ClassCastException) {
|
||||
Log.e(TAG, "Unable to retrieve the entry key")
|
||||
@@ -152,7 +151,7 @@ class EntryActivity : LockingActivity() {
|
||||
|
||||
// Assign title icon
|
||||
titleIconView?.let { iconView ->
|
||||
mDatabase?.iconDrawableFactory?.assignDatabaseIcon(iconView, entryInfo.icon, iconColor)
|
||||
mIconDrawableFactory?.assignDatabaseIcon(iconView, entryInfo.icon, iconColor)
|
||||
}
|
||||
|
||||
// Assign title text
|
||||
@@ -209,23 +208,36 @@ class EntryActivity : LockingActivity() {
|
||||
historySelected.historyPosition,
|
||||
mReadOnly)
|
||||
}
|
||||
}
|
||||
|
||||
mProgressDatabaseTaskProvider?.onActionFinish = { actionTask, result ->
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_RESTORE_ENTRY_HISTORY,
|
||||
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> {
|
||||
// Close the current activity after an history action
|
||||
if (result.isSuccess)
|
||||
finish()
|
||||
}
|
||||
ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Close the current activity
|
||||
this.showActionErrorIfNeeded(result)
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, database)
|
||||
mEntryViewModel.setDatabase(database)
|
||||
mEntryViewModel.loadEntry(mEntryId, mHistoryPosition)
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_RESTORE_ENTRY_HISTORY,
|
||||
ACTION_DATABASE_DELETE_ENTRY_HISTORY -> {
|
||||
// Close the current activity after an history action
|
||||
if (result.isSuccess)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Close the current activity
|
||||
this.showActionErrorIfNeeded(result)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -356,25 +368,23 @@ class EntryActivity : LockingActivity() {
|
||||
}
|
||||
R.id.menu_restore_entry_history -> {
|
||||
mEntryViewModel.getMainEntry()?.let { mainEntry ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseRestoreEntryHistory(
|
||||
restoreEntryHistory(
|
||||
mainEntry,
|
||||
mEntryViewModel.getEntryHistoryPosition(),
|
||||
!mReadOnly && mAutoSaveEnable)
|
||||
mEntryViewModel.getEntryHistoryPosition())
|
||||
}
|
||||
}
|
||||
R.id.menu_delete_entry_history -> {
|
||||
mEntryViewModel.getMainEntry()?.let { mainEntry ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseDeleteEntryHistory(
|
||||
deleteEntryHistory(
|
||||
mainEntry,
|
||||
mEntryViewModel.getEntryHistoryPosition(),
|
||||
!mReadOnly && mAutoSaveEnable)
|
||||
mEntryViewModel.getEntryHistoryPosition())
|
||||
}
|
||||
}
|
||||
R.id.menu_save_database -> {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSave(!mReadOnly)
|
||||
saveDatabase()
|
||||
}
|
||||
R.id.menu_reload_database -> {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseReload(false)
|
||||
reloadDatabase()
|
||||
}
|
||||
android.R.id.home -> finish() // close this activity and return to preview activity (if there is any)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||
import com.kunzisoft.keepass.services.KeyboardEntryNotificationService
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.AttachmentFileBinderManager
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
@@ -95,6 +96,9 @@ class EntryEditActivity : LockingActivity(),
|
||||
|
||||
private val mEntryEditViewModel: EntryEditViewModel by viewModels()
|
||||
|
||||
private var mAllowCustomFields = false
|
||||
private var mAllowOTP = false
|
||||
|
||||
// To manage attachments
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
||||
@@ -122,9 +126,6 @@ class EntryEditActivity : LockingActivity(),
|
||||
validateButton = findViewById(R.id.entry_edit_validate)
|
||||
loadingView = findViewById(R.id.loading)
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, mDatabase)
|
||||
|
||||
stopService(Intent(this, ClipboardEntryNotificationService::class.java))
|
||||
stopService(Intent(this, KeyboardEntryNotificationService::class.java))
|
||||
|
||||
@@ -227,7 +228,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
templateSelectorSpinner?.apply {
|
||||
// Build template selector
|
||||
if (templates.isNotEmpty()) {
|
||||
adapter = TemplatesSelectorAdapter(this@EntryEditActivity, mDatabase, templates)
|
||||
adapter = TemplatesSelectorAdapter(this@EntryEditActivity, mIconDrawableFactory, templates)
|
||||
setSelection(templates.indexOf(defaultTemplate))
|
||||
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
@@ -245,71 +246,74 @@ class EntryEditActivity : LockingActivity(),
|
||||
mEntryEditViewModel.onEntrySaved.observe(this) { entrySave ->
|
||||
// Open a progress dialog and save entry
|
||||
entrySave.parent?.let { parent ->
|
||||
mProgressDatabaseTaskProvider?.startDatabaseCreateEntry(
|
||||
entrySave.newEntry,
|
||||
parent,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
createEntry(entrySave.newEntry, parent)
|
||||
} ?: run {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseUpdateEntry(
|
||||
entrySave.oldEntry,
|
||||
entrySave.newEntry,
|
||||
!mReadOnly && mAutoSaveEnable
|
||||
)
|
||||
updateEntry(entrySave.oldEntry, entrySave.newEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create progress dialog
|
||||
mProgressDatabaseTaskProvider?.onActionFinish = { actionTask, result ->
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_CREATE_ENTRY_TASK,
|
||||
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||
try {
|
||||
if (result.isSuccess) {
|
||||
var newNodes: List<Node> = ArrayList()
|
||||
result.data?.getBundle(DatabaseTaskNotificationService.NEW_NODES_KEY)?.let { newNodesBundle ->
|
||||
mDatabase?.let { database ->
|
||||
newNodes = DatabaseTaskNotificationService.getListNodesFromBundle(database, newNodesBundle)
|
||||
}
|
||||
}
|
||||
if (newNodes.size == 1) {
|
||||
(newNodes[0] as? Entry?)?.let { entry ->
|
||||
EntrySelectionHelper.doSpecialAction(intent,
|
||||
{
|
||||
// Finish naturally
|
||||
finishForEntryResult(actionTask, entry)
|
||||
},
|
||||
{
|
||||
// Nothing when search retrieved
|
||||
},
|
||||
{
|
||||
entryValidatedForSave(actionTask, entry)
|
||||
},
|
||||
{
|
||||
entryValidatedForKeyboardSelection(actionTask, entry)
|
||||
},
|
||||
{ _, _ ->
|
||||
entryValidatedForAutofillSelection(entry)
|
||||
},
|
||||
{
|
||||
entryValidatedForAutofillRegistration(actionTask, entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, database)
|
||||
mEntryEditViewModel.setDatabase(database)
|
||||
mAllowCustomFields = database?.allowEntryCustomFields() == true
|
||||
mAllowOTP = database?.allowOTP == true
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_CREATE_ENTRY_TASK,
|
||||
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||
try {
|
||||
if (result.isSuccess) {
|
||||
var newNodes: List<Node> = ArrayList()
|
||||
result.data?.getBundle(DatabaseTaskNotificationService.NEW_NODES_KEY)?.let { newNodesBundle ->
|
||||
newNodes = DatabaseTaskNotificationService.getListNodesFromBundle(database, newNodesBundle)
|
||||
}
|
||||
if (newNodes.size == 1) {
|
||||
(newNodes[0] as? Entry?)?.let { entry ->
|
||||
EntrySelectionHelper.doSpecialAction(intent,
|
||||
{
|
||||
// Finish naturally
|
||||
finishForEntryResult(actionTask, entry)
|
||||
},
|
||||
{
|
||||
// Nothing when search retrieved
|
||||
},
|
||||
{
|
||||
entryValidatedForSave(actionTask, entry)
|
||||
},
|
||||
{
|
||||
entryValidatedForKeyboardSelection(database, actionTask, entry)
|
||||
},
|
||||
{ _, _ ->
|
||||
entryValidatedForAutofillSelection(database, entry)
|
||||
},
|
||||
{
|
||||
entryValidatedForAutofillRegistration(actionTask, entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to retrieve entry after database action", e)
|
||||
}
|
||||
}
|
||||
ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Close the current activity
|
||||
this.showActionErrorIfNeeded(result)
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to retrieve entry after database action", e)
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Close the current activity
|
||||
this.showActionErrorIfNeeded(result)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
}
|
||||
|
||||
private fun entryValidatedForSave(actionTask: String, entry: Entry) {
|
||||
@@ -317,26 +321,22 @@ class EntryEditActivity : LockingActivity(),
|
||||
finishForEntryResult(actionTask, entry)
|
||||
}
|
||||
|
||||
private fun entryValidatedForKeyboardSelection(actionTask: String, entry: Entry) {
|
||||
private fun entryValidatedForKeyboardSelection(database: Database, actionTask: String, entry: Entry) {
|
||||
// Populate Magikeyboard with entry
|
||||
mDatabase?.let { database ->
|
||||
populateKeyboardAndMoveAppToBackground(this,
|
||||
entry.getEntryInfo(database),
|
||||
intent)
|
||||
}
|
||||
populateKeyboardAndMoveAppToBackground(this,
|
||||
entry.getEntryInfo(database),
|
||||
intent)
|
||||
onValidateSpecialMode()
|
||||
// Don't keep activity history for entry edition
|
||||
finishForEntryResult(actionTask, entry)
|
||||
}
|
||||
|
||||
private fun entryValidatedForAutofillSelection(entry: Entry) {
|
||||
private fun entryValidatedForAutofillSelection(database: Database, entry: Entry) {
|
||||
// Build Autofill response with the entry selected
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mDatabase?.let { database ->
|
||||
AutofillHelper.buildResponseAndSetResult(this@EntryEditActivity,
|
||||
database,
|
||||
entry.getEntryInfo(database))
|
||||
}
|
||||
AutofillHelper.buildResponseAndSetResult(this@EntryEditActivity,
|
||||
database,
|
||||
entry.getEntryInfo(database))
|
||||
}
|
||||
onValidateSpecialMode()
|
||||
}
|
||||
@@ -466,10 +466,8 @@ class EntryEditActivity : LockingActivity(),
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||
|
||||
val allowCustomField = mDatabase?.allowEntryCustomFields() == true
|
||||
|
||||
menu?.findItem(R.id.menu_add_field)?.apply {
|
||||
isEnabled = allowCustomField
|
||||
isEnabled = mAllowCustomFields
|
||||
isVisible = isEnabled
|
||||
}
|
||||
|
||||
@@ -481,9 +479,8 @@ class EntryEditActivity : LockingActivity(),
|
||||
}
|
||||
|
||||
menu?.findItem(R.id.menu_add_otp)?.apply {
|
||||
val allowOTP = mDatabase?.allowOTP == true
|
||||
// OTP not compatible below KitKat
|
||||
isEnabled = allowOTP
|
||||
isEnabled = mAllowOTP
|
||||
&& !mEntryEditViewModel.entryIsTemplate()
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
isVisible = isEnabled
|
||||
@@ -513,7 +510,7 @@ class EntryEditActivity : LockingActivity(),
|
||||
|
||||
if (!generatePasswordEductionPerformed) {
|
||||
val addNewFieldView: View? = entryEditAddToolBar?.findViewById(R.id.menu_add_field)
|
||||
val addNewFieldEducationPerformed = mDatabase?.allowEntryCustomFields() == true
|
||||
val addNewFieldEducationPerformed = mAllowCustomFields
|
||||
&& addNewFieldView != null
|
||||
&& addNewFieldView.isVisible
|
||||
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.selection.DatabaseActivity
|
||||
import com.kunzisoft.keepass.database.action.DatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
||||
@@ -39,14 +41,10 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
* Activity to search or select entry in database,
|
||||
* Commonly used with Magikeyboard
|
||||
*/
|
||||
class EntrySelectionLauncherActivity : AppCompatActivity() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
class EntrySelectionLauncherActivity : DatabaseActivity() {
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
var sharedWebDomain: String? = null
|
||||
var otpString: String? = null
|
||||
|
||||
@@ -72,20 +70,22 @@ class EntrySelectionLauncherActivity : AppCompatActivity() {
|
||||
else -> {}
|
||||
}
|
||||
|
||||
|
||||
// Build domain search param
|
||||
val searchInfo = SearchInfo().apply {
|
||||
this.webDomain = sharedWebDomain
|
||||
this.otpString = otpString
|
||||
}
|
||||
|
||||
// End activity if database not loaded
|
||||
if (database?.loaded != true) {
|
||||
finish()
|
||||
}
|
||||
SearchInfo.getConcreteWebDomain(this, searchInfo.webDomain) { concreteWebDomain ->
|
||||
searchInfo.webDomain = concreteWebDomain
|
||||
mDatabase?.let { database ->
|
||||
database?.let { database ->
|
||||
launch(database, searchInfo)
|
||||
}
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
private fun launch(database: Database,
|
||||
|
||||
@@ -50,7 +50,6 @@ import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
|
||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
@@ -61,6 +60,7 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.DATABASE_URI_KEY
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
import com.kunzisoft.keepass.view.asError
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseFilesViewModel
|
||||
@@ -85,15 +85,9 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
mFileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(applicationContext)
|
||||
|
||||
setContentView(R.layout.activity_file_selection)
|
||||
@@ -195,36 +189,52 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
mAdapterDatabaseHistory?.setDefaultDatabase(it)
|
||||
}
|
||||
|
||||
// Attach the dialog thread to this activity
|
||||
mProgressDatabaseTaskProvider = ProgressDatabaseTaskProvider(this).apply {
|
||||
onActionFinish = { actionTask, result ->
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_CREATE_TASK -> {
|
||||
result.data?.getParcelable<Uri?>(DATABASE_URI_KEY)?.let { databaseUri ->
|
||||
val mainCredential = result.data?.getParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY) ?: MainCredential()
|
||||
databaseFilesViewModel.addDatabaseFile(databaseUri, mainCredential.keyFileUri)
|
||||
}
|
||||
GroupActivity.launch(this@FileDatabaseSelectActivity,
|
||||
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity))
|
||||
}
|
||||
ACTION_DATABASE_LOAD_TASK -> {
|
||||
val database = mDatabase
|
||||
if (result.isSuccess
|
||||
&& database?.loaded == true) {
|
||||
launchGroupActivity(database)
|
||||
} else {
|
||||
var resultError = ""
|
||||
val resultMessage = result.message
|
||||
// Show error message
|
||||
if (resultMessage != null && resultMessage.isNotEmpty()) {
|
||||
resultError = "$resultError $resultMessage"
|
||||
}
|
||||
Log.e(TAG, resultError)
|
||||
Snackbar.make(coordinatorLayout,
|
||||
resultError,
|
||||
Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
// Construct adapter with listeners
|
||||
if (PreferencesUtil.showRecentFiles(this@FileDatabaseSelectActivity)) {
|
||||
databaseFilesViewModel.loadListOfDatabases()
|
||||
} else {
|
||||
mAdapterDatabaseHistory?.clearDatabaseFileHistoryList()
|
||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
if (database?.loaded == true) {
|
||||
launchGroupActivity(database)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_CREATE_TASK -> {
|
||||
result.data?.getParcelable<Uri?>(DATABASE_URI_KEY)?.let { databaseUri ->
|
||||
val mainCredential = result.data?.getParcelable(DatabaseTaskNotificationService.MAIN_CREDENTIAL_KEY) ?: MainCredential()
|
||||
databaseFilesViewModel.addDatabaseFile(databaseUri, mainCredential.keyFileUri)
|
||||
}
|
||||
GroupActivity.launch(this@FileDatabaseSelectActivity,
|
||||
PreferencesUtil.enableReadOnlyDatabase(this@FileDatabaseSelectActivity))
|
||||
}
|
||||
ACTION_DATABASE_LOAD_TASK -> {
|
||||
if (result.isSuccess
|
||||
&& database.loaded) {
|
||||
launchGroupActivity(database)
|
||||
} else {
|
||||
var resultError = ""
|
||||
val resultMessage = result.message
|
||||
// Show error message
|
||||
if (resultMessage != null && resultMessage.isNotEmpty()) {
|
||||
resultError = "$resultError $resultMessage"
|
||||
}
|
||||
Log.e(TAG, resultError)
|
||||
Snackbar.make(coordinatorLayout,
|
||||
resultError,
|
||||
Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,29 +310,6 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
createDatabaseButtonView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
val database = mDatabase
|
||||
if (database?.loaded == true) {
|
||||
launchGroupActivity(database)
|
||||
} else {
|
||||
// Construct adapter with listeners
|
||||
if (PreferencesUtil.showRecentFiles(this)) {
|
||||
databaseFilesViewModel.loadListOfDatabases()
|
||||
} else {
|
||||
mAdapterDatabaseHistory?.clearDatabaseFileHistoryList()
|
||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
// Register progress task
|
||||
mProgressDatabaseTaskProvider?.registerProgressTask()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
// Unregister progress task
|
||||
mProgressDatabaseTaskProvider?.unregisterProgressTask()
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -334,15 +321,10 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
|
||||
}
|
||||
|
||||
override fun onAssignKeyDialogPositiveClick(mainCredential: MainCredential) {
|
||||
|
||||
try {
|
||||
mDatabaseFileUri?.let { databaseUri ->
|
||||
|
||||
// Create the new database
|
||||
mProgressDatabaseTaskProvider?.startDatabaseCreate(
|
||||
databaseUri,
|
||||
mainCredential
|
||||
)
|
||||
createDatabase(databaseUri, mainCredential)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val error = getString(R.string.error_create_database_file)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,6 +67,8 @@ class IconPickerActivity : LockingActivity() {
|
||||
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -84,11 +86,6 @@ class IconPickerActivity : LockingActivity() {
|
||||
mExternalFileHelper = ExternalFileHelper(this)
|
||||
|
||||
uploadButton = findViewById(R.id.icon_picker_upload)
|
||||
if (mDatabase?.allowCustomIcons == true) {
|
||||
uploadButton.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
} else {
|
||||
uploadButton.visibility = View.GONE
|
||||
}
|
||||
|
||||
lockView = findViewById(R.id.lock_button)
|
||||
lockView?.setOnClickListener {
|
||||
@@ -114,9 +111,6 @@ class IconPickerActivity : LockingActivity() {
|
||||
mIconImage = savedInstanceState.getParcelable(EXTRA_ICON) ?: mIconImage
|
||||
}
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
findViewById<ViewGroup>(R.id.icon_picker_container)?.resetAppTimeoutWhenViewFocusedOrChanged(this, mDatabase)
|
||||
|
||||
iconPickerViewModel.standardIconPicked.observe(this) { iconStandard ->
|
||||
mIconImage.standard = iconStandard
|
||||
// Remove the custom icon if a standard one is selected
|
||||
@@ -150,6 +144,22 @@ class IconPickerActivity : LockingActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
|
||||
mDatabase = database
|
||||
|
||||
if (database?.allowCustomIcons == true) {
|
||||
uploadButton.setOpenDocumentClickListener(mExternalFileHelper)
|
||||
} else {
|
||||
uploadButton.visibility = View.GONE
|
||||
}
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
findViewById<ViewGroup>(R.id.icon_picker_container)
|
||||
?.resetAppTimeoutWhenViewFocusedOrChanged(this, database)
|
||||
}
|
||||
|
||||
private fun updateIconsSelectedViews() {
|
||||
if (mIconsSelected.isEmpty()) {
|
||||
mCustomIconsSelectionMode = false
|
||||
|
||||
@@ -33,11 +33,15 @@ import com.igreenwood.loupe.Loupe
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.tasks.BinaryDatabaseManager
|
||||
import kotlin.math.max
|
||||
|
||||
class ImageViewerActivity : LockingActivity() {
|
||||
|
||||
private lateinit var imageView: ImageView
|
||||
private lateinit var progressView: View
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -49,43 +53,8 @@ class ImageViewerActivity : LockingActivity() {
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
val imageContainerView: ViewGroup = findViewById(R.id.image_viewer_container)
|
||||
val imageView: ImageView = findViewById(R.id.image_viewer_image)
|
||||
val progressView: View = findViewById(R.id.image_viewer_progress)
|
||||
|
||||
// Approximately, to not OOM and allow a zoom
|
||||
val mImagePreviewMaxWidth = max(
|
||||
resources.displayMetrics.widthPixels * 2,
|
||||
resources.displayMetrics.heightPixels * 2
|
||||
)
|
||||
|
||||
try {
|
||||
progressView.visibility = View.VISIBLE
|
||||
intent.getParcelableExtra<Attachment>(IMAGE_ATTACHMENT_TAG)?.let { attachment ->
|
||||
|
||||
supportActionBar?.title = attachment.name
|
||||
|
||||
val size = attachment.binaryData.getSize()
|
||||
supportActionBar?.subtitle = Formatter.formatFileSize(this, size)
|
||||
|
||||
mDatabase?.let { database ->
|
||||
BinaryDatabaseManager.loadBitmap(
|
||||
database,
|
||||
attachment.binaryData,
|
||||
mImagePreviewMaxWidth
|
||||
) { bitmapLoaded ->
|
||||
if (bitmapLoaded == null) {
|
||||
finish()
|
||||
} else {
|
||||
progressView.visibility = View.GONE
|
||||
imageView.setImageBitmap(bitmapLoaded)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: finish()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to view the binary", e)
|
||||
finish()
|
||||
}
|
||||
imageView = findViewById(R.id.image_viewer_image)
|
||||
progressView = findViewById(R.id.image_viewer_progress)
|
||||
|
||||
Loupe.create(imageView, imageContainerView) {
|
||||
onViewTranslateListener = object : Loupe.OnViewTranslateListener {
|
||||
@@ -110,6 +79,45 @@ class ImageViewerActivity : LockingActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
|
||||
try {
|
||||
progressView.visibility = View.VISIBLE
|
||||
intent.getParcelableExtra<Attachment>(IMAGE_ATTACHMENT_TAG)?.let { attachment ->
|
||||
|
||||
supportActionBar?.title = attachment.name
|
||||
|
||||
val size = attachment.binaryData.getSize()
|
||||
supportActionBar?.subtitle = Formatter.formatFileSize(this, size)
|
||||
|
||||
// Approximately, to not OOM and allow a zoom
|
||||
val mImagePreviewMaxWidth = max(
|
||||
resources.displayMetrics.widthPixels * 2,
|
||||
resources.displayMetrics.heightPixels * 2
|
||||
)
|
||||
|
||||
database?.let { database ->
|
||||
BinaryDatabaseManager.loadBitmap(
|
||||
database,
|
||||
attachment.binaryData,
|
||||
mImagePreviewMaxWidth
|
||||
) { bitmapLoaded ->
|
||||
if (bitmapLoaded == null) {
|
||||
finish()
|
||||
} else {
|
||||
progressView.visibility = View.GONE
|
||||
imageView.setImageBitmap(bitmapLoaded)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: finish()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to view the binary", e)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> finish()
|
||||
|
||||
@@ -19,22 +19,20 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.kunzisoft.keepass.activities.selection.DatabaseActivity
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.search.SearchHelper
|
||||
|
||||
/**
|
||||
* Activity to select entry in database and populate it in Magikeyboard
|
||||
*/
|
||||
class MagikeyboardLauncherActivity : AppCompatActivity() {
|
||||
class MagikeyboardLauncherActivity : DatabaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
val database = Database.getInstance()
|
||||
|
||||
val readOnly = database.isReadOnly
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
database?.let {
|
||||
val readOnly = database.isReadOnly
|
||||
SearchHelper.checkAutoSearchInfo(this,
|
||||
database,
|
||||
null,
|
||||
{
|
||||
@@ -49,8 +47,8 @@ class MagikeyboardLauncherActivity : AppCompatActivity() {
|
||||
// Pass extra to get entry
|
||||
FileDatabaseSelectActivity.launchForKeyboardSelectionResult(this)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
finish()
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
import com.kunzisoft.keepass.autofill.AutofillComponent
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockFragment
|
||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
|
||||
import com.kunzisoft.keepass.database.exception.FileNotFoundDatabaseException
|
||||
@@ -63,6 +62,7 @@ import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.MAIN_CREDENTIAL_KEY
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.READ_ONLY_KEY
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
|
||||
import com.kunzisoft.keepass.utils.MenuUtil
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
@@ -87,8 +87,6 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
|
||||
private val databaseFileViewModel: DatabaseFileViewModel by viewModels()
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
private var mDefaultDatabase: Boolean = false
|
||||
private var mDatabaseFileUri: Uri? = null
|
||||
private var mDatabaseKeyFileUri: Uri? = null
|
||||
@@ -109,15 +107,11 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
field = value
|
||||
}
|
||||
|
||||
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
|
||||
private var mAllowAutoOpenBiometricPrompt: Boolean = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
setContentView(R.layout.activity_password)
|
||||
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
@@ -208,74 +202,107 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
|
||||
onDatabaseFileLoaded(databaseFile?.databaseUri, keyFileUri)
|
||||
}
|
||||
}
|
||||
|
||||
mProgressDatabaseTaskProvider = ProgressDatabaseTaskProvider(this).apply {
|
||||
onActionFinish = { actionTask, result ->
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_LOAD_TASK -> {
|
||||
// Recheck advanced unlock if error
|
||||
advancedUnlockFragment?.initAdvancedUnlockMode()
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (result.isSuccess) {
|
||||
mDatabaseKeyFileUri = null
|
||||
clearCredentialsViews(true)
|
||||
mDatabase?.let { database ->
|
||||
launchGroupActivity(database)
|
||||
}
|
||||
} else {
|
||||
var resultError = ""
|
||||
val resultException = result.exception
|
||||
val resultMessage = result.message
|
||||
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this@PasswordActivity)
|
||||
|
||||
if (resultException != null) {
|
||||
resultError = resultException.getLocalizedMessage(resources)
|
||||
// Back to previous keyboard is setting activated
|
||||
if (PreferencesUtil.isKeyboardPreviousDatabaseCredentialsEnable(this@PasswordActivity)) {
|
||||
sendBroadcast(Intent(BACK_PREVIOUS_KEYBOARD_ACTION))
|
||||
}
|
||||
|
||||
when (resultException) {
|
||||
is DuplicateUuidDatabaseException -> {
|
||||
// Relaunch loading if we need to fix UUID
|
||||
showLoadDatabaseDuplicateUuidMessage {
|
||||
// Don't allow auto open prompt if lock become when UI visible
|
||||
mAllowAutoOpenBiometricPrompt = if (LockingActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK == true)
|
||||
false
|
||||
else
|
||||
mAllowAutoOpenBiometricPrompt
|
||||
mDatabaseFileUri?.let { databaseFileUri ->
|
||||
databaseFileViewModel.loadDatabaseFile(databaseFileUri)
|
||||
}
|
||||
|
||||
var databaseUri: Uri? = null
|
||||
var mainCredential: MainCredential = MainCredential()
|
||||
var readOnly = true
|
||||
var cipherEntity: CipherDatabaseEntity? = null
|
||||
checkPermission()
|
||||
}
|
||||
|
||||
result.data?.let { resultData ->
|
||||
databaseUri = resultData.getParcelable(DATABASE_URI_KEY)
|
||||
mainCredential = resultData.getParcelable(MAIN_CREDENTIAL_KEY) ?: mainCredential
|
||||
readOnly = resultData.getBoolean(READ_ONLY_KEY)
|
||||
cipherEntity = resultData.getParcelable(CIPHER_ENTITY_KEY)
|
||||
}
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
if (database?.loaded == true) {
|
||||
// If the database isn't accessible make sure to clear the password field, if it
|
||||
// was saved in the instance state
|
||||
clearCredentialsViews()
|
||||
launchGroupActivity(database)
|
||||
}
|
||||
}
|
||||
|
||||
databaseUri?.let { databaseFileUri ->
|
||||
showProgressDialogAndLoadDatabase(
|
||||
databaseFileUri,
|
||||
mainCredential,
|
||||
readOnly,
|
||||
cipherEntity,
|
||||
true)
|
||||
}
|
||||
}
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_LOAD_TASK -> {
|
||||
// Recheck advanced unlock if error
|
||||
advancedUnlockFragment?.initAdvancedUnlockMode()
|
||||
|
||||
if (result.isSuccess) {
|
||||
mDatabaseKeyFileUri = null
|
||||
clearCredentialsViews(true)
|
||||
launchGroupActivity(database)
|
||||
} else {
|
||||
var resultError = ""
|
||||
val resultException = result.exception
|
||||
val resultMessage = result.message
|
||||
|
||||
if (resultException != null) {
|
||||
resultError = resultException.getLocalizedMessage(resources)
|
||||
|
||||
when (resultException) {
|
||||
is DuplicateUuidDatabaseException -> {
|
||||
// Relaunch loading if we need to fix UUID
|
||||
showLoadDatabaseDuplicateUuidMessage {
|
||||
|
||||
var databaseUri: Uri? = null
|
||||
var mainCredential: MainCredential = MainCredential()
|
||||
var readOnly = true
|
||||
var cipherEntity: CipherDatabaseEntity? = null
|
||||
|
||||
result.data?.let { resultData ->
|
||||
databaseUri = resultData.getParcelable(DATABASE_URI_KEY)
|
||||
mainCredential = resultData.getParcelable(MAIN_CREDENTIAL_KEY) ?: mainCredential
|
||||
readOnly = resultData.getBoolean(READ_ONLY_KEY)
|
||||
cipherEntity = resultData.getParcelable(CIPHER_ENTITY_KEY)
|
||||
}
|
||||
is FileNotFoundDatabaseException -> {
|
||||
// Remove this default database inaccessible
|
||||
if (mDefaultDatabase) {
|
||||
databaseFileViewModel.removeDefaultDatabase()
|
||||
}
|
||||
|
||||
databaseUri?.let { databaseFileUri ->
|
||||
showProgressDialogAndLoadDatabase(
|
||||
databaseFileUri,
|
||||
mainCredential,
|
||||
readOnly,
|
||||
cipherEntity,
|
||||
true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show error message
|
||||
if (resultMessage != null && resultMessage.isNotEmpty()) {
|
||||
resultError = "$resultError $resultMessage"
|
||||
is FileNotFoundDatabaseException -> {
|
||||
// Remove this default database inaccessible
|
||||
if (mDefaultDatabase) {
|
||||
databaseFileViewModel.removeDefaultDatabase()
|
||||
}
|
||||
}
|
||||
Log.e(TAG, resultError)
|
||||
Snackbar.make(coordinatorLayout,
|
||||
resultError,
|
||||
Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
}
|
||||
|
||||
// Show error message
|
||||
if (resultMessage != null && resultMessage.isNotEmpty()) {
|
||||
resultError = "$resultError $resultMessage"
|
||||
}
|
||||
Log.e(TAG, resultError)
|
||||
Snackbar.make(coordinatorLayout,
|
||||
resultError,
|
||||
Snackbar.LENGTH_LONG).asError().show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,41 +386,6 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val database = mDatabase
|
||||
if (database?.loaded == true) {
|
||||
launchGroupActivity(database)
|
||||
} else {
|
||||
mRememberKeyFile = PreferencesUtil.rememberKeyFileLocations(this)
|
||||
|
||||
// If the database isn't accessible make sure to clear the password field, if it
|
||||
// was saved in the instance state
|
||||
if (mDatabase?.loaded == true) {
|
||||
clearCredentialsViews()
|
||||
}
|
||||
|
||||
mProgressDatabaseTaskProvider?.registerProgressTask()
|
||||
|
||||
// Back to previous keyboard is setting activated
|
||||
if (PreferencesUtil.isKeyboardPreviousDatabaseCredentialsEnable(this)) {
|
||||
sendBroadcast(Intent(BACK_PREVIOUS_KEYBOARD_ACTION))
|
||||
}
|
||||
|
||||
// Don't allow auto open prompt if lock become when UI visible
|
||||
mAllowAutoOpenBiometricPrompt = if (LockingActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK == true)
|
||||
false
|
||||
else
|
||||
mAllowAutoOpenBiometricPrompt
|
||||
mDatabaseFileUri?.let { databaseFileUri ->
|
||||
databaseFileViewModel.loadDatabaseFile(databaseFileUri)
|
||||
}
|
||||
|
||||
checkPermission()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDatabaseFileLoaded(databaseFileUri: Uri?, keyFileUri: Uri?) {
|
||||
// Define Key File text
|
||||
if (mRememberKeyFile) {
|
||||
@@ -417,8 +409,8 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
} else {
|
||||
// Init Biometric elements
|
||||
advancedUnlockFragment?.loadDatabase(databaseFileUri,
|
||||
mAllowAutoOpenBiometricPrompt
|
||||
&& mProgressDatabaseTaskProvider?.isBinded() != true)
|
||||
mAllowAutoOpenBiometricPrompt)
|
||||
// TODO && mDatabaseTaskProvider?.isBinded() != true)
|
||||
}
|
||||
|
||||
enableOrNotTheConfirmationButton()
|
||||
@@ -468,8 +460,6 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mProgressDatabaseTaskProvider?.unregisterProgressTask()
|
||||
|
||||
// Reinit locking activity UI variable
|
||||
LockingActivity.LOCKING_ACTIVITY_UI_VISIBLE_DURING_LOCK = null
|
||||
mAllowAutoOpenBiometricPrompt = true
|
||||
@@ -546,7 +536,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
readOnly: Boolean,
|
||||
cipherDatabaseEntity: CipherDatabaseEntity?,
|
||||
fixDuplicateUUID: Boolean) {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseLoad(
|
||||
loadDatabase(
|
||||
databaseUri,
|
||||
mainCredential,
|
||||
readOnly,
|
||||
@@ -716,7 +706,8 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
|
||||
when (resultCode) {
|
||||
LockingActivity.RESULT_EXIT_LOCK -> {
|
||||
clearCredentialsViews()
|
||||
mDatabase?.clearAndClose(this)
|
||||
// TODO Database
|
||||
Database.getInstance().clearAndClose(this)
|
||||
}
|
||||
Activity.RESULT_CANCELED -> {
|
||||
clearCredentialsViews()
|
||||
|
||||
@@ -57,17 +57,17 @@ open class DeleteNodesDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
|
||||
val database = Database.getInstance()
|
||||
|
||||
arguments?.apply {
|
||||
if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY)
|
||||
&& containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) {
|
||||
mNodesToDelete = getListNodesFromBundle(database, this)
|
||||
// TODO Database
|
||||
mNodesToDelete = getListNodesFromBundle(Database.getInstance(), this)
|
||||
}
|
||||
} ?: savedInstanceState?.apply {
|
||||
if (containsKey(DatabaseTaskNotificationService.GROUPS_ID_KEY)
|
||||
&& containsKey(DatabaseTaskNotificationService.ENTRIES_ID_KEY)) {
|
||||
mNodesToDelete = getListNodesFromBundle(database, savedInstanceState)
|
||||
// TODO Database
|
||||
mNodesToDelete = getListNodesFromBundle(Database.getInstance(), savedInstanceState)
|
||||
}
|
||||
}
|
||||
activity?.let { activity ->
|
||||
|
||||
@@ -43,8 +43,6 @@ import org.joda.time.DateTime
|
||||
|
||||
class GroupEditDialogFragment : DialogFragment() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
private var mEditGroupListener: EditGroupListener? = null
|
||||
|
||||
private var mEditGroupDialogAction = EditGroupDialogAction.NONE
|
||||
@@ -101,9 +99,6 @@ class GroupEditDialogFragment : DialogFragment() {
|
||||
iconColor = ta.getColor(0, Color.WHITE)
|
||||
ta.recycle()
|
||||
|
||||
// Init elements
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.containsKey(KEY_ACTION_ID)
|
||||
&& savedInstanceState.containsKey(KEY_GROUP_INFO)) {
|
||||
@@ -203,7 +198,8 @@ class GroupEditDialogFragment : DialogFragment() {
|
||||
}
|
||||
|
||||
private fun assignIconView() {
|
||||
mDatabase?.iconDrawableFactory?.assignDatabaseIcon(iconButtonView, mGroupInfo.icon, iconColor)
|
||||
// TODO Database
|
||||
Database.getInstance()?.iconDrawableFactory?.assignDatabaseIcon(iconButtonView, mGroupInfo.icon, iconColor)
|
||||
}
|
||||
|
||||
fun setIcon(icon: IconImage) {
|
||||
|
||||
@@ -1,15 +1,34 @@
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.kunzisoft.keepass.activities.DatabaseRetrieval
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.activities.stylish.StylishFragment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
abstract class DatabaseFragment : StylishFragment() {
|
||||
abstract class DatabaseFragment : StylishFragment(), DatabaseRetrieval {
|
||||
|
||||
protected var mDatabase: Database? = null
|
||||
private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
mDatabase = Database.getInstance()
|
||||
override fun onViewCreated(view: View,
|
||||
savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mDatabaseViewModel.database.observe(viewLifecycleOwner) { database ->
|
||||
view.resetAppTimeoutWhenViewFocusedOrChanged(requireContext(), database)
|
||||
onDatabaseRetrieved(database)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
// Can be overridden by a subclass
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import com.kunzisoft.keepass.activities.dialogs.SetOTPDialogFragment
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
import com.kunzisoft.keepass.model.AttachmentState
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
@@ -62,6 +63,8 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
private lateinit var attachmentsListView: RecyclerView
|
||||
private var attachmentsAdapter: EntryAttachmentsItemsAdapter? = null
|
||||
|
||||
private var mAllowMultipleAttachments: Boolean = false
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
@@ -89,8 +92,6 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
attachmentsContainerView = view.findViewById(R.id.entry_attachments_container)
|
||||
attachmentsListView = view.findViewById(R.id.entry_attachments_list)
|
||||
|
||||
view.resetAppTimeoutWhenViewFocusedOrChanged(requireContext(), mDatabase)
|
||||
|
||||
templateView.apply {
|
||||
populateIconMethod = { imageView, icon ->
|
||||
drawFactory?.assignDatabaseIcon(imageView, icon, iconColor)
|
||||
@@ -109,20 +110,6 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
attachmentsAdapter = EntryAttachmentsItemsAdapter(requireContext())
|
||||
attachmentsAdapter?.database = mDatabase
|
||||
attachmentsAdapter?.onListSizeChangedListener = { previousSize, newSize ->
|
||||
if (previousSize > 0 && newSize == 0) {
|
||||
attachmentsContainerView.collapse(true)
|
||||
} else if (previousSize == 0 && newSize == 1) {
|
||||
attachmentsContainerView.expand(true)
|
||||
}
|
||||
}
|
||||
attachmentsListView.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = attachmentsAdapter
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
if (savedInstanceState != null) {
|
||||
val attachments: List<Attachment> =
|
||||
savedInstanceState.getParcelableArrayList(ATTACHMENTS_TAG) ?: listOf()
|
||||
@@ -206,10 +193,12 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
mEntryEditViewModel.onBuildNewAttachment.observe(viewLifecycleOwner) {
|
||||
val attachmentToUploadUri = it.attachmentToUploadUri
|
||||
val fileName = it.fileName
|
||||
mDatabase?.buildNewBinaryAttachment()?.let { binaryAttachment ->
|
||||
|
||||
// TODO Database
|
||||
Database.getInstance()?.buildNewBinaryAttachment()?.let { binaryAttachment ->
|
||||
val entryAttachment = Attachment(fileName, binaryAttachment)
|
||||
// Ask to replace the current attachment
|
||||
if ((mDatabase?.allowMultipleAttachments == false
|
||||
if ((!mAllowMultipleAttachments
|
||||
&& containsAttachment()) ||
|
||||
containsAttachment(EntryAttachmentState(entryAttachment, StreamDirection.UPLOAD))) {
|
||||
ReplaceFileDialogFragment.build(attachmentToUploadUri, entryAttachment)
|
||||
@@ -249,16 +238,25 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
drawFactory = database?.iconDrawableFactory
|
||||
|
||||
drawFactory = mDatabase?.iconDrawableFactory
|
||||
}
|
||||
mAllowMultipleAttachments = database?.allowMultipleAttachments == true
|
||||
|
||||
override fun onDetach() {
|
||||
super.onDetach()
|
||||
|
||||
drawFactory = null
|
||||
attachmentsAdapter = EntryAttachmentsItemsAdapter(requireContext())
|
||||
attachmentsAdapter?.database = database
|
||||
attachmentsAdapter?.onListSizeChangedListener = { previousSize, newSize ->
|
||||
if (previousSize > 0 && newSize == 0) {
|
||||
attachmentsContainerView.collapse(true)
|
||||
} else if (previousSize == 0 && newSize == 1) {
|
||||
attachmentsContainerView.expand(true)
|
||||
}
|
||||
}
|
||||
attachmentsListView.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = attachmentsAdapter
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignEntryInfo(entryInfo: EntryInfo?) {
|
||||
@@ -307,7 +305,7 @@ class EntryEditFragment: DatabaseFragment() {
|
||||
private fun putAttachment(attachment: EntryAttachmentState,
|
||||
onPreviewLoaded: (() -> Unit)? = null) {
|
||||
// When only one attachment is allowed
|
||||
if (mDatabase?.allowMultipleAttachments == false) {
|
||||
if (mAllowMultipleAttachments) {
|
||||
clearAttachments()
|
||||
}
|
||||
attachmentsContainerView.visibility = View.VISIBLE
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.EntryAttachmentsItemsAdapter
|
||||
import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||
import com.kunzisoft.keepass.model.EntryAttachmentState
|
||||
@@ -63,8 +64,6 @@ class EntryFragment: DatabaseFragment() {
|
||||
|
||||
context?.let { context ->
|
||||
mClipboardHelper = ClipboardHelper(context)
|
||||
attachmentsAdapter = EntryAttachmentsItemsAdapter(context)
|
||||
attachmentsAdapter?.database = mDatabase
|
||||
}
|
||||
|
||||
rootView = view
|
||||
@@ -75,14 +74,6 @@ class EntryFragment: DatabaseFragment() {
|
||||
templateView = view.findViewById(R.id.entry_template)
|
||||
loadTemplateSettings()
|
||||
|
||||
attachmentsContainerView = view.findViewById(R.id.entry_attachments_container)
|
||||
attachmentsListView = view.findViewById(R.id.entry_attachments_list)
|
||||
attachmentsListView.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = attachmentsAdapter
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
|
||||
creationDateView = view.findViewById(R.id.entry_created)
|
||||
modificationDateView = view.findViewById(R.id.entry_modified)
|
||||
|
||||
@@ -112,6 +103,21 @@ class EntryFragment: DatabaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
context?.let { context ->
|
||||
attachmentsAdapter = EntryAttachmentsItemsAdapter(context)
|
||||
attachmentsAdapter?.database = database
|
||||
}
|
||||
|
||||
attachmentsContainerView = requireView().findViewById(R.id.entry_attachments_container)
|
||||
attachmentsListView = requireView().findViewById(R.id.entry_attachments_list)
|
||||
attachmentsListView.apply {
|
||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
adapter = attachmentsAdapter
|
||||
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTemplateSettings() {
|
||||
context?.let { context ->
|
||||
templateView.setFirstTimeAskAllowCopyProtectedFields(PreferencesUtil.isFirstTimeAskAllowCopyProtectedFields(context))
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.kunzisoft.keepass.activities.fragments
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageCustom
|
||||
|
||||
|
||||
@@ -31,8 +32,8 @@ class IconCustomFragment : IconFragment<IconImageCustom>() {
|
||||
return R.layout.fragment_icon_grid
|
||||
}
|
||||
|
||||
override fun defineIconList() {
|
||||
mDatabase?.doForEachCustomIcons { customIcon, _ ->
|
||||
override fun defineIconList(database: Database?) {
|
||||
database?.doForEachCustomIcons { customIcon, _ ->
|
||||
iconPickerAdapter.addIcon(customIcon, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.IconPickerAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageDraw
|
||||
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -47,31 +48,7 @@ abstract class IconFragment<T: IconImageDraw> : DatabaseFragment(),
|
||||
|
||||
abstract fun retrieveMainLayoutId(): Int
|
||||
|
||||
abstract fun defineIconList()
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
// Retrieve the textColor to tint the icon
|
||||
val ta = contextThemed?.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
|
||||
val tintColor = ta?.getColor(0, Color.BLACK) ?: Color.BLACK
|
||||
ta?.recycle()
|
||||
|
||||
iconPickerAdapter = IconPickerAdapter<T>(context, tintColor).apply {
|
||||
iconDrawableFactory = mDatabase?.iconDrawableFactory
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val populateList = launch {
|
||||
iconPickerAdapter.clear()
|
||||
defineIconList()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
populateList.join()
|
||||
iconPickerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
abstract fun defineIconList(database: Database?)
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@@ -88,6 +65,28 @@ abstract class IconFragment<T: IconImageDraw> : DatabaseFragment(),
|
||||
iconPickerAdapter.iconPickerListener = this
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
// Retrieve the textColor to tint the icon
|
||||
val ta = contextThemed?.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
|
||||
val tintColor = ta?.getColor(0, Color.BLACK) ?: Color.BLACK
|
||||
ta?.recycle()
|
||||
|
||||
iconPickerAdapter = IconPickerAdapter<T>(requireContext(), tintColor).apply {
|
||||
iconDrawableFactory = database?.iconDrawableFactory
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val populateList = launch {
|
||||
iconPickerAdapter.clear()
|
||||
defineIconList(database)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
populateList.join()
|
||||
iconPickerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onIconDeleteClicked() {
|
||||
iconActionSelectionMode = false
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.adapters.IconPickerPagerAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.viewmodels.IconPickerViewModel
|
||||
|
||||
class IconPickerFragment : DatabaseFragment() {
|
||||
@@ -28,17 +29,7 @@ class IconPickerFragment : DatabaseFragment() {
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
viewPager = view.findViewById(R.id.icon_picker_pager)
|
||||
val tabLayout = view.findViewById<TabLayout>(R.id.icon_picker_tabs)
|
||||
iconPickerPagerAdapter = IconPickerPagerAdapter(this,
|
||||
if (mDatabase?.allowCustomIcons == true) 2 else 1)
|
||||
viewPager.adapter = iconPickerPagerAdapter
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
tab.text = when (position) {
|
||||
1 -> getString(R.string.icon_section_custom)
|
||||
else -> getString(R.string.icon_section_standard)
|
||||
}
|
||||
}.attach()
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
arguments?.apply {
|
||||
if (containsKey(ICON_TAB_ARG)) {
|
||||
@@ -52,6 +43,20 @@ class IconPickerFragment : DatabaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
viewPager = requireView().findViewById(R.id.icon_picker_pager)
|
||||
val tabLayout = requireView().findViewById<TabLayout>(R.id.icon_picker_tabs)
|
||||
iconPickerPagerAdapter = IconPickerPagerAdapter(this,
|
||||
if (database?.allowCustomIcons == true) 2 else 1)
|
||||
viewPager.adapter = iconPickerPagerAdapter
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
tab.text = when (position) {
|
||||
1 -> getString(R.string.icon_section_custom)
|
||||
else -> getString(R.string.icon_section_standard)
|
||||
}
|
||||
}.attach()
|
||||
}
|
||||
|
||||
enum class IconTab {
|
||||
STANDARD, CUSTOM
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.kunzisoft.keepass.activities.fragments
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.icon.IconImageStandard
|
||||
|
||||
|
||||
@@ -29,8 +30,8 @@ class IconStandardFragment : IconFragment<IconImageStandard>() {
|
||||
return R.layout.fragment_icon_grid
|
||||
}
|
||||
|
||||
override fun defineIconList() {
|
||||
mDatabase?.doForEachStandardIcons { standardIcon ->
|
||||
override fun defineIconList(database: Database?) {
|
||||
database?.doForEachStandardIcons { standardIcon ->
|
||||
iconPickerAdapter.addIcon(standardIcon, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kunzisoft.keepass.R
|
||||
@@ -34,11 +35,13 @@ import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.adapters.NodeAdapter
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.SortNodeEnum
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.Type
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.viewmodels.GroupViewModel
|
||||
import java.util.*
|
||||
|
||||
class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionListener {
|
||||
@@ -47,10 +50,12 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
private var onScrollListener: OnScrollListener? = null
|
||||
|
||||
private var mNodesRecyclerView: RecyclerView? = null
|
||||
var mainGroup: Group? = null
|
||||
private set
|
||||
private var mAdapter: NodeAdapter? = null
|
||||
|
||||
private val mGroupViewModel: GroupViewModel by activityViewModels()
|
||||
|
||||
private var mCurrentGroup: Group? = null
|
||||
|
||||
var nodeActionSelectionMode = false
|
||||
private set
|
||||
var nodeActionPasteMode: PasteMode = PasteMode.UNDEFINED
|
||||
@@ -64,6 +69,9 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
private var readOnly: Boolean = false
|
||||
private var specialMode: SpecialMode = SpecialMode.DEFAULT
|
||||
|
||||
private var mRecycleBinEnable: Boolean = false
|
||||
private var mRecycleBin: Group? = null
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = mAdapter == null || mAdapter?.itemCount?:0 <= 0
|
||||
|
||||
@@ -99,56 +107,63 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments)
|
||||
}
|
||||
|
||||
arguments?.let { args ->
|
||||
// Contains all the group in element
|
||||
if (args.containsKey(GROUP_KEY)) {
|
||||
mainGroup = args.getParcelable(GROUP_KEY)
|
||||
}
|
||||
if (args.containsKey(IS_SEARCH)) {
|
||||
isASearchResult = args.getBoolean(IS_SEARCH)
|
||||
}
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mGroupViewModel.group.observe(viewLifecycleOwner) {
|
||||
mCurrentGroup = it.group
|
||||
isASearchResult = it.group.isVirtual
|
||||
rebuildList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
mRecycleBinEnable = database?.isRecycleBinEnabled == true
|
||||
mRecycleBin = database?.recycleBin
|
||||
|
||||
contextThemed?.let { context ->
|
||||
mDatabase?.let { database ->
|
||||
mAdapter = NodeAdapter(context, database)
|
||||
}
|
||||
mAdapter?.apply {
|
||||
setOnNodeClickListener(object : NodeAdapter.NodeClickCallback {
|
||||
override fun onNodeClick(node: Node) {
|
||||
if (nodeActionSelectionMode) {
|
||||
if (listActionNodes.contains(node)) {
|
||||
// Remove selected item if already selected
|
||||
listActionNodes.remove(node)
|
||||
database?.let { database ->
|
||||
mAdapter = NodeAdapter(context, database).apply {
|
||||
setOnNodeClickListener(object : NodeAdapter.NodeClickCallback {
|
||||
override fun onNodeClick(database: Database, node: Node) {
|
||||
if (nodeActionSelectionMode) {
|
||||
if (listActionNodes.contains(node)) {
|
||||
// Remove selected item if already selected
|
||||
listActionNodes.remove(node)
|
||||
} else {
|
||||
// Add selected item if not already selected
|
||||
listActionNodes.add(node)
|
||||
}
|
||||
nodeClickListener?.onNodeSelected(database, listActionNodes)
|
||||
setActionNodes(listActionNodes)
|
||||
notifyNodeChanged(node)
|
||||
} else {
|
||||
// Add selected item if not already selected
|
||||
listActionNodes.add(node)
|
||||
nodeClickListener?.onNodeClick(database, node)
|
||||
}
|
||||
nodeClickListener?.onNodeSelected(listActionNodes)
|
||||
setActionNodes(listActionNodes)
|
||||
notifyNodeChanged(node)
|
||||
} else {
|
||||
nodeClickListener?.onNodeClick(node)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNodeLongClick(node: Node): Boolean {
|
||||
if (nodeActionPasteMode == PasteMode.UNDEFINED) {
|
||||
// Select the first item after a long click
|
||||
if (!listActionNodes.contains(node))
|
||||
listActionNodes.add(node)
|
||||
override fun onNodeLongClick(database: Database, node: Node): Boolean {
|
||||
if (nodeActionPasteMode == PasteMode.UNDEFINED) {
|
||||
// Select the first item after a long click
|
||||
if (!listActionNodes.contains(node))
|
||||
listActionNodes.add(node)
|
||||
|
||||
nodeClickListener?.onNodeSelected(listActionNodes)
|
||||
nodeClickListener?.onNodeSelected(database, listActionNodes)
|
||||
|
||||
setActionNodes(listActionNodes)
|
||||
notifyNodeChanged(node)
|
||||
setActionNodes(listActionNodes)
|
||||
notifyNodeChanged(node)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
mNodesRecyclerView?.adapter = mAdapter
|
||||
}
|
||||
}
|
||||
|
||||
// TODO notify menu updated?
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -211,7 +226,7 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun rebuildList() {
|
||||
// Add elements to the list
|
||||
mainGroup?.let { mainGroup ->
|
||||
mCurrentGroup?.let { mainGroup ->
|
||||
mAdapter?.apply {
|
||||
// Thrown an exception when sort cannot be performed
|
||||
rebuildList(mainGroup)
|
||||
@@ -253,7 +268,7 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
R.id.menu_sort -> {
|
||||
context?.let { context ->
|
||||
val sortDialogFragment: SortDialogFragment =
|
||||
if (mDatabase?.isRecycleBinEnabled == true) {
|
||||
if (mRecycleBinEnable) {
|
||||
SortDialogFragment.getInstance(
|
||||
PreferencesUtil.getListSort(context),
|
||||
PreferencesUtil.getAscendingSort(context),
|
||||
@@ -275,7 +290,8 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
}
|
||||
}
|
||||
|
||||
fun actionNodesCallback(nodes: List<Node>,
|
||||
fun actionNodesCallback(database: Database,
|
||||
nodes: List<Node>,
|
||||
menuListener: NodesActionMenuListener?,
|
||||
actionModeCallback: ActionMode.Callback) : ActionMode.Callback {
|
||||
|
||||
@@ -289,49 +305,46 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||
menu?.clear()
|
||||
if (nodeActionPasteMode != PasteMode.UNDEFINED) {
|
||||
mode?.menuInflater?.inflate(R.menu.node_paste_menu, menu)
|
||||
} else {
|
||||
nodeActionSelectionMode = true
|
||||
mode?.menuInflater?.inflate(R.menu.node_menu, menu)
|
||||
|
||||
mDatabase?.let { database ->
|
||||
if (nodeActionPasteMode != PasteMode.UNDEFINED) {
|
||||
mode?.menuInflater?.inflate(R.menu.node_paste_menu, menu)
|
||||
} else {
|
||||
nodeActionSelectionMode = true
|
||||
mode?.menuInflater?.inflate(R.menu.node_menu, menu)
|
||||
|
||||
// Open and Edit for a single item
|
||||
if (nodes.size == 1) {
|
||||
// Edition
|
||||
if (readOnly
|
||||
|| (database.isRecycleBinEnabled && nodes[0] == database.recycleBin)) {
|
||||
menu?.removeItem(R.id.menu_edit)
|
||||
}
|
||||
} else {
|
||||
menu?.removeItem(R.id.menu_open)
|
||||
// Open and Edit for a single item
|
||||
if (nodes.size == 1) {
|
||||
// Edition
|
||||
if (readOnly
|
||||
|| (mRecycleBinEnable && nodes[0] == mRecycleBin)) {
|
||||
menu?.removeItem(R.id.menu_edit)
|
||||
}
|
||||
|
||||
// Move
|
||||
if (readOnly
|
||||
|| isASearchResult) {
|
||||
menu?.removeItem(R.id.menu_move)
|
||||
}
|
||||
|
||||
// Copy (not allowed for group)
|
||||
if (readOnly
|
||||
|| isASearchResult
|
||||
|| nodes.any { it.type == Type.GROUP }) {
|
||||
menu?.removeItem(R.id.menu_copy)
|
||||
}
|
||||
|
||||
// Deletion
|
||||
if (readOnly
|
||||
|| (database.isRecycleBinEnabled && nodes.any { it == database.recycleBin })) {
|
||||
menu?.removeItem(R.id.menu_delete)
|
||||
}
|
||||
} else {
|
||||
menu?.removeItem(R.id.menu_open)
|
||||
menu?.removeItem(R.id.menu_edit)
|
||||
}
|
||||
|
||||
// Add the number of items selected in title
|
||||
mode?.title = nodes.size.toString()
|
||||
// Move
|
||||
if (readOnly
|
||||
|| isASearchResult) {
|
||||
menu?.removeItem(R.id.menu_move)
|
||||
}
|
||||
|
||||
// Copy (not allowed for group)
|
||||
if (readOnly
|
||||
|| isASearchResult
|
||||
|| nodes.any { it.type == Type.GROUP }) {
|
||||
menu?.removeItem(R.id.menu_copy)
|
||||
}
|
||||
|
||||
// Deletion
|
||||
if (readOnly
|
||||
|| (mRecycleBinEnable && nodes.any { it == mRecycleBin })) {
|
||||
menu?.removeItem(R.id.menu_delete)
|
||||
}
|
||||
}
|
||||
|
||||
// Add the number of items selected in title
|
||||
mode?.title = nodes.size.toString()
|
||||
return actionModeCallback.onPrepareActionMode(mode, menu)
|
||||
}
|
||||
|
||||
@@ -339,25 +352,25 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
if (menuListener == null)
|
||||
return false
|
||||
return when (item?.itemId) {
|
||||
R.id.menu_open -> menuListener.onOpenMenuClick(nodes[0])
|
||||
R.id.menu_edit -> menuListener.onEditMenuClick(nodes[0])
|
||||
R.id.menu_open -> menuListener.onOpenMenuClick(database, nodes[0])
|
||||
R.id.menu_edit -> menuListener.onEditMenuClick(database, nodes[0])
|
||||
R.id.menu_copy -> {
|
||||
nodeActionPasteMode = PasteMode.PASTE_FROM_COPY
|
||||
mAdapter?.unselectActionNodes()
|
||||
val returnValue = menuListener.onCopyMenuClick(nodes)
|
||||
val returnValue = menuListener.onCopyMenuClick(database, nodes)
|
||||
nodeActionSelectionMode = false
|
||||
returnValue
|
||||
}
|
||||
R.id.menu_move -> {
|
||||
nodeActionPasteMode = PasteMode.PASTE_FROM_MOVE
|
||||
mAdapter?.unselectActionNodes()
|
||||
val returnValue = menuListener.onMoveMenuClick(nodes)
|
||||
val returnValue = menuListener.onMoveMenuClick(database, nodes)
|
||||
nodeActionSelectionMode = false
|
||||
returnValue
|
||||
}
|
||||
R.id.menu_delete -> menuListener.onDeleteMenuClick(nodes)
|
||||
R.id.menu_delete -> menuListener.onDeleteMenuClick(database, nodes)
|
||||
R.id.menu_paste -> {
|
||||
val returnValue = menuListener.onPasteMenuClick(nodeActionPasteMode, nodes)
|
||||
val returnValue = menuListener.onPasteMenuClick(database, nodeActionPasteMode, nodes)
|
||||
nodeActionPasteMode = PasteMode.UNDEFINED
|
||||
nodeActionSelectionMode = false
|
||||
returnValue
|
||||
@@ -435,20 +448,20 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
* Callback listener to redefine to do an action when a node is click
|
||||
*/
|
||||
interface NodeClickListener {
|
||||
fun onNodeClick(node: Node)
|
||||
fun onNodeSelected(nodes: List<Node>): Boolean
|
||||
fun onNodeClick(database: Database, node: Node)
|
||||
fun onNodeSelected(database: Database, nodes: List<Node>): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu listener to redefine to do an action in menu
|
||||
*/
|
||||
interface NodesActionMenuListener {
|
||||
fun onOpenMenuClick(node: Node): Boolean
|
||||
fun onEditMenuClick(node: Node): Boolean
|
||||
fun onCopyMenuClick(nodes: List<Node>): Boolean
|
||||
fun onMoveMenuClick(nodes: List<Node>): Boolean
|
||||
fun onDeleteMenuClick(nodes: List<Node>): Boolean
|
||||
fun onPasteMenuClick(pasteMode: PasteMode?, nodes: List<Node>): Boolean
|
||||
fun onOpenMenuClick(database: Database, node: Node): Boolean
|
||||
fun onEditMenuClick(database: Database, node: Node): Boolean
|
||||
fun onCopyMenuClick(database: Database, nodes: List<Node>): Boolean
|
||||
fun onMoveMenuClick(database: Database, nodes: List<Node>): Boolean
|
||||
fun onDeleteMenuClick(database: Database, nodes: List<Node>): Boolean
|
||||
fun onPasteMenuClick(database: Database, pasteMode: PasteMode?, nodes: List<Node>): Boolean
|
||||
}
|
||||
|
||||
enum class PasteMode {
|
||||
@@ -467,22 +480,6 @@ class ListNodesFragment : DatabaseFragment(), SortDialogFragment.SortSelectionLi
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = ListNodesFragment::class.java.name
|
||||
|
||||
private const val GROUP_KEY = "GROUP_KEY"
|
||||
private const val IS_SEARCH = "IS_SEARCH"
|
||||
|
||||
fun newInstance(group: Group?, readOnly: Boolean, isASearch: Boolean): ListNodesFragment {
|
||||
val bundle = Bundle()
|
||||
if (group != null) {
|
||||
bundle.putParcelable(GROUP_KEY, group)
|
||||
}
|
||||
bundle.putBoolean(IS_SEARCH, isASearch)
|
||||
ReadOnlyHelper.putReadOnlyInBundle(bundle, readOnly)
|
||||
val listNodesFragment = ListNodesFragment()
|
||||
listNodesFragment.arguments = bundle
|
||||
return listNodesFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ object ReadOnlyHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove read only
|
||||
fun retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState: Bundle?, arguments: Bundle?): Boolean {
|
||||
var readOnly = READ_ONLY_DEFAULT
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(READ_ONLY_KEY)) {
|
||||
|
||||
@@ -22,30 +22,43 @@ package com.kunzisoft.keepass.activities.lock
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.DeleteNodesDialogFragment
|
||||
import com.kunzisoft.keepass.activities.dialogs.EmptyRecycleBinDialogFragment
|
||||
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
|
||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
import com.kunzisoft.keepass.model.GroupInfo
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.utils.*
|
||||
|
||||
abstract class LockingActivity : SpecialModeActivity() {
|
||||
abstract class LockingActivity : SpecialModeActivity(),
|
||||
PasswordEncodingDialogFragment.Listener,
|
||||
DeleteNodesDialogFragment.DeleteNodeListener {
|
||||
|
||||
protected var mTimeoutEnable: Boolean = true
|
||||
|
||||
private var mLockReceiver: LockReceiver? = null
|
||||
private var mExitLock: Boolean = false
|
||||
|
||||
protected var mDatabase: Database? = null
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
// Force readOnly if Entry Selection mode
|
||||
protected var mReadOnly: Boolean
|
||||
@@ -58,23 +71,19 @@ abstract class LockingActivity : SpecialModeActivity() {
|
||||
private var mReadOnlyToSave: Boolean = false
|
||||
protected var mAutoSaveEnable: Boolean = true
|
||||
|
||||
var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
private set
|
||||
protected var mIconDrawableFactory: IconDrawableFactory? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
mProgressDatabaseTaskProvider = ProgressDatabaseTaskProvider(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (savedInstanceState != null
|
||||
&& savedInstanceState.containsKey(TIMEOUT_ENABLE_KEY)) {
|
||||
&& savedInstanceState.containsKey(TIMEOUT_ENABLE_KEY)
|
||||
) {
|
||||
mTimeoutEnable = savedInstanceState.getBoolean(TIMEOUT_ENABLE_KEY)
|
||||
} else {
|
||||
if (intent != null)
|
||||
mTimeoutEnable = intent.getBooleanExtra(TIMEOUT_ENABLE_KEY, TIMEOUT_ENABLE_KEY_DEFAULT)
|
||||
mTimeoutEnable =
|
||||
intent.getBooleanExtra(TIMEOUT_ENABLE_KEY, TIMEOUT_ENABLE_KEY_DEFAULT)
|
||||
}
|
||||
|
||||
if (mTimeoutEnable) {
|
||||
@@ -93,19 +102,193 @@ abstract class LockingActivity : SpecialModeActivity() {
|
||||
mExitLock = false
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == RESULT_EXIT_LOCK) {
|
||||
mExitLock = true
|
||||
if (mDatabase?.loaded == true) {
|
||||
lockAndExit()
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
mDatabase = database
|
||||
// End activity if database not loaded
|
||||
if (database?.loaded != true) {
|
||||
finish()
|
||||
}
|
||||
// check timeout
|
||||
if (mTimeoutEnable) {
|
||||
// After the first creation
|
||||
// or If simply swipe with another application
|
||||
// If the time is out -> close the Activity
|
||||
TimeoutHelper.checkTimeAndLockIfTimeout(this)
|
||||
// If onCreate already record time
|
||||
database?.let { it ->
|
||||
if (!mExitLock)
|
||||
TimeoutHelper.recordTime(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
// Force read only if the database is like that
|
||||
mReadOnly = database?.isReadOnly != false || mReadOnly
|
||||
mIconDrawableFactory = database?.iconDrawableFactory
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Reload the current activity
|
||||
if (result.isSuccess) {
|
||||
reload(database)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPasswordEncodingValidateListener(databaseUri: Uri?,
|
||||
mainCredential: MainCredential) {
|
||||
assignPasswordValidated(databaseUri, mainCredential)
|
||||
}
|
||||
|
||||
private fun assignPasswordValidated(databaseUri: Uri?,
|
||||
mainCredential: MainCredential) {
|
||||
if (databaseUri != null) {
|
||||
assignDatabasePassword(databaseUri, mainCredential)
|
||||
}
|
||||
}
|
||||
|
||||
fun assignPassword(mainCredential: MainCredential) {
|
||||
mDatabase?.let { database ->
|
||||
database.fileUri?.let { databaseUri ->
|
||||
// Show the progress dialog now or after dialog confirmation
|
||||
if (database.validatePasswordEncoding(mainCredential)) {
|
||||
assignPasswordValidated(databaseUri, mainCredential)
|
||||
} else {
|
||||
PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential)
|
||||
.show(supportFragmentManager, "passwordEncodingTag")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createEntry(newEntry: Entry,
|
||||
parent: Group) {
|
||||
createDatabaseEntry(newEntry, parent, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun updateEntry(oldEntry: Entry,
|
||||
entryToUpdate: Entry) {
|
||||
updateDatabaseEntry(oldEntry, entryToUpdate, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun copyNodes(nodesToCopy: List<Node>,
|
||||
newParent: Group) {
|
||||
copyDatabaseNodes(nodesToCopy, newParent, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun moveNodes(nodesToMove: List<Node>,
|
||||
newParent: Group) {
|
||||
moveDatabaseNodes(nodesToMove, newParent, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
private fun eachNodeRecyclable(database: Database, nodes: List<Node>): Boolean {
|
||||
return nodes.find { node ->
|
||||
var cannotRecycle = true
|
||||
if (node is Entry) {
|
||||
cannotRecycle = !database.canRecycle(node)
|
||||
} else if (node is Group) {
|
||||
cannotRecycle = !database.canRecycle(node)
|
||||
}
|
||||
cannotRecycle
|
||||
} == null
|
||||
}
|
||||
|
||||
fun deleteNodes(nodes: List<Node>, recycleBin: Boolean = false) {
|
||||
mDatabase?.let { database ->
|
||||
// If recycle bin enabled, ensure it exists
|
||||
if (database.isRecycleBinEnabled) {
|
||||
database.ensureRecycleBinExists(resources)
|
||||
}
|
||||
|
||||
// If recycle bin enabled and not in recycle bin, move in recycle bin
|
||||
if (eachNodeRecyclable(database, nodes)) {
|
||||
permanentlyDeleteNodes(nodes)
|
||||
}
|
||||
// else open the dialog to confirm deletion
|
||||
else {
|
||||
val deleteNodesDialogFragment: DeleteNodesDialogFragment =
|
||||
if (recycleBin) {
|
||||
EmptyRecycleBinDialogFragment.getInstance(nodes)
|
||||
} else {
|
||||
DeleteNodesDialogFragment.getInstance(nodes)
|
||||
}
|
||||
deleteNodesDialogFragment.show(supportFragmentManager, "deleteNodesDialogFragment")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun permanentlyDeleteNodes(nodes: List<Node>) {
|
||||
deleteDatabaseNodes(nodes,!mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun createGroup(parent: Group,
|
||||
groupInfo: GroupInfo?) {
|
||||
// Build the group
|
||||
mDatabase?.createGroup()?.let { newGroup ->
|
||||
groupInfo?.let { info ->
|
||||
newGroup.setGroupInfo(info)
|
||||
}
|
||||
// Not really needed here because added in runnable but safe
|
||||
newGroup.parent = parent
|
||||
createDatabaseGroup(newGroup, parent, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateGroup(oldGroup: Group,
|
||||
groupInfo: GroupInfo) {
|
||||
// If group updated save it in the database
|
||||
val updateGroup = Group(oldGroup).let { updateGroup ->
|
||||
updateGroup.apply {
|
||||
// WARNING remove parent and children to keep memory
|
||||
removeParent()
|
||||
removeChildren()
|
||||
this.setGroupInfo(groupInfo)
|
||||
}
|
||||
}
|
||||
updateDatabaseGroup(oldGroup, updateGroup, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun restoreEntryHistory(mainEntry: Entry,
|
||||
entryHistoryPosition: Int,) {
|
||||
restoreDatabaseEntryHistory(mainEntry, entryHistoryPosition, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
fun deleteEntryHistory(mainEntry: Entry,
|
||||
entryHistoryPosition: Int,) {
|
||||
deleteDatabaseEntryHistory(mainEntry, entryHistoryPosition, !mReadOnly && mAutoSaveEnable)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == RESULT_EXIT_LOCK) {
|
||||
mExitLock = true
|
||||
lockAndExit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun reload(database: Database) {
|
||||
// Reload the current activity
|
||||
startActivity(intent)
|
||||
finish()
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
database.wasReloaded = false
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (mDatabase?.wasReloaded == true) {
|
||||
reload(mDatabase!!)
|
||||
}
|
||||
|
||||
// If in ave or registration mode, don't allow read only
|
||||
if ((mSpecialMode == SpecialMode.SAVE
|
||||
|| mSpecialMode == SpecialMode.REGISTRATION)
|
||||
@@ -115,36 +298,19 @@ abstract class LockingActivity : SpecialModeActivity() {
|
||||
finish()
|
||||
}
|
||||
|
||||
mProgressDatabaseTaskProvider?.registerProgressTask()
|
||||
|
||||
// To refresh when back to normal workflow from selection workflow
|
||||
mReadOnlyToSave = ReadOnlyHelper.retrieveReadOnlyFromIntent(intent)
|
||||
mAutoSaveEnable = PreferencesUtil.isAutoSaveDatabaseEnabled(this)
|
||||
|
||||
invalidateOptionsMenu()
|
||||
|
||||
if (mTimeoutEnable) {
|
||||
// End activity if database not loaded
|
||||
// TODO generalize
|
||||
if (mDatabase?.loaded != true) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// After the first creation
|
||||
// or If simply swipe with another application
|
||||
// If the time is out -> close the Activity
|
||||
TimeoutHelper.checkTimeAndLockIfTimeout(this)
|
||||
// If onCreate already record time
|
||||
mDatabase?.let { database ->
|
||||
if (!mExitLock)
|
||||
TimeoutHelper.recordTime(this, database)
|
||||
}
|
||||
}
|
||||
|
||||
LOCKING_ACTIVITY_UI_VISIBLE = true
|
||||
}
|
||||
|
||||
protected fun checkTimeAndLockIfTimeoutOrResetTimeout(action: (() -> Unit)? = null) {
|
||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this, mDatabase, action)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putBoolean(TIMEOUT_ENABLE_KEY, mTimeoutEnable)
|
||||
super.onSaveInstanceState(outState)
|
||||
@@ -153,8 +319,6 @@ abstract class LockingActivity : SpecialModeActivity() {
|
||||
override fun onPause() {
|
||||
LOCKING_ACTIVITY_UI_VISIBLE = false
|
||||
|
||||
mProgressDatabaseTaskProvider?.unregisterProgressTask()
|
||||
|
||||
super.onPause()
|
||||
|
||||
if (mTimeoutEnable) {
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package com.kunzisoft.keepass.activities.selection
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import com.kunzisoft.keepass.activities.DatabaseRetrieval
|
||||
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
import com.kunzisoft.keepass.database.action.DatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
abstract class DatabaseActivity: StylishActivity(), DatabaseRetrieval {
|
||||
|
||||
private val mDatabaseViewModel: DatabaseViewModel by viewModels()
|
||||
private var mDatabaseTaskProvider: DatabaseTaskProvider? = null
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mDatabaseTaskProvider = DatabaseTaskProvider(this)
|
||||
|
||||
mDatabaseTaskProvider?.onDatabaseRetrieved = { database ->
|
||||
onDatabaseRetrieved(database)
|
||||
}
|
||||
mDatabaseTaskProvider?.onActionFinish = { database, actionTask, result ->
|
||||
onDatabaseActionFinished(database, actionTask, result)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveDatabase.observe(this) { save ->
|
||||
mDatabaseTaskProvider?.startDatabaseSave(save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.reloadDatabase.observe(this) { fixDuplicateUuid ->
|
||||
// TODO keepCurrentScreen()
|
||||
mDatabaseTaskProvider?.startDatabaseReload(fixDuplicateUuid)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveName.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveName(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveDescription.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveDescription(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveDefaultUsername.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveName(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveColor.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveColor(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveCompression.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveCompression(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.removeUnlinkData.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseRemoveUnlinkedData(it)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveRecycleBin.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveRecycleBin(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveTemplatesGroup.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveTemplatesGroup(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveMaxHistoryItems.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveMaxHistoryItems(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveMaxHistorySize.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveMaxHistorySize(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveEncryption.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveEncryption(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveKeyDerivation.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveKeyDerivation(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveIterations.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveIterations(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveMemoryUsage.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveMemoryUsage(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
|
||||
mDatabaseViewModel.saveParallelism.observe(this) {
|
||||
mDatabaseTaskProvider?.startDatabaseSaveParallelism(it.oldValue, it.newValue, it.save)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
mDatabase = database
|
||||
mDatabaseViewModel.defineDatabase(database)
|
||||
// optional method implementation
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
// optional method implementation
|
||||
}
|
||||
|
||||
fun createDatabase(databaseUri: Uri,
|
||||
mainCredential: MainCredential) {
|
||||
mDatabaseTaskProvider?.startDatabaseCreate(databaseUri, mainCredential)
|
||||
}
|
||||
|
||||
// TODO Database functions
|
||||
|
||||
fun loadDatabase(databaseUri: Uri,
|
||||
mainCredential: MainCredential,
|
||||
readOnly: Boolean,
|
||||
cipherEntity: CipherDatabaseEntity?,
|
||||
fixDuplicateUuid: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseLoad(databaseUri, mainCredential, readOnly, cipherEntity, fixDuplicateUuid)
|
||||
}
|
||||
|
||||
fun assignDatabasePassword(databaseUri: Uri,
|
||||
mainCredential: MainCredential) {
|
||||
mDatabaseTaskProvider?.startDatabaseAssignPassword(databaseUri, mainCredential)
|
||||
}
|
||||
|
||||
fun saveDatabase() {
|
||||
mDatabaseTaskProvider?.startDatabaseSave(mDatabase?.isReadOnly != true)
|
||||
}
|
||||
|
||||
fun reloadDatabase() {
|
||||
mDatabaseTaskProvider?.startDatabaseReload(false)
|
||||
}
|
||||
|
||||
fun createDatabaseEntry(newEntry: Entry,
|
||||
parent: Group,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseCreateEntry(newEntry, parent, save)
|
||||
}
|
||||
|
||||
fun updateDatabaseEntry(oldEntry: Entry,
|
||||
entryToUpdate: Entry,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseUpdateEntry(oldEntry, entryToUpdate, save)
|
||||
}
|
||||
|
||||
fun copyDatabaseNodes(nodesToCopy: List<Node>,
|
||||
newParent: Group,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseCopyNodes(nodesToCopy, newParent, save)
|
||||
}
|
||||
|
||||
fun moveDatabaseNodes(nodesToMove: List<Node>,
|
||||
newParent: Group,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseMoveNodes(nodesToMove, newParent, save)
|
||||
}
|
||||
|
||||
fun deleteDatabaseNodes(nodesToDelete: List<Node>,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseDeleteNodes(nodesToDelete, save)
|
||||
}
|
||||
|
||||
fun createDatabaseGroup(newGroup: Group,
|
||||
parent: Group,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseCreateGroup(newGroup, parent, save)
|
||||
}
|
||||
|
||||
fun updateDatabaseGroup(oldGroup: Group,
|
||||
groupToUpdate: Group,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseUpdateGroup(oldGroup, groupToUpdate, save)
|
||||
}
|
||||
|
||||
fun restoreDatabaseEntryHistory(mainEntry: Entry,
|
||||
entryHistoryPosition: Int,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseRestoreEntryHistory(mainEntry, entryHistoryPosition, save)
|
||||
}
|
||||
|
||||
fun deleteDatabaseEntryHistory(mainEntry: Entry,
|
||||
entryHistoryPosition: Int,
|
||||
save: Boolean) {
|
||||
mDatabaseTaskProvider?.startDatabaseDeleteEntryHistory(mainEntry, entryHistoryPosition, save)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mDatabaseTaskProvider?.registerProgressTask()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mDatabaseTaskProvider?.unregisterProgressTask()
|
||||
super.onPause()
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.kunzisoft.keepass.activities.selection
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.room.Database
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.SpecialMode
|
||||
@@ -15,7 +16,7 @@ import com.kunzisoft.keepass.view.SpecialModeView
|
||||
/**
|
||||
* Activity to manage special mode (ie: selection mode)
|
||||
*/
|
||||
abstract class SpecialModeActivity : StylishActivity() {
|
||||
abstract class SpecialModeActivity : DatabaseActivity() {
|
||||
|
||||
protected var mSpecialMode: SpecialMode = SpecialMode.DEFAULT
|
||||
private var mTypeMode: TypeMode = TypeMode.DEFAULT
|
||||
|
||||
@@ -358,10 +358,10 @@ class NodeAdapter (private val context: Context,
|
||||
|
||||
// Assign click
|
||||
holder.container.setOnClickListener {
|
||||
mNodeClickCallback?.onNodeClick(subNode)
|
||||
mNodeClickCallback?.onNodeClick(database, subNode)
|
||||
}
|
||||
holder.container.setOnLongClickListener {
|
||||
mNodeClickCallback?.onNodeLongClick(subNode) ?: false
|
||||
mNodeClickCallback?.onNodeLongClick(database, subNode) ?: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,8 +380,8 @@ class NodeAdapter (private val context: Context,
|
||||
* Callback listener to redefine to do an action when a node is click
|
||||
*/
|
||||
interface NodeClickCallback {
|
||||
fun onNodeClick(node: Node)
|
||||
fun onNodeLongClick(node: Node): Boolean
|
||||
fun onNodeClick(database: Database, node: Node)
|
||||
fun onNodeLongClick(database: Database, node: Node): Boolean
|
||||
}
|
||||
|
||||
class NodeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
@@ -12,10 +12,11 @@ import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.template.Template
|
||||
import com.kunzisoft.keepass.database.element.template.TemplateField
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
|
||||
|
||||
class TemplatesSelectorAdapter(private val context: Context,
|
||||
private val database: Database?,
|
||||
private val iconDrawableFactory: IconDrawableFactory?,
|
||||
private var templates: List<Template>): BaseAdapter() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
@@ -43,7 +44,7 @@ class TemplatesSelectorAdapter(private val context: Context,
|
||||
}
|
||||
|
||||
holder.icon?.let { icon ->
|
||||
database?.iconDrawableFactory?.assignDatabaseIcon(icon, template.icon, mIconColor)
|
||||
iconDrawableFactory?.assignDatabaseIcon(icon, template.icon, mIconColor)
|
||||
}
|
||||
holder.name?.text = TemplateField.getLocalizedName(context, template.title)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class App : MultiDexApplication() {
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
Database.getInstance().clearAndClose(this)
|
||||
// TODO Database.getInstance().clearAndClose(this)
|
||||
super.onTerminate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,13 +56,9 @@ class KeeAutofillService : AutofillService() {
|
||||
var autofillInlineSuggestionsEnabled: Boolean = false
|
||||
private var mLock = AtomicBoolean()
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
|
||||
getPreferences()
|
||||
}
|
||||
|
||||
@@ -102,7 +98,8 @@ class KeeAutofillService : AutofillService() {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
mDatabase?.let { database ->
|
||||
// TODO database
|
||||
Database.getInstance()?.let { database ->
|
||||
launchSelection(database,
|
||||
searchInfo,
|
||||
parseResult,
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.kunzisoft.keepass.activities.dialogs.DatabaseChangedDialogFragment.Co
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
@@ -83,9 +84,12 @@ import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
class DatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
|
||||
var onActionFinish: ((actionTask: String,
|
||||
var onDatabaseRetrieved: ((database: Database?) -> Unit)? = null
|
||||
|
||||
var onActionFinish: ((database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result) -> Unit)? = null
|
||||
|
||||
private var intentDatabaseTask = Intent(activity.applicationContext, DatabaseTaskNotificationService::class.java)
|
||||
@@ -99,16 +103,16 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
private var databaseChangedDialogFragment: DatabaseChangedDialogFragment? = null
|
||||
|
||||
private val actionTaskListener = object: DatabaseTaskNotificationService.ActionTaskListener {
|
||||
override fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?) {
|
||||
override fun onStartAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?) {
|
||||
startDialog(titleId, messageId, warningId)
|
||||
}
|
||||
|
||||
override fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?) {
|
||||
override fun onUpdateAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?) {
|
||||
updateDialog(titleId, messageId, warningId)
|
||||
}
|
||||
|
||||
override fun onStopAction(actionTask: String, result: ActionRunnable.Result) {
|
||||
onActionFinish?.invoke(actionTask, result)
|
||||
override fun onStopAction(database: Database, actionTask: String, result: ActionRunnable.Result) {
|
||||
onActionFinish?.invoke(database, actionTask, result)
|
||||
// Remove the progress task
|
||||
stopDialog()
|
||||
}
|
||||
@@ -144,6 +148,12 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
}
|
||||
}
|
||||
|
||||
private var databaseListener = object: DatabaseTaskNotificationService.DatabaseListener {
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
onDatabaseRetrieved?.invoke(database)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startDialog(titleId: Int? = null,
|
||||
messageId: Int? = null,
|
||||
warningId: Int? = null) {
|
||||
@@ -187,16 +197,19 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
serviceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, serviceBinder: IBinder?) {
|
||||
mBinder = (serviceBinder as DatabaseTaskNotificationService.ActionTaskBinder?)?.apply {
|
||||
addActionTaskListener(actionTaskListener)
|
||||
addDatabaseListener(databaseListener)
|
||||
addDatabaseFileInfoListener(databaseInfoListener)
|
||||
getService().checkAction()
|
||||
addActionTaskListener(actionTaskListener)
|
||||
getService().checkDatabase()
|
||||
getService().checkDatabaseInfo()
|
||||
getService().checkAction()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
|
||||
mBinder?.removeActionTaskListener(actionTaskListener)
|
||||
mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
|
||||
mBinder?.removeDatabaseListener(databaseListener)
|
||||
mBinder = null
|
||||
}
|
||||
}
|
||||
@@ -257,8 +270,9 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
fun unregisterProgressTask() {
|
||||
stopDialog()
|
||||
|
||||
mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
|
||||
mBinder?.removeActionTaskListener(actionTaskListener)
|
||||
mBinder?.removeDatabaseFileInfoListener(databaseInfoListener)
|
||||
mBinder?.removeDatabaseListener(databaseListener)
|
||||
mBinder = null
|
||||
|
||||
unBindService()
|
||||
@@ -639,6 +653,6 @@ class ProgressDatabaseTaskProvider(private val activity: FragmentActivity) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = ProgressDatabaseTaskProvider::class.java.name
|
||||
private val TAG = DatabaseTaskProvider::class.java.name
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
|
||||
|
||||
// Remove entry info if the database is not loaded
|
||||
// or if entry info timestamp is before database loaded timestamp
|
||||
// TODO Database
|
||||
val database = Database.getInstance()
|
||||
val databaseTime = database.loadTimestamp
|
||||
val entryTime = entryInfoTimestamp
|
||||
|
||||
@@ -49,8 +49,6 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private var mDatabase: Database? = Database.getInstance()
|
||||
|
||||
override fun retrieveChannelId(): String {
|
||||
return CHANNEL_ATTACHMENT_ID
|
||||
}
|
||||
@@ -289,7 +287,8 @@ class AttachmentFileNotificationService: LockNotificationService() {
|
||||
// Add action to the list on start
|
||||
attachmentNotificationList.add(attachmentNotification)
|
||||
|
||||
mDatabase?.let { database ->
|
||||
// TODO Database
|
||||
Database.getInstance()?.let { database ->
|
||||
mainScope.launch {
|
||||
AttachmentFileAction(attachmentNotification,
|
||||
database,
|
||||
|
||||
@@ -58,12 +58,12 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
|
||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
private var mDatabaseListeners = LinkedList<DatabaseListener>()
|
||||
private var mDatabaseInfoListeners = LinkedList<DatabaseInfoListener>()
|
||||
private var mActionTaskBinder = ActionTaskBinder()
|
||||
private var mActionTaskListeners = LinkedList<ActionTaskListener>()
|
||||
private var mActionRunning = false
|
||||
|
||||
private var mDatabaseInfoListeners = LinkedList<DatabaseInfoListener>()
|
||||
|
||||
private var mIconId: Int = R.drawable.notification_ic_database_load
|
||||
private var mTitleId: Int = R.string.database_opened
|
||||
private var mMessageId: Int? = null
|
||||
@@ -81,13 +81,13 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
|
||||
fun getService(): DatabaseTaskNotificationService = this@DatabaseTaskNotificationService
|
||||
|
||||
fun addActionTaskListener(actionTaskListener: ActionTaskListener) {
|
||||
if (!mActionTaskListeners.contains(actionTaskListener))
|
||||
mActionTaskListeners.add(actionTaskListener)
|
||||
fun addDatabaseListener(databaseListener: DatabaseListener) {
|
||||
if (!mDatabaseListeners.contains(databaseListener))
|
||||
mDatabaseListeners.add(databaseListener)
|
||||
}
|
||||
|
||||
fun removeActionTaskListener(actionTaskListener: ActionTaskListener) {
|
||||
mActionTaskListeners.remove(actionTaskListener)
|
||||
fun removeDatabaseListener(databaseListener: DatabaseListener) {
|
||||
mDatabaseListeners.remove(databaseListener)
|
||||
}
|
||||
|
||||
fun addDatabaseFileInfoListener(databaseInfoListener: DatabaseInfoListener) {
|
||||
@@ -98,12 +98,19 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
fun removeDatabaseFileInfoListener(databaseInfoListener: DatabaseInfoListener) {
|
||||
mDatabaseInfoListeners.remove(databaseInfoListener)
|
||||
}
|
||||
|
||||
fun addActionTaskListener(actionTaskListener: ActionTaskListener) {
|
||||
if (!mActionTaskListeners.contains(actionTaskListener))
|
||||
mActionTaskListeners.add(actionTaskListener)
|
||||
}
|
||||
|
||||
fun removeActionTaskListener(actionTaskListener: ActionTaskListener) {
|
||||
mActionTaskListeners.remove(actionTaskListener)
|
||||
}
|
||||
}
|
||||
|
||||
interface ActionTaskListener {
|
||||
fun onStartAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
||||
fun onUpdateAction(titleId: Int?, messageId: Int?, warningId: Int?)
|
||||
fun onStopAction(actionTask: String, result: ActionRunnable.Result)
|
||||
interface DatabaseListener {
|
||||
fun onDatabaseRetrieved(database: Database?)
|
||||
}
|
||||
|
||||
interface DatabaseInfoListener {
|
||||
@@ -111,14 +118,15 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
newDatabaseInfo: SnapFileDatabaseInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Force to call [ActionTaskListener.onStartAction] if the action is still running
|
||||
*/
|
||||
fun checkAction() {
|
||||
if (mActionRunning) {
|
||||
mActionTaskListeners.forEach { actionTaskListener ->
|
||||
actionTaskListener.onStartAction(mTitleId, mMessageId, mWarningId)
|
||||
}
|
||||
interface ActionTaskListener {
|
||||
fun onStartAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?)
|
||||
fun onUpdateAction(database: Database, titleId: Int?, messageId: Int?, warningId: Int?)
|
||||
fun onStopAction(database: Database, actionTask: String, result: ActionRunnable.Result)
|
||||
}
|
||||
|
||||
fun checkDatabase() {
|
||||
mDatabaseListeners.forEach { databaseListener ->
|
||||
databaseListener.onDatabaseRetrieved(mDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,6 +183,17 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force to call [ActionTaskListener.onStartAction] if the action is still running
|
||||
*/
|
||||
fun checkAction() {
|
||||
if (mActionRunning) {
|
||||
mActionTaskListeners.forEach { actionTaskListener ->
|
||||
actionTaskListener.onStartAction(mDatabase, mTitleId, mMessageId, mWarningId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
super.onBind(intent)
|
||||
return mActionTaskBinder
|
||||
@@ -184,6 +203,9 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
mDatabaseListeners.forEach { listener ->
|
||||
listener.onDatabaseRetrieved(mDatabase)
|
||||
}
|
||||
|
||||
// Create the notification
|
||||
buildMessage(intent)
|
||||
@@ -243,7 +265,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
})
|
||||
|
||||
mActionTaskListeners.forEach { actionTaskListener ->
|
||||
actionTaskListener.onStartAction(mTitleId, mMessageId, mWarningId)
|
||||
actionTaskListener.onStartAction(mDatabase, mTitleId, mMessageId, mWarningId)
|
||||
}
|
||||
|
||||
},
|
||||
@@ -253,7 +275,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
{ result ->
|
||||
try {
|
||||
mActionTaskListeners.forEach { actionTaskListener ->
|
||||
actionTaskListener.onStopAction(intentAction!!, result)
|
||||
actionTaskListener.onStopAction(mDatabase, intentAction!!, result)
|
||||
}
|
||||
} finally {
|
||||
// Save the database info before performing action
|
||||
@@ -443,7 +465,7 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
override fun updateMessage(resId: Int) {
|
||||
mMessageId = resId
|
||||
mActionTaskListeners.forEach { actionTaskListener ->
|
||||
actionTaskListener.onUpdateAction(mTitleId, mMessageId, mWarningId)
|
||||
actionTaskListener.onUpdateAction(mDatabase, mTitleId, mMessageId, mWarningId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
package com.kunzisoft.keepass.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.kunzisoft.keepass.activities.DatabaseRetrieval
|
||||
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
abstract class DatabasePreferenceFragment : PreferenceFragmentCompat() {
|
||||
abstract class DatabasePreferenceFragment : PreferenceFragmentCompat(), DatabaseRetrieval {
|
||||
|
||||
protected var mDatabase: Database? = null
|
||||
private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
override fun onViewCreated(view: View,
|
||||
savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
mDatabaseViewModel.database.observe(viewLifecycleOwner) { database ->
|
||||
view.resetAppTimeoutWhenViewFocusedOrChanged(requireContext(), database)
|
||||
onDatabaseRetrieved(database)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
// Can be overridden by a subclass
|
||||
}
|
||||
|
||||
protected fun saveDatabase(save: Boolean) {
|
||||
mDatabaseViewModel.saveDatabase(save)
|
||||
}
|
||||
|
||||
protected fun reloadDatabase() {
|
||||
mDatabaseViewModel.reloadDatabase(false)
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,14 @@ import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
|
||||
class MainPreferenceFragment : DatabasePreferenceFragment() {
|
||||
|
||||
private var mCallback: Callback? = null
|
||||
|
||||
private var mDatabaseLoaded: Boolean = false
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
@@ -43,6 +46,10 @@ class MainPreferenceFragment : DatabasePreferenceFragment() {
|
||||
super.onDetach()
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
mDatabaseLoaded = database?.loaded == true
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
|
||||
@@ -80,7 +87,8 @@ class MainPreferenceFragment : DatabasePreferenceFragment() {
|
||||
mCallback?.onNestedPreferenceSelected(NestedSettingsFragment.Screen.DATABASE)
|
||||
false
|
||||
}
|
||||
if (mDatabase?.loaded != true) {
|
||||
// TODO Check
|
||||
if (mDatabaseLoaded) {
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,9 @@ import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseAction
|
||||
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryAction
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.Education
|
||||
import com.kunzisoft.keepass.icons.IconDrawableFactory
|
||||
import com.kunzisoft.keepass.icons.IconPackChooser
|
||||
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
|
||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.DurationDialogFragmentCompat
|
||||
@@ -54,6 +56,12 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
|
||||
private var deleteKeysAlertDialog: AlertDialog? = null
|
||||
|
||||
private var mIconDrawableFactory: IconDrawableFactory? = null
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
this.mIconDrawableFactory = database?.iconDrawableFactory
|
||||
}
|
||||
|
||||
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
||||
// Load the preferences from an XML resource
|
||||
@@ -426,8 +434,9 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
}
|
||||
}
|
||||
if (iconPackEnabled) {
|
||||
mDatabase?.let {
|
||||
IconPackChooser.setSelectedIconPack(it.iconDrawableFactory, iconPackId)
|
||||
// TODO Check
|
||||
mIconDrawableFactory?.let {
|
||||
IconPackChooser.setSelectedIconPack(it, iconPackId)
|
||||
}
|
||||
}
|
||||
iconPackEnabled
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.SwitchPreference
|
||||
@@ -33,6 +34,7 @@ import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
||||
@@ -40,9 +42,11 @@ import com.kunzisoft.keepass.settings.preference.*
|
||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.utils.MenuUtil
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
private var mDatabaseReadOnly: Boolean = false
|
||||
private var mDatabaseAutoSaveEnabled: Boolean = true
|
||||
|
||||
@@ -61,6 +65,10 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
|
||||
private var mMemoryPref: InputKdfSizePreference? = null
|
||||
private var mParallelismPref: InputKdfNumberPreference? = null
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
mDatabase = database
|
||||
}
|
||||
|
||||
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
@@ -161,8 +169,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
|
||||
database.enableRecycleBin(recycleBinEnabled, resources)
|
||||
refreshRecycleBinGroup()
|
||||
// Save the database if not in readonly mode
|
||||
(context as SettingsActivity?)?.
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSave(mDatabaseAutoSaveEnabled)
|
||||
saveDatabase(mDatabaseAutoSaveEnabled)
|
||||
true
|
||||
}
|
||||
true
|
||||
@@ -195,8 +202,7 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
|
||||
database.enableTemplates(templatesEnabled, resources)
|
||||
refreshTemplatesGroup()
|
||||
// Save the database if not in readonly mode
|
||||
(context as SettingsActivity?)?.
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSave(mDatabaseAutoSaveEnabled)
|
||||
saveDatabase(mDatabaseAutoSaveEnabled)
|
||||
true
|
||||
}
|
||||
true
|
||||
@@ -621,25 +627,20 @@ class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
val settingActivity = activity as SettingsActivity?
|
||||
|
||||
return when (item.itemId) {
|
||||
R.id.menu_save_database -> {
|
||||
settingActivity?.mProgressDatabaseTaskProvider?.startDatabaseSave(!mDatabaseReadOnly)
|
||||
saveDatabase(!mDatabaseReadOnly)
|
||||
true
|
||||
}
|
||||
R.id.menu_reload_database -> {
|
||||
settingActivity?.apply {
|
||||
keepCurrentScreen()
|
||||
mProgressDatabaseTaskProvider?.startDatabaseReload(false)
|
||||
}
|
||||
reloadDatabase()
|
||||
return true
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Check the time lock before launching settings
|
||||
settingActivity?.let {
|
||||
// TODO activity menu
|
||||
(activity as SettingsActivity?)?.let {
|
||||
MenuUtil.onDefaultMenuOptionsItemSelected(it, item, mDatabaseReadOnly, true)
|
||||
}
|
||||
super.onOptionsItemSelected(item)
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFra
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
|
||||
// TODO Move database fragment in sub class
|
||||
abstract class NestedSettingsFragment : DatabasePreferenceFragment() {
|
||||
|
||||
enum class Screen {
|
||||
|
||||
@@ -22,7 +22,6 @@ package com.kunzisoft.keepass.settings
|
||||
import android.app.Activity
|
||||
import android.app.backup.BackupManager
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
@@ -33,7 +32,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
||||
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
@@ -41,6 +39,7 @@ import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrCha
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.model.MainCredential
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||
import org.joda.time.DateTime
|
||||
@@ -49,8 +48,7 @@ import java.util.*
|
||||
open class SettingsActivity
|
||||
: LockingActivity(),
|
||||
MainPreferenceFragment.Callback,
|
||||
AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
|
||||
PasswordEncodingDialogFragment.Listener {
|
||||
AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
|
||||
|
||||
private var backupManager: BackupManager? = null
|
||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||
@@ -60,14 +58,6 @@ open class SettingsActivity
|
||||
private var toolbar: Toolbar? = null
|
||||
private var lockView: View? = null
|
||||
|
||||
/**
|
||||
* Retrieve the main fragment to show in first
|
||||
* @return The main fragment
|
||||
*/
|
||||
protected open fun retrieveMainFragment(): Fragment {
|
||||
return MainPreferenceFragment()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -90,9 +80,6 @@ open class SettingsActivity
|
||||
lockAndExit()
|
||||
}
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, mDatabase)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.fragment_container, retrieveMainFragment())
|
||||
@@ -103,29 +90,6 @@ open class SettingsActivity
|
||||
|
||||
backupManager = BackupManager(this)
|
||||
|
||||
mProgressDatabaseTaskProvider?.onActionFinish = { actionTask, result ->
|
||||
when (actionTask) {
|
||||
DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Reload the current activity
|
||||
if (result.isSuccess) {
|
||||
startActivity(intent)
|
||||
finish()
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
} else {
|
||||
this.showActionErrorIfNeeded(result)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Call result in fragment
|
||||
(supportFragmentManager
|
||||
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?)
|
||||
?.onProgressDialogThreadResult(actionTask, result)
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
}
|
||||
|
||||
// To reload the current screen
|
||||
if (intent.extras?.containsKey(FRAGMENT_ARG) == true) {
|
||||
intent.extras?.getString(FRAGMENT_ARG)?.let { fragmentScreenName ->
|
||||
@@ -136,6 +100,49 @@ open class SettingsActivity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the main fragment to show in first
|
||||
* @return The main fragment
|
||||
*/
|
||||
protected open fun retrieveMainFragment(): Fragment {
|
||||
return MainPreferenceFragment()
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
|
||||
// Focus view to reinitialize timeout
|
||||
coordinatorLayout?.resetAppTimeoutWhenViewFocusedOrChanged(this, database)
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
DatabaseTaskNotificationService.ACTION_DATABASE_RELOAD_TASK -> {
|
||||
// Reload the current activity
|
||||
if (result.isSuccess) {
|
||||
startActivity(intent)
|
||||
finish()
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
} else {
|
||||
this.showActionErrorIfNeeded(result)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Call result in fragment
|
||||
(supportFragmentManager
|
||||
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?)
|
||||
?.onProgressDialogThreadResult(actionTask, result)
|
||||
}
|
||||
}
|
||||
coordinatorLayout?.showActionErrorIfNeeded(result)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> onBackPressed()
|
||||
@@ -149,31 +156,8 @@ open class SettingsActivity
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onPasswordEncodingValidateListener(databaseUri: Uri?,
|
||||
mainCredential: MainCredential) {
|
||||
databaseUri?.let {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseAssignPassword(
|
||||
databaseUri,
|
||||
mainCredential
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAssignKeyDialogPositiveClick(mainCredential: MainCredential) {
|
||||
mDatabase?.let { database ->
|
||||
database.fileUri?.let { databaseUri ->
|
||||
// Show the progress dialog now or after dialog confirmation
|
||||
if (database.validatePasswordEncoding(mainCredential)) {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseAssignPassword(
|
||||
databaseUri,
|
||||
mainCredential
|
||||
)
|
||||
} else {
|
||||
PasswordEncodingDialogFragment.getInstance(databaseUri, mainCredential)
|
||||
.show(supportFragmentManager, "passwordEncodingTag")
|
||||
}
|
||||
}
|
||||
}
|
||||
assignPassword(mainCredential)
|
||||
}
|
||||
|
||||
override fun onAssignKeyDialogNegativeClick(mainCredential: MainCredential) {}
|
||||
@@ -243,7 +227,7 @@ open class SettingsActivity
|
||||
|
||||
override fun onNestedPreferenceSelected(key: NestedSettingsFragment.Screen, reload: Boolean) {
|
||||
if (mTimeoutEnable)
|
||||
TimeoutHelper.checkTimeAndLockIfTimeoutOrResetTimeout(this, mDatabase) {
|
||||
checkTimeAndLockIfTimeoutOrResetTimeout {
|
||||
replaceFragment(key, reload)
|
||||
}
|
||||
else
|
||||
|
||||
@@ -45,6 +45,7 @@ class IconPackListPreference @JvmOverloads constructor(context: Context,
|
||||
|
||||
setEntries(entries.toTypedArray())
|
||||
entryValues = values.toTypedArray()
|
||||
// TODO database
|
||||
IconPackChooser.getSelectedIconPack(context, Database.getInstance().iconDrawableFactory)?.let { selectedIconPack ->
|
||||
setDefaultValue(selectedIconPack.id)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.kunzisoft.androidclearchroma.colormode.ColorMode
|
||||
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment
|
||||
import com.kunzisoft.androidclearchroma.fragment.ChromaColorFragment.*
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
|
||||
class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat() {
|
||||
|
||||
@@ -88,7 +89,7 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
|
||||
}
|
||||
val oldColor = database.customColor
|
||||
database.customColor = newColor
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveColor(oldColor, newColor, mDatabaseAutoSaveEnable)
|
||||
saveColor(oldColor, newColor)
|
||||
}
|
||||
|
||||
onDialogClosed(true)
|
||||
|
||||
@@ -63,7 +63,7 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
|
||||
database.compressionAlgorithm = newCompression
|
||||
|
||||
if (oldCompression != null && newCompression != null)
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable)
|
||||
saveCompression(oldCompression, newCompression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class DatabaseDefaultUsernamePreferenceDialogFragmentCompat : DatabaseSavePrefer
|
||||
val newDefaultUsername = inputText
|
||||
val oldDefaultUsername = database.defaultUsername
|
||||
database.defaultUsername = newDefaultUsername
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveDefaultUsername(oldDefaultUsername, newDefaultUsername, mDatabaseAutoSaveEnable)
|
||||
saveDefaultUsername(oldDefaultUsername, newDefaultUsername)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreference
|
||||
val newDescription = inputText
|
||||
val oldDescription = database.description
|
||||
database.description = newDescription
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveDescription(oldDescription, newDescription, mDatabaseAutoSaveEnable)
|
||||
saveDescription(oldDescription, newDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
|
||||
database.encryptionAlgorithm = newAlgorithm
|
||||
|
||||
if (oldAlgorithm != null && newAlgorithm != null)
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveEncryption(oldAlgorithm, newAlgorithm, mDatabaseAutoSaveEnable)
|
||||
saveEncryption(oldAlgorithm, newAlgorithm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
|
||||
val oldKdfEngine = database.kdfEngine
|
||||
if (newKdfEngine != null && oldKdfEngine != null) {
|
||||
database.kdfEngine = newKdfEngine
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveKeyDerivation(oldKdfEngine, newKdfEngine, mDatabaseAutoSaveEnable)
|
||||
saveKeyDerivation(oldKdfEngine, newKdfEngine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogF
|
||||
val newName = inputText
|
||||
val oldName = database.name
|
||||
database.name = newName
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveName(oldName, newName, mDatabaseAutoSaveEnable)
|
||||
saveName(oldName, newName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,7 @@ class DatabaseRecycleBinGroupPreferenceDialogFragmentCompat
|
||||
val oldGroup = database.recycleBin
|
||||
val newGroup = mGroupRecycleBin
|
||||
database.setRecycleBin(newGroup)
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveRecycleBin(
|
||||
oldGroup,
|
||||
newGroup,
|
||||
mDatabaseAutoSaveEnable
|
||||
)
|
||||
saveRecycleBin(oldGroup, newGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class DatabaseRemoveUnlinkedDataPreferenceDialogFragmentCompat : DatabaseSavePre
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
mDatabase?.let { _ ->
|
||||
if (positiveResult) {
|
||||
mProgressDatabaseTaskProvider?.startDatabaseRemoveUnlinkedData(mDatabaseAutoSaveEnable)
|
||||
removeUnlinkedData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,28 +20,123 @@
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.content.Context
|
||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.kunzisoft.keepass.activities.DatabaseRetrieval
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.settings.SettingsActivity
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
|
||||
|
||||
abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
|
||||
abstract class DatabaseSavePreferenceDialogFragmentCompat
|
||||
: InputPreferenceDialogFragmentCompat(), DatabaseRetrieval {
|
||||
|
||||
protected var mDatabaseAutoSaveEnable = true
|
||||
protected var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
private var mDatabaseAutoSaveEnable = true
|
||||
private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
|
||||
protected var mDatabase: Database? = null
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
// Attach dialog thread to start action
|
||||
if (context is SettingsActivity) {
|
||||
mProgressDatabaseTaskProvider = context.mProgressDatabaseTaskProvider
|
||||
}
|
||||
|
||||
this.mDatabaseAutoSaveEnable = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
mProgressDatabaseTaskProvider = null
|
||||
super.onDetach()
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mDatabaseViewModel.database.observe(viewLifecycleOwner) { database ->
|
||||
onDatabaseRetrieved(database)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDatabaseRetrieved(database: Database?) {
|
||||
this.mDatabase = database
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: Database,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
// Optional
|
||||
}
|
||||
|
||||
protected fun saveColor(oldColor: String,
|
||||
newColor: String) {
|
||||
mDatabaseViewModel.saveColor(oldColor, newColor, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveCompression(oldCompression: CompressionAlgorithm,
|
||||
newCompression: CompressionAlgorithm) {
|
||||
mDatabaseViewModel.saveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveDefaultUsername(oldUsername: String,
|
||||
newUsername: String) {
|
||||
mDatabaseViewModel.saveDefaultUsername(oldUsername, newUsername, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveDescription(oldDescription: String,
|
||||
newDescription: String) {
|
||||
mDatabaseViewModel.saveDescription(oldDescription, newDescription, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveEncryption(oldEncryption: EncryptionAlgorithm,
|
||||
newEncryptionAlgorithm: EncryptionAlgorithm) {
|
||||
mDatabaseViewModel.saveEncryption(oldEncryption, newEncryptionAlgorithm, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveKeyDerivation(oldKeyDerivation: KdfEngine,
|
||||
newKeyDerivation: KdfEngine) {
|
||||
mDatabaseViewModel.saveKeyDerivation(oldKeyDerivation, newKeyDerivation, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveName(oldName: String,
|
||||
newName: String) {
|
||||
mDatabaseViewModel.saveName(oldName, newName, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveRecycleBin(oldGroup: Group?,
|
||||
newGroup: Group?) {
|
||||
mDatabaseViewModel.saveRecycleBin(oldGroup, newGroup, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun removeUnlinkedData() {
|
||||
mDatabaseViewModel.removeUnlinkedData(mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveTemplatesGroup(oldGroup: Group?,
|
||||
newGroup: Group?) {
|
||||
mDatabaseViewModel.saveTemplatesGroup(oldGroup, newGroup, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveMaxHistoryItems(oldNumber: Int,
|
||||
newNumber: Int) {
|
||||
mDatabaseViewModel.saveMaxHistoryItems(oldNumber, newNumber, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveMaxHistorySize(oldNumber: Long,
|
||||
newNumber: Long) {
|
||||
mDatabaseViewModel.saveMaxHistorySize(oldNumber, newNumber, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveMemoryUsage(oldNumber: Long,
|
||||
newNumber: Long) {
|
||||
mDatabaseViewModel.saveMemoryUsage(oldNumber, newNumber, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveParallelism(oldNumber: Long,
|
||||
newNumber: Long) {
|
||||
mDatabaseViewModel.saveParallelism(oldNumber, newNumber, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
protected fun saveIterations(oldNumber: Long,
|
||||
newNumber: Long) {
|
||||
mDatabaseViewModel.saveIterations(oldNumber, newNumber, mDatabaseAutoSaveEnable)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -62,11 +62,7 @@ class DatabaseTemplatesGroupPreferenceDialogFragmentCompat
|
||||
val oldGroup = database.templatesGroup
|
||||
val newGroup = mGroupTemplates
|
||||
database.setTemplatesGroup(newGroup)
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveTemplatesGroup(
|
||||
oldGroup,
|
||||
newGroup,
|
||||
mDatabaseAutoSaveEnable
|
||||
)
|
||||
saveTemplatesGroup(oldGroup, newGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
@@ -30,7 +29,6 @@ import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceDialogFragmentCompat
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
|
||||
abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat() {
|
||||
|
||||
@@ -41,14 +39,6 @@ abstract class InputPreferenceDialogFragmentCompat : PreferenceDialogFragmentCom
|
||||
|
||||
private var mOnInputTextEditorActionListener: TextView.OnEditorActionListener? = null
|
||||
|
||||
protected var mDatabase: Database? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mDatabase = Database.getInstance()
|
||||
}
|
||||
|
||||
var inputText: String
|
||||
get() = this.inputTextView?.text?.toString() ?: ""
|
||||
set(inputText) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class MaxHistoryItemsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDial
|
||||
// Remove all history items
|
||||
database.removeOldestHistoryForEachEntry()
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems, maxHistoryItems, mDatabaseAutoSaveEnable)
|
||||
saveMaxHistoryItems(oldMaxHistoryItems, maxHistoryItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class MaxHistorySizePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialo
|
||||
val oldMaxHistorySize = database.historyMaxSize
|
||||
database.historyMaxSize = numberOfBytes
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveMaxHistorySize(oldMaxHistorySize, numberOfBytes, mDatabaseAutoSaveEnable)
|
||||
saveMaxHistorySize(oldMaxHistorySize, numberOfBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ class MemoryUsagePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
|
||||
val oldMemoryUsage = database.memoryUsage
|
||||
database.memoryUsage = numberOfBytes
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveMemoryUsage(oldMemoryUsage, numberOfBytes, mDatabaseAutoSaveEnable)
|
||||
saveMemoryUsage(oldMemoryUsage, numberOfBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,7 @@ class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
|
||||
val oldParallelism = database.parallelism
|
||||
database.parallelism = parallelism
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveParallelism(
|
||||
oldParallelism,
|
||||
parallelism,
|
||||
mDatabaseAutoSaveEnable)
|
||||
saveParallelism(oldParallelism, parallelism)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class RoundsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmen
|
||||
database.numberKeyEncryptionRounds = Long.MAX_VALUE
|
||||
}
|
||||
|
||||
mProgressDatabaseTaskProvider?.startDatabaseSaveIterations(oldRounds, rounds, mDatabaseAutoSaveEnable)
|
||||
saveIterations(oldRounds, rounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,5 +138,5 @@ fun Context.closeDatabase() {
|
||||
cancelAll()
|
||||
}
|
||||
// Clear data
|
||||
Database.getInstance().clearAndClose(this)
|
||||
// TODO Database.getInstance().clearAndClose(this)
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.kunzisoft.keepass.viewmodels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
|
||||
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
|
||||
|
||||
class DatabaseViewModel: ViewModel() {
|
||||
|
||||
val database : LiveData<Database?> get() = _database
|
||||
private val _database = SingleLiveEvent<Database?>()
|
||||
|
||||
val saveDatabase : LiveData<Boolean> get() = _saveDatabase
|
||||
private val _saveDatabase = SingleLiveEvent<Boolean>()
|
||||
|
||||
val reloadDatabase : LiveData<Boolean> get() = _reloadDatabase
|
||||
private val _reloadDatabase = SingleLiveEvent<Boolean>()
|
||||
|
||||
val saveName : LiveData<SuperString> get() = _saveName
|
||||
private val _saveName = SingleLiveEvent<SuperString>()
|
||||
|
||||
val saveDescription : LiveData<SuperString> get() = _saveDescription
|
||||
private val _saveDescription = SingleLiveEvent<SuperString>()
|
||||
|
||||
val saveDefaultUsername : LiveData<SuperString> get() = _saveDefaultUsername
|
||||
private val _saveDefaultUsername = SingleLiveEvent<SuperString>()
|
||||
|
||||
val saveColor : LiveData<SuperString> get() = _saveColor
|
||||
private val _saveColor = SingleLiveEvent<SuperString>()
|
||||
|
||||
val saveCompression : LiveData<SuperCompression> get() = _saveCompression
|
||||
private val _saveCompression = SingleLiveEvent<SuperCompression>()
|
||||
|
||||
val removeUnlinkData : LiveData<Boolean> get() = _removeUnlinkData
|
||||
private val _removeUnlinkData = SingleLiveEvent<Boolean>()
|
||||
|
||||
val saveRecycleBin : LiveData<SuperGroup> get() = _saveRecycleBin
|
||||
private val _saveRecycleBin = SingleLiveEvent<SuperGroup>()
|
||||
|
||||
val saveTemplatesGroup : LiveData<SuperGroup> get() = _saveTemplatesGroup
|
||||
private val _saveTemplatesGroup = SingleLiveEvent<SuperGroup>()
|
||||
|
||||
val saveMaxHistoryItems : LiveData<SuperInt> get() = _saveMaxHistoryItems
|
||||
private val _saveMaxHistoryItems = SingleLiveEvent<SuperInt>()
|
||||
|
||||
val saveMaxHistorySize : LiveData<SuperLong> get() = _saveMaxHistorySize
|
||||
private val _saveMaxHistorySize = SingleLiveEvent<SuperLong>()
|
||||
|
||||
val saveEncryption : LiveData<SuperEncryption> get() = _saveEncryption
|
||||
private val _saveEncryption = SingleLiveEvent<SuperEncryption>()
|
||||
|
||||
val saveKeyDerivation : LiveData<SuperKeyDerivation> get() = _saveKeyDerivation
|
||||
private val _saveKeyDerivation = SingleLiveEvent<SuperKeyDerivation>()
|
||||
|
||||
val saveIterations : LiveData<SuperLong> get() = _saveIterations
|
||||
private val _saveIterations = SingleLiveEvent<SuperLong>()
|
||||
|
||||
val saveMemoryUsage : LiveData<SuperLong> get() = _saveMemoryUsage
|
||||
private val _saveMemoryUsage = SingleLiveEvent<SuperLong>()
|
||||
|
||||
val saveParallelism : LiveData<SuperLong> get() = _saveParallelism
|
||||
private val _saveParallelism = SingleLiveEvent<SuperLong>()
|
||||
|
||||
|
||||
fun defineDatabase(database: Database?) {
|
||||
this._database.value = database
|
||||
}
|
||||
|
||||
fun saveDatabase(save: Boolean) {
|
||||
_saveDatabase.value = save
|
||||
}
|
||||
|
||||
fun reloadDatabase(fixDuplicateUuid: Boolean) {
|
||||
_reloadDatabase.value = fixDuplicateUuid
|
||||
}
|
||||
|
||||
fun saveName(oldValue: String,
|
||||
newValue: String,
|
||||
save: Boolean) {
|
||||
_saveName.value = SuperString(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveDescription(oldValue: String,
|
||||
newValue: String,
|
||||
save: Boolean) {
|
||||
_saveDescription.value = SuperString(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveDefaultUsername(oldValue: String,
|
||||
newValue: String,
|
||||
save: Boolean) {
|
||||
_saveDefaultUsername.value = SuperString(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveColor(oldValue: String,
|
||||
newValue: String,
|
||||
save: Boolean) {
|
||||
_saveColor.value = SuperString(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveCompression(oldValue: CompressionAlgorithm,
|
||||
newValue: CompressionAlgorithm,
|
||||
save: Boolean) {
|
||||
_saveCompression.value = SuperCompression(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun removeUnlinkedData(save: Boolean) {
|
||||
_removeUnlinkData.value = save
|
||||
}
|
||||
|
||||
fun saveRecycleBin(oldValue: Group?,
|
||||
newValue: Group?,
|
||||
save: Boolean) {
|
||||
_saveRecycleBin.value = SuperGroup(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveTemplatesGroup(oldValue: Group?,
|
||||
newValue: Group?,
|
||||
save: Boolean) {
|
||||
_saveTemplatesGroup.value = SuperGroup(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveMaxHistoryItems(oldValue: Int,
|
||||
newValue: Int,
|
||||
save: Boolean) {
|
||||
_saveMaxHistoryItems.value = SuperInt(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveMaxHistorySize(oldValue: Long,
|
||||
newValue: Long,
|
||||
save: Boolean) {
|
||||
_saveMaxHistorySize.value = SuperLong(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
|
||||
fun saveEncryption(oldValue: EncryptionAlgorithm,
|
||||
newValue: EncryptionAlgorithm,
|
||||
save: Boolean) {
|
||||
_saveEncryption.value = SuperEncryption(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveKeyDerivation(oldValue: KdfEngine,
|
||||
newValue: KdfEngine,
|
||||
save: Boolean) {
|
||||
_saveKeyDerivation.value = SuperKeyDerivation(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveIterations(oldValue: Long,
|
||||
newValue: Long,
|
||||
save: Boolean) {
|
||||
_saveIterations.value = SuperLong(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveMemoryUsage(oldValue: Long,
|
||||
newValue: Long,
|
||||
save: Boolean) {
|
||||
_saveMemoryUsage.value = SuperLong(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
fun saveParallelism(oldValue: Long,
|
||||
newValue: Long,
|
||||
save: Boolean) {
|
||||
_saveParallelism.value = SuperLong(oldValue, newValue, save)
|
||||
}
|
||||
|
||||
data class SuperString(val oldValue: String, val newValue: String, val save: Boolean)
|
||||
data class SuperInt(val oldValue: Int, val newValue: Int, val save: Boolean)
|
||||
data class SuperLong(val oldValue: Long, val newValue: Long, val save: Boolean)
|
||||
data class SuperCompression(val oldValue: CompressionAlgorithm, val newValue: CompressionAlgorithm, val save: Boolean)
|
||||
data class SuperEncryption(val oldValue: EncryptionAlgorithm, val newValue: EncryptionAlgorithm, val save: Boolean)
|
||||
data class SuperKeyDerivation(val oldValue: KdfEngine, val newValue: KdfEngine, val save: Boolean)
|
||||
data class SuperGroup(val oldValue: Group?, val newValue: Group?, val save: Boolean)
|
||||
|
||||
}
|
||||
@@ -19,10 +19,9 @@ import java.util.*
|
||||
|
||||
class EntryEditViewModel: ViewModel() {
|
||||
|
||||
private val mDatabase: Database? = Database.getInstance()
|
||||
|
||||
private var mParent : Group? = null
|
||||
private var mEntry : Entry? = null
|
||||
private var mDatabase: Database? = null
|
||||
private var mParent: Group? = null
|
||||
private var mEntry: Entry? = null
|
||||
private var mIsTemplate: Boolean = false
|
||||
|
||||
private val mTempAttachments = mutableListOf<EntryAttachmentState>()
|
||||
@@ -80,6 +79,10 @@ class EntryEditViewModel: ViewModel() {
|
||||
val onBinaryPreviewLoaded : LiveData<AttachmentPosition> get() = _onBinaryPreviewLoaded
|
||||
private val _onBinaryPreviewLoaded = SingleLiveEvent<AttachmentPosition>()
|
||||
|
||||
fun setDatabase(database: Database?) {
|
||||
this.mDatabase = database
|
||||
}
|
||||
|
||||
fun initializeEntryToUpdate(entryId: NodeId<UUID>,
|
||||
registerInfo: RegisterInfo?,
|
||||
searchInfo: SearchInfo?) {
|
||||
@@ -166,7 +169,7 @@ class EntryEditViewModel: ViewModel() {
|
||||
}
|
||||
}
|
||||
// Set default username
|
||||
username = mDatabase.defaultUsername
|
||||
username = mDatabase?.defaultUsername ?: ""
|
||||
// Warning only the entry recognize is parent, parent don't yet recognize the new entry
|
||||
// Useful to recognize child state (ie: entry is a template)
|
||||
parent = parentGroup
|
||||
@@ -246,7 +249,7 @@ class EntryEditViewModel: ViewModel() {
|
||||
val tempAttachment = tempAttachmentState.attachment
|
||||
mDatabase?.attachmentPool?.let { binaryPool ->
|
||||
if (!newEntry.getAttachments(binaryPool).contains(tempAttachment)) {
|
||||
mDatabase.removeAttachmentIfNotUsed(tempAttachment)
|
||||
mDatabase?.removeAttachmentIfNotUsed(tempAttachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,7 @@ import java.util.*
|
||||
|
||||
class EntryViewModel: ViewModel() {
|
||||
|
||||
private val mDatabase: Database? = Database.getInstance()
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
private var mEntryTemplate: Template? = null
|
||||
private var mEntry: Entry? = null
|
||||
private var mLastEntryVersion: Entry? = null
|
||||
@@ -48,59 +47,69 @@ class EntryViewModel: ViewModel() {
|
||||
val historySelected : LiveData<EntryHistory> get() = _historySelected
|
||||
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
||||
|
||||
fun loadEntry(entryId: NodeId<UUID>, historyPosition: Int) {
|
||||
IOActionTask(
|
||||
{
|
||||
// Manage current version and history
|
||||
mLastEntryVersion = mDatabase?.getEntryById(entryId)
|
||||
fun setDatabase(database: Database?) {
|
||||
mDatabase = database
|
||||
}
|
||||
|
||||
mEntry = if (historyPosition > -1) {
|
||||
mLastEntryVersion?.getHistory()?.get(historyPosition)
|
||||
} else {
|
||||
mLastEntryVersion
|
||||
}
|
||||
fun loadEntry(entryId: NodeId<UUID>?, historyPosition: Int) {
|
||||
if (entryId != null) {
|
||||
IOActionTask(
|
||||
{
|
||||
// Manage current version and history
|
||||
mLastEntryVersion = mDatabase?.getEntryById(entryId)
|
||||
|
||||
mEntryTemplate = mEntry?.let {
|
||||
mDatabase?.getTemplate(it)
|
||||
} ?: Template.STANDARD
|
||||
mEntry = if (historyPosition > -1) {
|
||||
mLastEntryVersion?.getHistory()?.get(historyPosition)
|
||||
} else {
|
||||
mLastEntryVersion
|
||||
}
|
||||
|
||||
mHistoryPosition = historyPosition
|
||||
mEntryTemplate = mEntry?.let {
|
||||
mDatabase?.getTemplate(it)
|
||||
} ?: Template.STANDARD
|
||||
|
||||
// To simplify template field visibility
|
||||
mEntry?.let { entry ->
|
||||
// Add mLastEntryVersion to check the parent and define the template state
|
||||
mDatabase?.decodeEntryWithTemplateConfiguration(entry, mLastEntryVersion)?.let {
|
||||
// To update current modification time
|
||||
it.touch(modified = false, touchParents = false)
|
||||
mHistoryPosition = historyPosition
|
||||
|
||||
// Build history info
|
||||
val entryInfoHistory = it.getHistory().map { entryHistory ->
|
||||
entryHistory.getEntryInfo(mDatabase)
|
||||
}
|
||||
// To simplify template field visibility
|
||||
mEntry?.let { entry ->
|
||||
// Add mLastEntryVersion to check the parent and define the template state
|
||||
mDatabase?.decodeEntryWithTemplateConfiguration(entry, mLastEntryVersion)
|
||||
?.let {
|
||||
// To update current modification time
|
||||
it.touch(modified = false, touchParents = false)
|
||||
|
||||
EntryInfoHistory(
|
||||
mEntryTemplate ?: Template.STANDARD,
|
||||
it.getEntryInfo(mDatabase),
|
||||
entryInfoHistory
|
||||
)
|
||||
// Build history info
|
||||
val entryInfoHistory = it.getHistory().map { entryHistory ->
|
||||
entryHistory.getEntryInfo(mDatabase)
|
||||
}
|
||||
|
||||
EntryInfoHistory(
|
||||
mEntryTemplate ?: Template.STANDARD,
|
||||
it.getEntryInfo(mDatabase),
|
||||
entryInfoHistory
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
{ entryInfoHistory ->
|
||||
if (entryInfoHistory != null) {
|
||||
_template.value = entryInfoHistory.template
|
||||
_entryInfo.value = entryInfoHistory.entryInfo
|
||||
_entryIsHistory.value = mHistoryPosition != -1
|
||||
_entryHistory.value = entryInfoHistory.entryHistory
|
||||
}
|
||||
}
|
||||
},
|
||||
{ entryInfoHistory ->
|
||||
if (entryInfoHistory != null) {
|
||||
_template.value = entryInfoHistory.template
|
||||
_entryInfo.value = entryInfoHistory.entryInfo
|
||||
_entryIsHistory.value = mHistoryPosition != -1
|
||||
_entryHistory.value = entryInfoHistory.entryHistory
|
||||
}
|
||||
}
|
||||
).execute()
|
||||
).execute()
|
||||
} else {
|
||||
mEntryTemplate = null
|
||||
mEntry = null
|
||||
mLastEntryVersion = null
|
||||
mHistoryPosition = -1
|
||||
}
|
||||
}
|
||||
|
||||
fun updateEntry() {
|
||||
mEntry?.nodeId?.let { nodeId ->
|
||||
loadEntry(nodeId, mHistoryPosition)
|
||||
}
|
||||
loadEntry(mEntry?.nodeId, mHistoryPosition)
|
||||
}
|
||||
|
||||
// TODO Remove
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.kunzisoft.keepass.viewmodels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.kunzisoft.keepass.app.database.IOActionTask
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.element.Group
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
|
||||
|
||||
class GroupViewModel: ViewModel() {
|
||||
|
||||
private var mDatabase: Database? = null
|
||||
|
||||
val group : LiveData<SuperGroup> get() = _group
|
||||
private val _group = MutableLiveData<SuperGroup>()
|
||||
|
||||
val onGroupSelected : LiveData<SuperGroup> get() = _onGroupSelected
|
||||
private val _onGroupSelected = SingleLiveEvent<SuperGroup>()
|
||||
|
||||
fun setDatabase(database: Database?) {
|
||||
this.mDatabase = database
|
||||
}
|
||||
|
||||
fun loadGroup(groupId: NodeId<*>?) {
|
||||
IOActionTask(
|
||||
{
|
||||
if (groupId != null) {
|
||||
mDatabase?.getGroupById(groupId)
|
||||
} else {
|
||||
mDatabase?.rootGroup
|
||||
}
|
||||
},
|
||||
{ group ->
|
||||
if (group != null) {
|
||||
_group.value = SuperGroup(group, mDatabase?.recycleBin == group)
|
||||
}
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun loadGroup(group: Group) {
|
||||
_group.value = SuperGroup(group, mDatabase?.recycleBin == group)
|
||||
}
|
||||
|
||||
fun loadGroupFromSearch(searchQuery: String,
|
||||
omitBackup: Boolean) {
|
||||
IOActionTask(
|
||||
{
|
||||
mDatabase?.createVirtualGroupFromSearch(searchQuery, omitBackup)
|
||||
},
|
||||
{ group ->
|
||||
if (group != null) {
|
||||
_group.value = SuperGroup(group, mDatabase?.recycleBin == group)
|
||||
}
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun selectGroup(group: Group) {
|
||||
_onGroupSelected.value = SuperGroup(group, mDatabase?.recycleBin == group)
|
||||
}
|
||||
|
||||
data class SuperGroup(val group: Group, val isRecycleBin: Boolean)
|
||||
|
||||
companion object {
|
||||
private val TAG = GroupViewModel::class.java.name
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user