Merge branch 'feature/Manually_Save_Satabase' into develop

This commit is contained in:
J-Jamet
2019-11-19 19:18:00 +01:00
31 changed files with 1186 additions and 1070 deletions

View File

@@ -329,9 +329,9 @@ class EntryActivity : LockingHideActivity() {
val inflater = menuInflater val inflater = menuInflater
MenuUtil.contributionMenuInflater(inflater, menu) MenuUtil.contributionMenuInflater(inflater, menu)
inflater.inflate(R.menu.entry, menu) inflater.inflate(R.menu.entry, menu)
inflater.inflate(R.menu.database_lock, menu) inflater.inflate(R.menu.database, menu)
if (mReadOnly) { if (mReadOnly) {
menu.findItem(R.id.menu_save_database)?.isVisible = false
menu.findItem(R.id.menu_edit)?.isVisible = false menu.findItem(R.id.menu_edit)?.isVisible = false
} }
@@ -400,21 +400,18 @@ class EntryActivity : LockingHideActivity() {
MenuUtil.onContributionItemSelected(this) MenuUtil.onContributionItemSelected(this)
return true return true
} }
R.id.menu_toggle_pass -> { R.id.menu_toggle_pass -> {
mShowPassword = !mShowPassword mShowPassword = !mShowPassword
changeShowPasswordIcon(item) changeShowPasswordIcon(item)
entryContentsView?.setHiddenPasswordStyle(!mShowPassword) entryContentsView?.setHiddenPasswordStyle(!mShowPassword)
return true return true
} }
R.id.menu_edit -> { R.id.menu_edit -> {
mEntry?.let { mEntry?.let {
EntryEditActivity.launch(this@EntryActivity, it) EntryEditActivity.launch(this@EntryActivity, it)
} }
return true return true
} }
R.id.menu_goto_url -> { R.id.menu_goto_url -> {
var url: String = mEntry?.url ?: "" var url: String = mEntry?.url ?: ""
@@ -424,18 +421,17 @@ class EntryActivity : LockingHideActivity() {
} }
UriUtil.gotoUrl(this, url) UriUtil.gotoUrl(this, url)
return true return true
} }
R.id.menu_lock -> { R.id.menu_lock -> {
lockAndExit() lockAndExit()
return true return true
} }
R.id.menu_save_database -> {
mProgressDialogThread?.startDatabaseSave(!mReadOnly)
}
android.R.id.home -> finish() // close this activity and return to preview activity (if there is any) android.R.id.home -> finish() // close this activity and return to preview activity (if there is any)
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }

View File

@@ -33,7 +33,6 @@ import com.kunzisoft.keepass.activities.dialogs.SetOTPDialogFragment
import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.activities.lock.LockingHideActivity import com.kunzisoft.keepass.activities.lock.LockingHideActivity
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.EntryEditActivityEducation import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
@@ -67,9 +66,6 @@ class EntryEditActivity : LockingHideActivity(),
private var entryEditContentsView: EntryEditContentsView? = null private var entryEditContentsView: EntryEditContentsView? = null
private var saveView: View? = null private var saveView: View? = null
// Dialog thread
private var progressDialogThread: ProgressDialogThread? = null
// Education // Education
private var entryEditActivityEducation: EntryEditActivityEducation? = null private var entryEditActivityEducation: EntryEditActivityEducation? = null
@@ -176,7 +172,7 @@ class EntryEditActivity : LockingHideActivity(),
entryEditActivityEducation = EntryEditActivityEducation(this) entryEditActivityEducation = EntryEditActivityEducation(this)
// Create progress dialog // Create progress dialog
progressDialogThread = ProgressDialogThread(this) { actionTask, result -> mProgressDialogThread?.onActionFinish = { actionTask, result ->
when (actionTask) { when (actionTask) {
ACTION_DATABASE_CREATE_ENTRY_TASK, ACTION_DATABASE_CREATE_ENTRY_TASK,
ACTION_DATABASE_UPDATE_ENTRY_TASK -> { ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
@@ -273,18 +269,18 @@ class EntryEditActivity : LockingHideActivity(),
// Open a progress dialog and save entry // Open a progress dialog and save entry
if (mIsNew) { if (mIsNew) {
mParent?.let { parent -> mParent?.let { parent ->
progressDialogThread?.startDatabaseCreateEntry( mProgressDialogThread?.startDatabaseCreateEntry(
newEntry, newEntry,
parent, parent,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} }
} else { } else {
mEntry?.let { oldEntry -> mEntry?.let { oldEntry ->
progressDialogThread?.startDatabaseUpdateEntry( mProgressDialogThread?.startDatabaseUpdateEntry(
oldEntry, oldEntry,
newEntry, newEntry,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} }
} }
@@ -292,23 +288,13 @@ class EntryEditActivity : LockingHideActivity(),
} }
} }
override fun onResume() {
super.onResume()
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu) super.onCreateOptionsMenu(menu)
val inflater = menuInflater val inflater = menuInflater
inflater.inflate(R.menu.database_lock, menu) inflater.inflate(R.menu.database, menu)
// Save database not needed here
menu.findItem(R.id.menu_save_database)?.isVisible = false
MenuUtil.contributionMenuInflater(inflater, menu) MenuUtil.contributionMenuInflater(inflater, menu)
if (mDatabase?.allowOTP == true) if (mDatabase?.allowOTP == true)
inflater.inflate(R.menu.entry_otp, menu) inflater.inflate(R.menu.entry_otp, menu)
@@ -352,12 +338,13 @@ class EntryEditActivity : LockingHideActivity(),
lockAndExit() lockAndExit()
return true return true
} }
R.id.menu_save_database -> {
mProgressDialogThread?.startDatabaseSave(!mReadOnly)
}
R.id.menu_contribute -> { R.id.menu_contribute -> {
MenuUtil.onContributionItemSelected(this) MenuUtil.onContributionItemSelected(this)
return true return true
} }
R.id.menu_add_otp -> { R.id.menu_add_otp -> {
// Retrieve the current otpElement if exists // Retrieve the current otpElement if exists
// and open the dialog to set up the OTP // and open the dialog to set up the OTP
@@ -365,7 +352,6 @@ class EntryEditActivity : LockingHideActivity(),
.show(supportFragmentManager, "addOTPDialog") .show(supportFragmentManager, "addOTPDialog")
return true return true
} }
android.R.id.home -> finish() android.R.id.home -> finish()
} }

View File

@@ -76,7 +76,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
private var mOpenFileHelper: OpenFileHelper? = null private var mOpenFileHelper: OpenFileHelper? = null
private var progressDialogThread: ProgressDialogThread? = null private var mProgressDialogThread: ProgressDialogThread? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -163,7 +163,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
} }
// Attach the dialog thread to this activity // Attach the dialog thread to this activity
progressDialogThread = ProgressDialogThread(this) { actionTask, _ -> mProgressDialogThread?.onActionFinish = { actionTask, _ ->
when (actionTask) { when (actionTask) {
ACTION_DATABASE_CREATE_TASK -> { ACTION_DATABASE_CREATE_TASK -> {
// TODO Check // TODO Check
@@ -296,12 +296,12 @@ class FileDatabaseSelectActivity : StylishActivity(),
} }
// Register progress task // Register progress task
progressDialogThread?.registerProgressTask() mProgressDialogThread?.registerProgressTask()
} }
override fun onPause() { override fun onPause() {
// Unregister progress task // Unregister progress task
progressDialogThread?.unregisterProgressTask() mProgressDialogThread?.unregisterProgressTask()
super.onPause() super.onPause()
} }
@@ -329,7 +329,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
mDatabaseFileUri?.let { databaseUri -> mDatabaseFileUri?.let { databaseUri ->
// Create the new database // Create the new database
progressDialogThread?.startDatabaseCreate( mProgressDialogThread?.startDatabaseCreate(
databaseUri, databaseUri,
masterPasswordChecked, masterPasswordChecked,
masterPassword, masterPassword,

View File

@@ -49,7 +49,6 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter import com.kunzisoft.keepass.adapters.SearchEntryCursorAdapter
import com.kunzisoft.keepass.autofill.AutofillHelper import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.SortNodeEnum import com.kunzisoft.keepass.database.SortNodeEnum
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.* import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.education.GroupActivityEducation import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.icons.assignDatabaseIcon import com.kunzisoft.keepass.icons.assignDatabaseIcon
@@ -94,8 +93,6 @@ class GroupActivity : LockingActivity(),
private var mListNodesFragment: ListNodesFragment? = null private var mListNodesFragment: ListNodesFragment? = null
private var mCurrentGroupIsASearch: Boolean = false private var mCurrentGroupIsASearch: Boolean = false
private var progressDialogThread: ProgressDialogThread? = null
// Nodes // Nodes
private var mRootGroup: GroupVersioned? = null private var mRootGroup: GroupVersioned? = null
private var mCurrentGroup: GroupVersioned? = null private var mCurrentGroup: GroupVersioned? = null
@@ -205,7 +202,7 @@ class GroupActivity : LockingActivity(),
mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, database) mSearchSuggestionAdapter = SearchEntryCursorAdapter(this, database)
// Init dialog thread // Init dialog thread
progressDialogThread = ProgressDialogThread(this) { actionTask, result -> mProgressDialogThread?.onActionFinish = { actionTask, result ->
var oldNodes: List<NodeVersioned> = ArrayList() var oldNodes: List<NodeVersioned> = ArrayList()
result.data?.getBundle(OLD_NODES_KEY)?.let { oldNodesBundle -> result.data?.getBundle(OLD_NODES_KEY)?.let { oldNodesBundle ->
@@ -569,20 +566,20 @@ class GroupActivity : LockingActivity(),
ListNodesFragment.PasteMode.PASTE_FROM_COPY -> { ListNodesFragment.PasteMode.PASTE_FROM_COPY -> {
// Copy // Copy
mCurrentGroup?.let { newParent -> mCurrentGroup?.let { newParent ->
progressDialogThread?.startDatabaseCopyNodes( mProgressDialogThread?.startDatabaseCopyNodes(
nodes, nodes,
newParent, newParent,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} }
} }
ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> { ListNodesFragment.PasteMode.PASTE_FROM_MOVE -> {
// Move // Move
mCurrentGroup?.let { newParent -> mCurrentGroup?.let { newParent ->
progressDialogThread?.startDatabaseMoveNodes( mProgressDialogThread?.startDatabaseMoveNodes(
nodes, nodes,
newParent, newParent,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} }
} }
@@ -598,9 +595,9 @@ class GroupActivity : LockingActivity(),
&& database.isRecycleBinEnabled && database.isRecycleBinEnabled
&& database.recycleBin != mCurrentGroup) { && database.recycleBin != mCurrentGroup) {
// If recycle bin enabled and not in recycle bin, move in recycle bin // If recycle bin enabled and not in recycle bin, move in recycle bin
progressDialogThread?.startDatabaseDeleteNodes( mProgressDialogThread?.startDatabaseDeleteNodes(
nodes, nodes,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} else { } else {
// open the dialog to confirm deletion // open the dialog to confirm deletion
@@ -612,9 +609,9 @@ class GroupActivity : LockingActivity(),
} }
override fun permanentlyDeleteNodes(nodes: List<NodeVersioned>) { override fun permanentlyDeleteNodes(nodes: List<NodeVersioned>) {
progressDialogThread?.startDatabaseDeleteNodes( mProgressDialogThread?.startDatabaseDeleteNodes(
nodes, nodes,
!mReadOnly !mReadOnly && mAutoSaveEnable
) )
} }
@@ -624,13 +621,9 @@ class GroupActivity : LockingActivity(),
assignGroupViewElements() assignGroupViewElements()
// Refresh suggestions to change preferences // Refresh suggestions to change preferences
mSearchSuggestionAdapter?.reInit(this) mSearchSuggestionAdapter?.reInit(this)
progressDialogThread?.registerProgressTask()
} }
override fun onPause() { override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause() super.onPause()
finishNodeAction() finishNodeAction()
@@ -640,7 +633,10 @@ class GroupActivity : LockingActivity(),
val inflater = menuInflater val inflater = menuInflater
inflater.inflate(R.menu.search, menu) inflater.inflate(R.menu.search, menu)
inflater.inflate(R.menu.database_lock, menu) inflater.inflate(R.menu.database, menu)
if (mReadOnly) {
menu.findItem(R.id.menu_save_database)?.isVisible = false
}
if (!mSelectionMode) { if (!mSelectionMode) {
inflater.inflate(R.menu.default_menu, menu) inflater.inflate(R.menu.default_menu, menu)
MenuUtil.contributionMenuInflater(inflater, menu) MenuUtil.contributionMenuInflater(inflater, menu)
@@ -760,6 +756,10 @@ class GroupActivity : LockingActivity(),
lockAndExit() lockAndExit()
return true return true
} }
R.id.menu_save_database -> {
mProgressDialogThread?.startDatabaseSave(!mReadOnly)
return true
}
R.id.menu_empty_recycle_bin -> { R.id.menu_empty_recycle_bin -> {
mCurrentGroup?.getChildren()?.let { listChildren -> mCurrentGroup?.getChildren()?.let { listChildren ->
// Automatically delete all elements // Automatically delete all elements
@@ -791,8 +791,11 @@ class GroupActivity : LockingActivity(),
// Not really needed here because added in runnable but safe // Not really needed here because added in runnable but safe
newGroup.parent = currentGroup newGroup.parent = currentGroup
progressDialogThread?.startDatabaseCreateGroup( mProgressDialogThread?.startDatabaseCreateGroup(
newGroup, currentGroup, !mReadOnly) newGroup,
currentGroup,
!mReadOnly && mAutoSaveEnable
)
} }
} }
} }
@@ -810,8 +813,11 @@ class GroupActivity : LockingActivity(),
} }
} }
// If group updated save it in the database // If group updated save it in the database
progressDialogThread?.startDatabaseUpdateGroup( mProgressDialogThread?.startDatabaseUpdateGroup(
oldGroupToUpdate, updateGroup, !mReadOnly) oldGroupToUpdate,
updateGroup,
!mReadOnly && mAutoSaveEnable
)
} }
} }
else -> {} else -> {}

View File

@@ -163,7 +163,8 @@ class PasswordActivity : StylishActivity() {
enableOrNotTheConfirmationButton() enableOrNotTheConfirmationButton()
} }
progressDialogThread = ProgressDialogThread(this) { actionTask, result -> progressDialogThread = ProgressDialogThread(this).apply {
onActionFinish = { actionTask, result ->
when (actionTask) { when (actionTask) {
ACTION_DATABASE_LOAD_TASK -> { ACTION_DATABASE_LOAD_TASK -> {
// Recheck biometric if error // Recheck biometric if error
@@ -231,6 +232,7 @@ class PasswordActivity : StylishActivity() {
} }
} }
} }
}
private fun launchGroupActivity() { private fun launchGroupActivity() {
EntrySelectionHelper.doEntrySelectionAction(intent, EntrySelectionHelper.doEntrySelectionAction(intent,

View File

@@ -32,6 +32,7 @@ import android.view.ViewGroup
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.stylish.StylishActivity import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
import com.kunzisoft.keepass.magikeyboard.MagikIME import com.kunzisoft.keepass.magikeyboard.MagikIME
@@ -63,6 +64,10 @@ abstract class LockingActivity : StylishActivity() {
return field || mSelectionMode return field || mSelectionMode
} }
protected var mSelectionMode: Boolean = false protected var mSelectionMode: Boolean = false
protected var mAutoSaveEnable: Boolean = true
var mProgressDialogThread: ProgressDialogThread? = null
private set
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -86,6 +91,8 @@ abstract class LockingActivity : StylishActivity() {
mExitLock = false mExitLock = false
mReadOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent) mReadOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrIntent(savedInstanceState, intent)
mProgressDialogThread = ProgressDialogThread(this)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@@ -101,8 +108,13 @@ abstract class LockingActivity : StylishActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
mProgressDialogThread?.registerProgressTask()
// To refresh when back to normal workflow from selection workflow // To refresh when back to normal workflow from selection workflow
mSelectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(intent) mSelectionMode = EntrySelectionHelper.retrieveEntrySelectionModeFromIntent(intent)
mAutoSaveEnable = PreferencesUtil.isAutoSaveDatabaseEnabled(this)
invalidateOptionsMenu()
if (mTimeoutEnable) { if (mTimeoutEnable) {
// End activity if database not loaded // End activity if database not loaded
@@ -119,8 +131,6 @@ abstract class LockingActivity : StylishActivity() {
if (!mExitLock) if (!mExitLock)
TimeoutHelper.recordTime(this) TimeoutHelper.recordTime(this)
} }
invalidateOptionsMenu()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@@ -130,6 +140,8 @@ abstract class LockingActivity : StylishActivity() {
} }
override fun onPause() { override fun onPause() {
mProgressDialogThread?.unregisterProgressTask()
super.onPause() super.onPause()
if (mTimeoutEnable) { if (mTimeoutEnable) {

View File

@@ -21,18 +21,18 @@ import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Compa
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_LOAD_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_MOVE_NODES_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COLOR_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_COLOR_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COMPRESSION_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_COMPRESSION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DESCRIPTION_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_DESCRIPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ENCRYPTION_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENCRYPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ITERATIONS_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ITERATIONS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_NAME_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_NAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_PARALLELISM_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_PARALLELISM_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_GROUP_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getBundleFromListNodes import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.getBundleFromListNodes
@@ -45,10 +45,10 @@ import com.kunzisoft.keepass.utils.DATABASE_STOP_TASK_ACTION
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ProgressDialogThread(private val activity: FragmentActivity) {
class ProgressDialogThread(private val activity: FragmentActivity, var onActionFinish: ((actionTask: String,
var onActionFinish: (actionTask: String, result: ActionRunnable.Result) -> Unit)? = null
result: ActionRunnable.Result) -> Unit) {
private var intentDatabaseTask = Intent(activity, DatabaseTaskNotificationService::class.java) private var intentDatabaseTask = Intent(activity, DatabaseTaskNotificationService::class.java)
@@ -69,7 +69,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
} }
override fun onStopAction(actionTask: String, result: ActionRunnable.Result) { override fun onStopAction(actionTask: String, result: ActionRunnable.Result) {
onActionFinish.invoke(actionTask, result) onActionFinish?.invoke(actionTask, result)
// Remove the progress task // Remove the progress task
ProgressTaskDialogFragment.stop(activity) ProgressTaskDialogFragment.stop(activity)
TimeoutHelper.releaseTemporarilyDisableTimeoutAndLockIfTimeout(activity) TimeoutHelper.releaseTemporarilyDisableTimeoutAndLockIfTimeout(activity)
@@ -357,7 +357,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newName) putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newName)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_NAME_TASK) , ACTION_DATABASE_UPDATE_NAME_TASK)
} }
fun startDatabaseSaveDescription(oldDescription: String, fun startDatabaseSaveDescription(oldDescription: String,
@@ -368,7 +368,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDescription) putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDescription)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_DESCRIPTION_TASK) , ACTION_DATABASE_UPDATE_DESCRIPTION_TASK)
} }
fun startDatabaseSaveDefaultUsername(oldDefaultUsername: String, fun startDatabaseSaveDefaultUsername(oldDefaultUsername: String,
@@ -379,7 +379,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDefaultUsername) putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newDefaultUsername)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK) , ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK)
} }
fun startDatabaseSaveColor(oldColor: String, fun startDatabaseSaveColor(oldColor: String,
@@ -390,7 +390,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newColor) putString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newColor)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_COLOR_TASK) , ACTION_DATABASE_UPDATE_COLOR_TASK)
} }
fun startDatabaseSaveCompression(oldCompression: PwCompressionAlgorithm, fun startDatabaseSaveCompression(oldCompression: PwCompressionAlgorithm,
@@ -401,7 +401,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newCompression) putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newCompression)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_COMPRESSION_TASK) , ACTION_DATABASE_UPDATE_COMPRESSION_TASK)
} }
fun startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems: Int, fun startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems: Int,
@@ -412,7 +412,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistoryItems) putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistoryItems)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK) , ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK)
} }
fun startDatabaseSaveMaxHistorySize(oldMaxHistorySize: Long, fun startDatabaseSaveMaxHistorySize(oldMaxHistorySize: Long,
@@ -423,7 +423,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistorySize) putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMaxHistorySize)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK) , ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK)
} }
/* /*
@@ -440,7 +440,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newEncryption) putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newEncryption)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_ENCRYPTION_TASK) , ACTION_DATABASE_UPDATE_ENCRYPTION_TASK)
} }
fun startDatabaseSaveKeyDerivation(oldKeyDerivation: KdfEngine, fun startDatabaseSaveKeyDerivation(oldKeyDerivation: KdfEngine,
@@ -451,7 +451,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newKeyDerivation) putSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newKeyDerivation)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK) , ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK)
} }
fun startDatabaseSaveIterations(oldIterations: Long, fun startDatabaseSaveIterations(oldIterations: Long,
@@ -462,7 +462,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newIterations) putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newIterations)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_ITERATIONS_TASK) , ACTION_DATABASE_UPDATE_ITERATIONS_TASK)
} }
fun startDatabaseSaveMemoryUsage(oldMemoryUsage: Long, fun startDatabaseSaveMemoryUsage(oldMemoryUsage: Long,
@@ -473,7 +473,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMemoryUsage) putLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newMemoryUsage)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK) , ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK)
} }
fun startDatabaseSaveParallelism(oldParallelism: Int, fun startDatabaseSaveParallelism(oldParallelism: Int,
@@ -484,7 +484,7 @@ class ProgressDialogThread(private val activity: FragmentActivity,
putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism) putInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY, newParallelism)
putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save) putBoolean(DatabaseTaskNotificationService.SAVE_DATABASE_KEY, save)
} }
, ACTION_DATABASE_SAVE_PARALLELISM_TASK) , ACTION_DATABASE_UPDATE_PARALLELISM_TASK)
} }
/** /**

View File

@@ -70,17 +70,28 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
val intentAction = intent.action val intentAction = intent.action
var saveAction = true
if (intent.hasExtra(SAVE_DATABASE_KEY)) {
saveAction = intent.getBooleanExtra(SAVE_DATABASE_KEY, saveAction)
}
val titleId: Int = when (intentAction) { val titleId: Int = when (intentAction) {
ACTION_DATABASE_CREATE_TASK -> R.string.creating_database ACTION_DATABASE_CREATE_TASK -> R.string.creating_database
ACTION_DATABASE_LOAD_TASK -> R.string.loading_database ACTION_DATABASE_LOAD_TASK -> R.string.loading_database
else -> R.string.saving_database else -> {
if (saveAction)
R.string.saving_database
else
R.string.command_execution
}
} }
val messageId: Int? = when (intentAction) { val messageId: Int? = when (intentAction) {
ACTION_DATABASE_LOAD_TASK -> null ACTION_DATABASE_LOAD_TASK -> null
else -> null else -> null
} }
val warningId: Int? = val warningId: Int? =
if (intentAction == ACTION_DATABASE_LOAD_TASK) if (!saveAction
|| intentAction == ACTION_DATABASE_LOAD_TASK)
null null
else else
R.string.do_not_kill_app R.string.do_not_kill_app
@@ -96,19 +107,20 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
ACTION_DATABASE_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent) ACTION_DATABASE_COPY_NODES_TASK -> buildDatabaseCopyNodesActionTask(intent)
ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent) ACTION_DATABASE_MOVE_NODES_TASK -> buildDatabaseMoveNodesActionTask(intent)
ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent) ACTION_DATABASE_DELETE_NODES_TASK -> buildDatabaseDeleteNodesActionTask(intent)
ACTION_DATABASE_SAVE_NAME_TASK, ACTION_DATABASE_UPDATE_NAME_TASK,
ACTION_DATABASE_SAVE_DESCRIPTION_TASK, ACTION_DATABASE_UPDATE_DESCRIPTION_TASK,
ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK, ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK,
ACTION_DATABASE_SAVE_COLOR_TASK, ACTION_DATABASE_UPDATE_COLOR_TASK,
ACTION_DATABASE_SAVE_COMPRESSION_TASK, ACTION_DATABASE_UPDATE_COMPRESSION_TASK,
ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK, ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK,
ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK, ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK,
ACTION_DATABASE_SAVE_ENCRYPTION_TASK, ACTION_DATABASE_UPDATE_ENCRYPTION_TASK,
ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK, ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK,
ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK, ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK,
ACTION_DATABASE_SAVE_PARALLELISM_TASK, ACTION_DATABASE_UPDATE_PARALLELISM_TASK,
ACTION_DATABASE_SAVE_ITERATIONS_TASK -> buildDatabaseSaveElementActionTask(intent) ACTION_DATABASE_UPDATE_ITERATIONS_TASK -> buildDatabaseUpdateElementActionTask(intent)
else -> buildDatabaseSave(intent) ACTION_DATABASE_SAVE -> buildDatabaseSave(intent)
else -> null
} }
actionRunnable?.let { actionRunnableNotNull -> actionRunnable?.let { actionRunnableNotNull ->
@@ -397,7 +409,7 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
} }
} }
private fun buildDatabaseSaveElementActionTask(intent: Intent): ActionRunnable? { private fun buildDatabaseUpdateElementActionTask(intent: Intent): ActionRunnable? {
return if (intent.hasExtra(SAVE_DATABASE_KEY)) { return if (intent.hasExtra(SAVE_DATABASE_KEY)) {
return SaveDatabaseRunnable(this, return SaveDatabaseRunnable(this,
Database.getInstance(), Database.getInstance(),
@@ -472,18 +484,18 @@ class DatabaseTaskNotificationService : NotificationService(), ProgressTaskUpdat
const val ACTION_DATABASE_COPY_NODES_TASK = "ACTION_DATABASE_COPY_NODES_TASK" const val ACTION_DATABASE_COPY_NODES_TASK = "ACTION_DATABASE_COPY_NODES_TASK"
const val ACTION_DATABASE_MOVE_NODES_TASK = "ACTION_DATABASE_MOVE_NODES_TASK" const val ACTION_DATABASE_MOVE_NODES_TASK = "ACTION_DATABASE_MOVE_NODES_TASK"
const val ACTION_DATABASE_DELETE_NODES_TASK = "ACTION_DATABASE_DELETE_NODES_TASK" const val ACTION_DATABASE_DELETE_NODES_TASK = "ACTION_DATABASE_DELETE_NODES_TASK"
const val ACTION_DATABASE_SAVE_NAME_TASK = "ACTION_DATABASE_SAVE_NAME_TASK" const val ACTION_DATABASE_UPDATE_NAME_TASK = "ACTION_DATABASE_UPDATE_NAME_TASK"
const val ACTION_DATABASE_SAVE_DESCRIPTION_TASK = "ACTION_DATABASE_SAVE_DESCRIPTION_TASK" const val ACTION_DATABASE_UPDATE_DESCRIPTION_TASK = "ACTION_DATABASE_UPDATE_DESCRIPTION_TASK"
const val ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK = "ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK" const val ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK = "ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK"
const val ACTION_DATABASE_SAVE_COLOR_TASK = "ACTION_DATABASE_SAVE_COLOR_TASK" const val ACTION_DATABASE_UPDATE_COLOR_TASK = "ACTION_DATABASE_UPDATE_COLOR_TASK"
const val ACTION_DATABASE_SAVE_COMPRESSION_TASK = "ACTION_DATABASE_SAVE_COMPRESSION_TASK" const val ACTION_DATABASE_UPDATE_COMPRESSION_TASK = "ACTION_DATABASE_UPDATE_COMPRESSION_TASK"
const val ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK = "ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK" const val ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK"
const val ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK = "ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK" const val ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK = "ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK"
const val ACTION_DATABASE_SAVE_ENCRYPTION_TASK = "ACTION_DATABASE_SAVE_ENCRYPTION_TASK" const val ACTION_DATABASE_UPDATE_ENCRYPTION_TASK = "ACTION_DATABASE_UPDATE_ENCRYPTION_TASK"
const val ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK = "ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK" const val ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK = "ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK"
const val ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK = "ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK" const val ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK = "ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK"
const val ACTION_DATABASE_SAVE_PARALLELISM_TASK = "ACTION_DATABASE_SAVE_PARALLELISM_TASK" const val ACTION_DATABASE_UPDATE_PARALLELISM_TASK = "ACTION_DATABASE_UPDATE_PARALLELISM_TASK"
const val ACTION_DATABASE_SAVE_ITERATIONS_TASK = "ACTION_DATABASE_SAVE_ITERATIONS_TASK" const val ACTION_DATABASE_UPDATE_ITERATIONS_TASK = "ACTION_DATABASE_UPDATE_ITERATIONS_TASK"
const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE" const val ACTION_DATABASE_SAVE = "ACTION_DATABASE_SAVE"
const val DATABASE_URI_KEY = "DATABASE_URI_KEY" const val DATABASE_URI_KEY = "DATABASE_URI_KEY"

View File

@@ -0,0 +1,351 @@
package com.kunzisoft.keepass.settings
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.view.autofill.AutofillManager
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricManager
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.SwitchPreference
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.ProFeatureDialogFragment
import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment
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.BiometricUnlockDatabaseHelper
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
import com.kunzisoft.keepass.utils.UriUtil
class NestedAppSettingsFragment : NestedSettingsFragment() {
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
// Load the preferences from an XML resource
when (screen) {
Screen.APPLICATION -> {
onCreateApplicationPreferences(rootKey)
}
Screen.FORM_FILLING -> {
onCreateFormFillingPreference(rootKey)
}
Screen.ADVANCED_UNLOCK -> {
onCreateAdvancedUnlockPreferences(rootKey)
}
Screen.APPEARANCE -> {
onCreateAppearancePreferences(rootKey)
}
else -> {}
}
}
private fun onCreateApplicationPreferences(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_application, rootKey)
activity?.let { activity ->
allowCopyPassword()
findPreference<Preference>(getString(R.string.keyfile_key))?.setOnPreferenceChangeListener { _, newValue ->
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAllKeyFiles()
}
true
}
findPreference<Preference>(getString(R.string.recentfile_key))?.setOnPreferenceChangeListener { _, newValue ->
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAll()
}
true
}
}
}
private fun onCreateFormFillingPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_form_filling, rootKey)
activity?.let { activity ->
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val autofillManager = activity.getSystemService(AutofillManager::class.java)
if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
autoFillEnablePreference?.isChecked = autofillManager.hasEnabledAutofillServices()
autoFillEnablePreference?.onPreferenceClickListener = object : Preference.OnPreferenceClickListener {
@RequiresApi(api = Build.VERSION_CODES.O)
override fun onPreferenceClick(preference: Preference): Boolean {
if ((preference as SwitchPreference).isChecked) {
try {
startEnableService()
} catch (e: ActivityNotFoundException) {
val error = getString(R.string.error_autofill_enable_service)
preference.isChecked = false
Log.d(javaClass.name, error, e)
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
}
} else {
disableService()
}
return false
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun disableService() {
if (autofillManager != null && autofillManager.hasEnabledAutofillServices()) {
autofillManager.disableAutofillServices()
} else {
Log.d(javaClass.name, "Sample service already disabled.")
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Throws(ActivityNotFoundException::class)
private fun startEnableService() {
if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
// TODO Autofill
intent.data = Uri.parse("package:com.example.android.autofill.service")
Log.d(javaClass.name, "enableService(): intent=$intent")
startActivityForResult(intent, REQUEST_CODE_AUTOFILL)
} else {
Log.d(javaClass.name, "Sample service already enabled.")
}
}
}
} else {
autoFillEnablePreference?.setOnPreferenceClickListener { preference ->
(preference as SwitchPreference).isChecked = false
val fragmentManager = fragmentManager!!
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.O)
.show(fragmentManager, "unavailableFeatureDialog")
false
}
}
}
findPreference<Preference>(getString(R.string.magic_keyboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.magic_keyboard_explanation_url)
false
}
findPreference<Preference>(getString(R.string.magic_keyboard_key))?.setOnPreferenceClickListener {
startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
})
false
}
findPreference<Preference>(getString(R.string.magic_keyboard_preference_key))?.setOnPreferenceClickListener {
startActivity(Intent(context, MagikIMESettings::class.java))
false
}
findPreference<Preference>(getString(R.string.clipboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.clipboard_explanation_url)
false
}
findPreference<Preference>(getString(R.string.autofill_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.autofill_explanation_url)
false
}
// Present in two places
allowCopyPassword()
}
private fun allowCopyPassword() {
val copyPasswordPreference: SwitchPreference? = findPreference(getString(R.string.allow_copy_password_key))
copyPasswordPreference?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean && context != null) {
val message = getString(R.string.allow_copy_password_warning) +
"\n\n" +
getString(R.string.clipboard_warning)
AlertDialog
.Builder(context!!)
.setMessage(message)
.create()
.apply {
setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable))
{ dialog, _ ->
dialog.dismiss()
}
setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable))
{ dialog, _ ->
copyPasswordPreference.isChecked = false
dialog.dismiss()
}
show()
}
}
true
}
}
private fun onCreateAdvancedUnlockPreferences(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_advanced_unlock, rootKey)
activity?.let { activity ->
val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key))
// < M solve verifyError exception
var biometricUnlockSupported = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val biometricCanAuthenticate = BiometricManager.from(activity).canAuthenticate()
biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
}
if (!biometricUnlockSupported) {
// False if under Marshmallow
biometricUnlockEnablePreference?.apply {
isChecked = false
setOnPreferenceClickListener { preference ->
fragmentManager?.let { fragmentManager ->
(preference as SwitchPreference).isChecked = false
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
.show(fragmentManager, "unavailableFeatureDialog")
}
false
}
}
}
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
if (!biometricUnlockSupported) {
deleteKeysFingerprints?.isEnabled = false
} else {
deleteKeysFingerprints?.setOnPreferenceClickListener {
context?.let { context ->
AlertDialog.Builder(context)
.setMessage(resources.getString(R.string.biometric_delete_all_key_warning))
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(resources.getString(android.R.string.yes)
) { _, _ ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
BiometricUnlockDatabaseHelper.deleteEntryKeyInKeystoreForBiometric(
activity,
object : BiometricUnlockDatabaseHelper.BiometricUnlockErrorCallback {
override fun onInvalidKeyException(e: Exception) {}
override fun onBiometricException(e: Exception) {
Toast.makeText(context,
getString(R.string.biometric_scanning_error, e.localizedMessage),
Toast.LENGTH_SHORT).show()
}
})
}
CipherDatabaseAction.getInstance(context.applicationContext).deleteAll()
}
.setNegativeButton(resources.getString(android.R.string.no))
{ _, _ -> }.show()
}
false
}
}
}
findPreference<Preference>(getString(R.string.advanced_unlock_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.advanced_unlock_explanation_url)
false
}
}
private fun onCreateAppearancePreferences(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_appearance, rootKey)
activity?.let { activity ->
findPreference<ListPreference>(getString(R.string.setting_style_key))?.setOnPreferenceChangeListener { _, newValue ->
var styleEnabled = true
val styleIdString = newValue as String
if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
for (themeIdDisabled in BuildConfig.STYLES_DISABLED) {
if (themeIdDisabled == styleIdString) {
styleEnabled = false
fragmentManager?.let { fragmentManager ->
ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
}
}
}
if (styleEnabled) {
Stylish.assignStyle(styleIdString)
activity.recreate()
}
styleEnabled
}
findPreference<IconPackListPreference>(getString(R.string.setting_icon_pack_choose_key))?.setOnPreferenceChangeListener { _, newValue ->
var iconPackEnabled = true
val iconPackId = newValue as String
if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
for (iconPackIdDisabled in BuildConfig.ICON_PACKS_DISABLED) {
if (iconPackIdDisabled == iconPackId) {
iconPackEnabled = false
fragmentManager?.let { fragmentManager ->
ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
}
}
}
if (iconPackEnabled) {
IconPackChooser.setSelectedIconPack(iconPackId)
}
iconPackEnabled
}
findPreference<Preference>(getString(R.string.reset_education_screens_key))?.setOnPreferenceClickListener {
// To allow only one toast
if (mCount == 0) {
val sharedPreferences = Education.getEducationSharedPreferences(context!!)
val editor = sharedPreferences.edit()
for (resourceId in Education.educationResourcesKeys) {
editor.putBoolean(getString(resourceId), false)
}
editor.apply()
Toast.makeText(context, R.string.reset_education_screens_text, Toast.LENGTH_SHORT).show()
}
mCount++
false
}
}
}
override fun onResume() {
super.onResume()
activity?.let { activity ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
if (autoFillEnablePreference != null) {
val autofillManager = activity.getSystemService(AutofillManager::class.java)
autoFillEnablePreference.isChecked = autofillManager != null
&& autofillManager.hasEnabledAutofillServices()
}
}
}
}
private var mCount = 0
override fun onStop() {
super.onStop()
activity?.let { activity ->
if (mCount == 10) {
Education.getEducationSharedPreferences(activity).edit()
.putBoolean(getString(R.string.education_screen_reclicked_key), true).apply()
}
}
}
companion object {
private const val REQUEST_CODE_AUTOFILL = 5201
}
}

View File

@@ -0,0 +1,566 @@
package com.kunzisoft.keepass.settings
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.*
import androidx.fragment.app.DialogFragment
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.SwitchPreference
import com.kunzisoft.androidclearchroma.ChromaUtil
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.lock
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.preference.*
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.MenuUtil
class NestedDatabaseSettingsFragment : NestedSettingsFragment() {
private var mDatabase: Database = Database.getInstance()
private var mDatabaseReadOnly: Boolean = false
private var mDatabaseAutoSaveEnabled: Boolean = true
private var dbNamePref: InputTextPreference? = null
private var dbDescriptionPref: InputTextPreference? = null
private var dbDefaultUsername: InputTextPreference? = null
private var dbCustomColorPref: DialogColorPreference? = null
private var dbDataCompressionPref: Preference? = null
private var recycleBinGroupPref: Preference? = null
private var dbMaxHistoryItemsPref: InputNumberPreference? = null
private var dbMaxHistorySizePref: InputNumberPreference? = null
private var mEncryptionAlgorithmPref: DialogListExplanationPreference? = null
private var mKeyDerivationPref: DialogListExplanationPreference? = null
private var mRoundPref: InputKdfNumberPreference? = null
private var mMemoryPref: InputKdfNumberPreference? = null
private var mParallelismPref: InputKdfNumberPreference? = null
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
setHasOptionsMenu(true)
mDatabaseReadOnly = mDatabase.isReadOnly
|| ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments)
// Load the preferences from an XML resource
when (screen) {
Screen.DATABASE -> {
onCreateDatabasePreference(rootKey)
}
Screen.DATABASE_SECURITY -> {
onCreateDatabaseSecurityPreference(rootKey)
}
Screen.DATABASE_MASTER_KEY -> {
onCreateDatabaseMasterKeyPreference(rootKey)
}
else -> {}
}
}
private fun onCreateDatabasePreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database, rootKey)
if (mDatabase.loaded) {
val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key))
// Database name
dbNamePref = findPreference(getString(R.string.database_name_key))
if (mDatabase.allowName) {
dbNamePref?.summary = mDatabase.name
} else {
dbGeneralPrefCategory?.removePreference(dbNamePref)
}
// Database description
dbDescriptionPref = findPreference(getString(R.string.database_description_key))
if (mDatabase.allowDescription) {
dbDescriptionPref?.summary = mDatabase.description
} else {
dbGeneralPrefCategory?.removePreference(dbDescriptionPref)
}
// Database default username
dbDefaultUsername = findPreference(getString(R.string.database_default_username_key))
if (mDatabase.allowDefaultUsername) {
dbDefaultUsername?.summary = mDatabase.defaultUsername
} else {
dbDefaultUsername?.isEnabled = false
// TODO dbGeneralPrefCategory?.removePreference(dbDefaultUsername)
}
// Database custom color
dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
if (mDatabase.allowCustomColor) {
dbCustomColorPref?.apply {
try {
color = Color.parseColor(mDatabase.customColor)
summary = mDatabase.customColor
} catch (e: Exception) {
color = DialogColorPreference.DISABLE_COLOR
summary = ""
}
}
} else {
dbCustomColorPref?.isEnabled = false
// TODO dbGeneralPrefCategory?.removePreference(dbCustomColorPref)
}
// Version
findPreference<Preference>(getString(R.string.database_version_key))
?.summary = mDatabase.version
val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_compression_key))
// Database compression
dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
if (mDatabase.allowDataCompression) {
dbDataCompressionPref?.summary = (mDatabase.compressionAlgorithm
?: PwCompressionAlgorithm.None).getName(resources)
} else {
dbCompressionPrefCategory?.isVisible = false
}
val dbRecycleBinPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_recycle_bin_key))
recycleBinGroupPref = findPreference(getString(R.string.recycle_bin_group_key))
// Recycle bin
if (mDatabase.allowRecycleBin) {
val recycleBinEnablePref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_enable_key))
recycleBinEnablePref?.apply {
isChecked = mDatabase.isRecycleBinEnabled
isEnabled = if (!mDatabaseReadOnly) {
setOnPreferenceChangeListener { _, newValue ->
val recycleBinEnabled = newValue as Boolean
mDatabase.isRecycleBinEnabled = recycleBinEnabled
if (recycleBinEnabled) {
mDatabase.ensureRecycleBinExists(resources)
} else {
mDatabase.removeRecycleBin()
}
refreshRecycleBinGroup()
// Save the database if not in readonly mode
(context as SettingsActivity?)?.
mProgressDialogThread?.startDatabaseSave(mDatabaseAutoSaveEnabled)
true
}
true
} else {
false
}
}
// Recycle Bin group
refreshRecycleBinGroup()
} else {
dbRecycleBinPrefCategory?.isVisible = false
}
// History
findPreference<PreferenceCategory>(getString(R.string.database_category_history_key))
?.isVisible = mDatabase.manageHistory == true
// Max history items
dbMaxHistoryItemsPref = findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))?.apply {
summary = mDatabase.historyMaxItems.toString()
}
// Max history size
dbMaxHistorySizePref = findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))?.apply {
summary = mDatabase.historyMaxSize.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private fun refreshRecycleBinGroup() {
recycleBinGroupPref?.apply {
if (mDatabase.isRecycleBinEnabled) {
summary = mDatabase.recycleBin?.title
isEnabled = true
} else {
summary = null
isEnabled = false
}
}
}
private fun onCreateDatabaseSecurityPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database_security, rootKey)
if (mDatabase.loaded) {
// Encryption Algorithm
mEncryptionAlgorithmPref = findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))?.apply {
summary = mDatabase.getEncryptionAlgorithmName(resources)
}
// Key derivation function
mKeyDerivationPref = findPreference<DialogListExplanationPreference>(getString(R.string.key_derivation_function_key))?.apply {
summary = mDatabase.getKeyDerivationName(resources)
}
// Round encryption
mRoundPref = findPreference<InputKdfNumberPreference>(getString(R.string.transform_rounds_key))?.apply {
summary = mDatabase.numberKeyEncryptionRounds.toString()
}
// Memory Usage
mMemoryPref = findPreference<InputKdfNumberPreference>(getString(R.string.memory_usage_key))?.apply {
summary = mDatabase.memoryUsage.toString()
}
// Parallelism
mParallelismPref = findPreference<InputKdfNumberPreference>(getString(R.string.parallelism_key))?.apply {
summary = mDatabase.parallelism.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private fun onCreateDatabaseMasterKeyPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database_master_key, rootKey)
if (mDatabase.loaded) {
findPreference<Preference>(getString(R.string.settings_database_change_credentials_key))?.apply {
isEnabled = if (!mDatabaseReadOnly) {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
fragmentManager?.let { fragmentManager ->
AssignMasterKeyDialogFragment.getInstance(mDatabase.allowNoMasterKey)
.show(fragmentManager, "passwordDialog")
}
false
}
true
} else {
false
}
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private val colorSelectedListener: ((Boolean, Int)-> Unit)? = { enable, color ->
dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
if (enable) {
dbCustomColorPref?.color = color
} else {
dbCustomColorPref?.color = DialogColorPreference.DISABLE_COLOR
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)
try {
// To reassign color listener after orientation change
val chromaDialog = fragmentManager?.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
chromaDialog?.onColorSelectedListener = colorSelectedListener
} catch (e: Exception) {}
return view
}
override fun onProgressDialogThreadResult(actionTask: String,
result: ActionRunnable.Result) {
result.data?.let { data ->
if (data.containsKey(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
&& data.containsKey(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)) {
when (actionTask) {
/*
--------
Main preferences
--------
*/
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_NAME_TASK -> {
val oldName = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
val newName = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
val nameToShow =
if (result.isSuccess) {
newName
} else {
mDatabase.name = oldName
oldName
}
dbNamePref?.summary = nameToShow
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_DESCRIPTION_TASK -> {
val oldDescription = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
val newDescription = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
val descriptionToShow =
if (result.isSuccess) {
newDescription
} else {
mDatabase.description = oldDescription
oldDescription
}
dbDescriptionPref?.summary = descriptionToShow
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK -> {
val oldDefaultUsername = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
val newDefaultUsername = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
val defaultUsernameToShow =
if (result.isSuccess) {
newDefaultUsername
} else {
mDatabase.defaultUsername = oldDefaultUsername
oldDefaultUsername
}
dbDefaultUsername?.summary = defaultUsernameToShow
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COLOR_TASK -> {
val oldColor = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
val newColor = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
val defaultColorToShow =
if (result.isSuccess) {
newColor
} else {
mDatabase.customColor = oldColor
oldColor
}
dbCustomColorPref?.summary = defaultColorToShow
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> {
val oldCompression = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as PwCompressionAlgorithm
val newCompression = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as PwCompressionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newCompression
} else {
mDatabase.compressionAlgorithm = oldCompression
oldCompression
}
dbDataCompressionPref?.summary = algorithmToShow.getName(resources)
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK -> {
val oldMaxHistoryItems = data.getInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newMaxHistoryItems = data.getInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val maxHistoryItemsToShow =
if (result.isSuccess) {
newMaxHistoryItems
} else {
mDatabase.historyMaxItems = oldMaxHistoryItems
oldMaxHistoryItems
}
dbMaxHistoryItemsPref?.summary = maxHistoryItemsToShow.toString()
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK -> {
val oldMaxHistorySize = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newMaxHistorySize = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val maxHistorySizeToShow =
if (result.isSuccess) {
newMaxHistorySize
} else {
mDatabase.historyMaxSize = oldMaxHistorySize
oldMaxHistorySize
}
dbMaxHistorySizePref?.summary = maxHistorySizeToShow.toString()
}
/*
--------
Security
--------
*/
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_ENCRYPTION_TASK -> {
val oldEncryption = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as PwEncryptionAlgorithm
val newEncryption = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as PwEncryptionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newEncryption
} else {
mDatabase.encryptionAlgorithm = oldEncryption
oldEncryption
}
mEncryptionAlgorithmPref?.summary = algorithmToShow.getName(resources)
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK -> {
val oldKeyDerivationEngine = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as KdfEngine
val newKeyDerivationEngine = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as KdfEngine
val kdfEngineToShow =
if (result.isSuccess) {
newKeyDerivationEngine
} else {
mDatabase.kdfEngine = oldKeyDerivationEngine
oldKeyDerivationEngine
}
mKeyDerivationPref?.summary = kdfEngineToShow.getName(resources)
mRoundPref?.summary = kdfEngineToShow.defaultKeyRounds.toString()
// Disable memory and parallelism if not available
mMemoryPref?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
mParallelismPref?.summary = kdfEngineToShow.defaultParallelism.toString()
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_ITERATIONS_TASK -> {
val oldIterations = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newIterations = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val roundsToShow =
if (result.isSuccess) {
newIterations
} else {
mDatabase.numberKeyEncryptionRounds = oldIterations
oldIterations
}
mRoundPref?.summary = roundsToShow.toString()
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK -> {
val oldMemoryUsage = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newMemoryUsage = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val memoryToShow =
if (result.isSuccess) {
newMemoryUsage
} else {
mDatabase.memoryUsage = oldMemoryUsage
oldMemoryUsage
}
mMemoryPref?.summary = memoryToShow.toString()
}
DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_PARALLELISM_TASK -> {
val oldParallelism = data.getInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
val newParallelism = data.getInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
val parallelismToShow =
if (result.isSuccess) {
newParallelism
} else {
mDatabase.parallelism = oldParallelism
oldParallelism
}
mParallelismPref?.summary = parallelismToShow.toString()
}
}
}
}
}
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
fragmentManager?.let { fragmentManager ->
preference?.let { preference ->
var dialogFragment: DialogFragment? = null
when {
// Main Preferences
preference.key == getString(R.string.database_name_key) -> {
dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_description_key) -> {
dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_default_username_key) -> {
dialogFragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_custom_color_key) -> {
dialogFragment = DatabaseColorPreferenceDialogFragmentCompat.newInstance(preference.key).apply {
onColorSelectedListener = colorSelectedListener
}
}
preference.key == getString(R.string.database_data_compression_key) -> {
dialogFragment = DatabaseDataCompressionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.max_history_items_key) -> {
dialogFragment = MaxHistoryItemsPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.max_history_size_key) -> {
dialogFragment = MaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key)
}
// Security
preference.key == getString(R.string.encryption_algorithm_key) -> {
dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.key_derivation_function_key) -> {
val keyDerivationDialogFragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat.newInstance(preference.key)
// Add other prefs to manage
keyDerivationDialogFragment.setRoundPreference(mRoundPref)
keyDerivationDialogFragment.setMemoryPreference(mMemoryPref)
keyDerivationDialogFragment.setParallelismPreference(mParallelismPref)
dialogFragment = keyDerivationDialogFragment
}
preference.key == getString(R.string.transform_rounds_key) -> {
dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.memory_usage_key) -> {
dialogFragment = MemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.parallelism_key) -> {
dialogFragment = ParallelismPreferenceDialogFragmentCompat.newInstance(preference.key)
}
else -> otherDialogFragment = true
}
if (dialogFragment != null && !mDatabaseReadOnly) {
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(fragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
super.onDisplayPreferenceDialog(preference)
}
}
}
}
override fun onResume() {
super.onResume()
context?.let { context ->
mDatabaseAutoSaveEnabled = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.database, menu)
if (mDatabaseReadOnly) {
menu.findItem(R.id.menu_save_database)?.isVisible = false
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val settingActivity = activity as SettingsActivity?
when (item.itemId) {
R.id.menu_lock -> {
settingActivity?.lock()
return true
}
R.id.menu_save_database -> {
settingActivity?.mProgressDialogThread?.startDatabaseSave(!mDatabaseReadOnly)
return true
}
else -> {
// Check the time lock before launching settings
settingActivity?.let {
MenuUtil.onDefaultMenuOptionsItemSelected(it, item, mDatabaseReadOnly, true)
}
return super.onOptionsItemSelected(item)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
ReadOnlyHelper.onSaveInstanceState(outState, mDatabaseReadOnly)
super.onSaveInstanceState(outState)
}
companion object {
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
}
}

View File

@@ -19,588 +19,37 @@
*/ */
package com.kunzisoft.keepass.settings package com.kunzisoft.keepass.settings
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import androidx.preference.Preference
import android.util.Log import androidx.preference.PreferenceFragmentCompat
import android.view.LayoutInflater import androidx.preference.SwitchPreference
import android.view.View
import android.view.ViewGroup
import android.view.autofill.AutofillManager
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricManager
import androidx.fragment.app.DialogFragment
import androidx.preference.*
import com.kunzisoft.androidclearchroma.ChromaUtil
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.* import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
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.BiometricUnlockDatabaseHelper
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.PwCompressionAlgorithm
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
import com.kunzisoft.keepass.education.Education
import com.kunzisoft.keepass.icons.IconPackChooser
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COLOR_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_COMPRESSION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_DESCRIPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ENCRYPTION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_ITERATIONS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_NAME_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_SAVE_PARALLELISM_TASK
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.NEW_ELEMENT_KEY
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.OLD_ELEMENT_KEY
import com.kunzisoft.keepass.settings.preference.*
import com.kunzisoft.keepass.settings.preference.DialogColorPreference.Companion.DISABLE_COLOR
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.UriUtil
class NestedSettingsFragment : PreferenceFragmentCompat() { abstract class NestedSettingsFragment : PreferenceFragmentCompat() {
private var mDatabase: Database = Database.getInstance()
private var mDatabaseReadOnly: Boolean = false
private var mCount = 0
private var dbNamePref: InputTextPreference? = null
private var dbDescriptionPref: InputTextPreference? = null
private var dbDefaultUsername: InputTextPreference? = null
private var dbCustomColorPref: DialogColorPreference? = null
private var dbDataCompressionPref: Preference? = null
private var recycleBinGroupPref: Preference? = null
private var dbMaxHistoryItemsPref: InputNumberPreference? = null
private var dbMaxHistorySizePref: InputNumberPreference? = null
private var mEncryptionAlgorithmPref: DialogListExplanationPreference? = null
private var mKeyDerivationPref: DialogListExplanationPreference? = null
private var mRoundPref: InputKdfNumberPreference? = null
private var mMemoryPref: InputKdfNumberPreference? = null
private var mParallelismPref: InputKdfNumberPreference? = null
enum class Screen { enum class Screen {
APPLICATION, FORM_FILLING, ADVANCED_UNLOCK, APPEARANCE, DATABASE, DATABASE_SECURITY, DATABASE_MASTER_KEY APPLICATION, FORM_FILLING, ADVANCED_UNLOCK, APPEARANCE, DATABASE, DATABASE_SECURITY, DATABASE_MASTER_KEY
} }
override fun onResume() {
super.onResume()
activity?.let { activity ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
if (autoFillEnablePreference != null) {
val autofillManager = activity.getSystemService(AutofillManager::class.java)
autoFillEnablePreference.isChecked = autofillManager != null
&& autofillManager.hasEnabledAutofillServices()
}
}
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
var key = 0 var key = 0
if (arguments != null) if (arguments != null)
key = arguments!!.getInt(TAG_KEY) key = arguments!!.getInt(TAG_KEY)
mDatabaseReadOnly = mDatabase.isReadOnly onCreateScreenPreference(Screen.values()[key], savedInstanceState, rootKey)
|| ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrArguments(savedInstanceState, arguments)
// Load the preferences from an XML resource
when (Screen.values()[key]) {
Screen.APPLICATION -> {
onCreateApplicationPreferences(rootKey)
}
Screen.FORM_FILLING -> {
onCreateFormFillingPreference(rootKey)
}
Screen.ADVANCED_UNLOCK -> {
onCreateAdvancedUnlockPreferences(rootKey)
}
Screen.APPEARANCE -> {
onCreateAppearancePreferences(rootKey)
}
Screen.DATABASE -> {
onCreateDatabasePreference(rootKey)
}
Screen.DATABASE_SECURITY -> {
onCreateDatabaseSecurityPreference(rootKey)
}
Screen.DATABASE_MASTER_KEY -> {
onCreateDatabaseMasterKeyPreference(rootKey)
}
}
} }
private fun onCreateApplicationPreferences(rootKey: String?) { abstract fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?)
setPreferencesFromResource(R.xml.preferences_application, rootKey)
activity?.let { activity -> open fun onProgressDialogThreadResult(actionTask: String,
allowCopyPassword() result: ActionRunnable.Result) {}
findPreference<Preference>(getString(R.string.keyfile_key))?.setOnPreferenceChangeListener { _, newValue -> protected fun preferenceInDevelopment(preferenceInDev: Preference) {
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAllKeyFiles()
}
true
}
findPreference<Preference>(getString(R.string.recentfile_key))?.setOnPreferenceChangeListener { _, newValue ->
if (!(newValue as Boolean)) {
FileDatabaseHistoryAction.getInstance(activity.applicationContext).deleteAll()
}
true
}
}
}
private fun onCreateFormFillingPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_form_filling, rootKey)
activity?.let { activity ->
val autoFillEnablePreference: SwitchPreference? = findPreference(getString(R.string.settings_autofill_enable_key))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val autofillManager = activity.getSystemService(AutofillManager::class.java)
if (autofillManager != null && autofillManager.hasEnabledAutofillServices())
autoFillEnablePreference?.isChecked = autofillManager.hasEnabledAutofillServices()
autoFillEnablePreference?.onPreferenceClickListener = object : Preference.OnPreferenceClickListener {
@RequiresApi(api = Build.VERSION_CODES.O)
override fun onPreferenceClick(preference: Preference): Boolean {
if ((preference as SwitchPreference).isChecked) {
try {
startEnableService()
} catch (e: ActivityNotFoundException) {
val error = getString(R.string.error_autofill_enable_service)
preference.isChecked = false
Log.d(javaClass.name, error, e)
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
}
} else {
disableService()
}
return false
}
@RequiresApi(api = Build.VERSION_CODES.O)
private fun disableService() {
if (autofillManager != null && autofillManager.hasEnabledAutofillServices()) {
autofillManager.disableAutofillServices()
} else {
Log.d(javaClass.name, "Sample service already disabled.")
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Throws(ActivityNotFoundException::class)
private fun startEnableService() {
if (autofillManager != null && !autofillManager.hasEnabledAutofillServices()) {
val intent = Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
// TODO Autofill
intent.data = Uri.parse("package:com.example.android.autofill.service")
Log.d(javaClass.name, "enableService(): intent=$intent")
startActivityForResult(intent, REQUEST_CODE_AUTOFILL)
} else {
Log.d(javaClass.name, "Sample service already enabled.")
}
}
}
} else {
autoFillEnablePreference?.setOnPreferenceClickListener { preference ->
(preference as SwitchPreference).isChecked = false
val fragmentManager = fragmentManager!!
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.O)
.show(fragmentManager, "unavailableFeatureDialog")
false
}
}
}
findPreference<Preference>(getString(R.string.magic_keyboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.magic_keyboard_explanation_url)
false
}
findPreference<Preference>(getString(R.string.magic_keyboard_key))?.setOnPreferenceClickListener {
startActivity(Intent(Settings.ACTION_INPUT_METHOD_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
})
false
}
findPreference<Preference>(getString(R.string.magic_keyboard_preference_key))?.setOnPreferenceClickListener {
startActivity(Intent(context, MagikIMESettings::class.java))
false
}
findPreference<Preference>(getString(R.string.clipboard_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.clipboard_explanation_url)
false
}
findPreference<Preference>(getString(R.string.autofill_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.autofill_explanation_url)
false
}
// Present in two places
allowCopyPassword()
}
private fun onCreateAdvancedUnlockPreferences(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_advanced_unlock, rootKey)
activity?.let { activity ->
val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key))
// < M solve verifyError exception
var biometricUnlockSupported = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val biometricCanAuthenticate = BiometricManager.from(activity).canAuthenticate()
biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
}
if (!biometricUnlockSupported) {
// False if under Marshmallow
biometricUnlockEnablePreference?.apply {
isChecked = false
setOnPreferenceClickListener { preference ->
fragmentManager?.let { fragmentManager ->
(preference as SwitchPreference).isChecked = false
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
.show(fragmentManager, "unavailableFeatureDialog")
}
false
}
}
}
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
if (!biometricUnlockSupported) {
deleteKeysFingerprints?.isEnabled = false
} else {
deleteKeysFingerprints?.setOnPreferenceClickListener {
context?.let { context ->
AlertDialog.Builder(context)
.setMessage(resources.getString(R.string.biometric_delete_all_key_warning))
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(resources.getString(android.R.string.yes)
) { _, _ ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
BiometricUnlockDatabaseHelper.deleteEntryKeyInKeystoreForBiometric(
activity,
object : BiometricUnlockDatabaseHelper.BiometricUnlockErrorCallback {
override fun onInvalidKeyException(e: Exception) {}
override fun onBiometricException(e: Exception) {
Toast.makeText(context,
getString(R.string.biometric_scanning_error, e.localizedMessage),
Toast.LENGTH_SHORT).show()
}
})
}
CipherDatabaseAction.getInstance(context.applicationContext).deleteAll()
}
.setNegativeButton(resources.getString(android.R.string.no))
{ _, _ -> }.show()
}
false
}
}
}
findPreference<Preference>(getString(R.string.advanced_unlock_explanation_key))?.setOnPreferenceClickListener {
UriUtil.gotoUrl(context!!, R.string.advanced_unlock_explanation_url)
false
}
}
private fun onCreateAppearancePreferences(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_appearance, rootKey)
activity?.let { activity ->
findPreference<ListPreference>(getString(R.string.setting_style_key))?.setOnPreferenceChangeListener { _, newValue ->
var styleEnabled = true
val styleIdString = newValue as String
if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
for (themeIdDisabled in BuildConfig.STYLES_DISABLED) {
if (themeIdDisabled == styleIdString) {
styleEnabled = false
fragmentManager?.let { fragmentManager ->
ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
}
}
}
if (styleEnabled) {
Stylish.assignStyle(styleIdString)
activity.recreate()
}
styleEnabled
}
findPreference<IconPackListPreference>(getString(R.string.setting_icon_pack_choose_key))?.setOnPreferenceChangeListener { _, newValue ->
var iconPackEnabled = true
val iconPackId = newValue as String
if (BuildConfig.CLOSED_STORE || !Education.isEducationScreenReclickedPerformed(context!!))
for (iconPackIdDisabled in BuildConfig.ICON_PACKS_DISABLED) {
if (iconPackIdDisabled == iconPackId) {
iconPackEnabled = false
fragmentManager?.let { fragmentManager ->
ProFeatureDialogFragment().show(fragmentManager, "pro_feature_dialog")
}
}
}
if (iconPackEnabled) {
IconPackChooser.setSelectedIconPack(iconPackId)
}
iconPackEnabled
}
findPreference<Preference>(getString(R.string.reset_education_screens_key))?.setOnPreferenceClickListener {
// To allow only one toast
if (mCount == 0) {
val sharedPreferences = Education.getEducationSharedPreferences(context!!)
val editor = sharedPreferences.edit()
for (resourceId in Education.educationResourcesKeys) {
editor.putBoolean(getString(resourceId), false)
}
editor.apply()
Toast.makeText(context, R.string.reset_education_screens_text, Toast.LENGTH_SHORT).show()
}
mCount++
false
}
}
}
private fun onCreateDatabasePreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database, rootKey)
if (mDatabase.loaded) {
val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key))
// Database name
dbNamePref = findPreference(getString(R.string.database_name_key))
if (mDatabase.allowName) {
dbNamePref?.summary = mDatabase.name
} else {
dbGeneralPrefCategory?.removePreference(dbNamePref)
}
// Database description
dbDescriptionPref = findPreference(getString(R.string.database_description_key))
if (mDatabase.allowDescription) {
dbDescriptionPref?.summary = mDatabase.description
} else {
dbGeneralPrefCategory?.removePreference(dbDescriptionPref)
}
// Database default username
dbDefaultUsername = findPreference(getString(R.string.database_default_username_key))
if (mDatabase.allowDefaultUsername) {
dbDefaultUsername?.summary = mDatabase.defaultUsername
} else {
dbDefaultUsername?.isEnabled = false
// TODO dbGeneralPrefCategory?.removePreference(dbDefaultUsername)
}
// Database custom color
dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
if (mDatabase.allowCustomColor) {
dbCustomColorPref?.apply {
try {
color = Color.parseColor(mDatabase.customColor)
summary = mDatabase.customColor
} catch (e: Exception) {
color = DISABLE_COLOR
summary = ""
}
}
} else {
dbCustomColorPref?.isEnabled = false
// TODO dbGeneralPrefCategory?.removePreference(dbCustomColorPref)
}
// Version
findPreference<Preference>(getString(R.string.database_version_key))
?.summary = mDatabase.version
val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_compression_key))
// Database compression
dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
if (mDatabase.allowDataCompression) {
dbDataCompressionPref?.summary = (mDatabase.compressionAlgorithm
?: PwCompressionAlgorithm.None).getName(resources)
} else {
dbCompressionPrefCategory?.isVisible = false
}
val dbRecycleBinPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_recycle_bin_key))
recycleBinGroupPref = findPreference(getString(R.string.recycle_bin_group_key))
// Recycle bin
if (mDatabase.allowRecycleBin) {
val recycleBinEnablePref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_enable_key))
recycleBinEnablePref?.apply {
isChecked = mDatabase.isRecycleBinEnabled
isEnabled = if (!mDatabaseReadOnly) {
setOnPreferenceChangeListener { _, newValue ->
val recycleBinEnabled = newValue as Boolean
mDatabase.isRecycleBinEnabled = recycleBinEnabled
if (recycleBinEnabled) {
mDatabase.ensureRecycleBinExists(resources)
} else {
mDatabase.removeRecycleBin()
}
refreshRecycleBinGroup()
// Save the database if not in readonly mode
(context as SettingsActivity?)?.progressDialogThread?.startDatabaseSave(true)
true
}
true
} else {
false
}
}
// Recycle Bin group
refreshRecycleBinGroup()
} else {
dbRecycleBinPrefCategory?.isVisible = false
}
// History
findPreference<PreferenceCategory>(getString(R.string.database_category_history_key))
?.isVisible = mDatabase.manageHistory == true
// Max history items
dbMaxHistoryItemsPref = findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))?.apply {
summary = mDatabase.historyMaxItems.toString()
}
// Max history size
dbMaxHistorySizePref = findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))?.apply {
summary = mDatabase.historyMaxSize.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private fun refreshRecycleBinGroup() {
recycleBinGroupPref?.apply {
if (mDatabase.isRecycleBinEnabled) {
summary = mDatabase.recycleBin?.title
isEnabled = true
} else {
summary = null
isEnabled = false
}
}
}
private fun onCreateDatabaseSecurityPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database_security, rootKey)
if (mDatabase.loaded) {
// Encryption Algorithm
mEncryptionAlgorithmPref = findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))?.apply {
summary = mDatabase.getEncryptionAlgorithmName(resources)
}
// Key derivation function
mKeyDerivationPref = findPreference<DialogListExplanationPreference>(getString(R.string.key_derivation_function_key))?.apply {
summary = mDatabase.getKeyDerivationName(resources)
}
// Round encryption
mRoundPref = findPreference<InputKdfNumberPreference>(getString(R.string.transform_rounds_key))?.apply {
summary = mDatabase.numberKeyEncryptionRounds.toString()
}
// Memory Usage
mMemoryPref = findPreference<InputKdfNumberPreference>(getString(R.string.memory_usage_key))?.apply {
summary = mDatabase.memoryUsage.toString()
}
// Parallelism
mParallelismPref = findPreference<InputKdfNumberPreference>(getString(R.string.parallelism_key))?.apply {
summary = mDatabase.parallelism.toString()
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private fun onCreateDatabaseMasterKeyPreference(rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_database_master_key, rootKey)
if (mDatabase.loaded) {
findPreference<Preference>(getString(R.string.settings_database_change_credentials_key))?.apply {
isEnabled = if (!mDatabaseReadOnly) {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
fragmentManager?.let { fragmentManager ->
AssignMasterKeyDialogFragment.getInstance(mDatabase.allowNoMasterKey)
.show(fragmentManager, "passwordDialog")
}
false
}
true
} else {
false
}
}
} else {
Log.e(javaClass.name, "Database isn't ready")
}
}
private fun allowCopyPassword() {
val copyPasswordPreference: SwitchPreference? = findPreference(getString(R.string.allow_copy_password_key))
copyPasswordPreference?.setOnPreferenceChangeListener { _, newValue ->
if (newValue as Boolean && context != null) {
val message = getString(R.string.allow_copy_password_warning) +
"\n\n" +
getString(R.string.clipboard_warning)
AlertDialog
.Builder(context!!)
.setMessage(message)
.create()
.apply {
setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable))
{ dialog, _ ->
dialog.dismiss()
}
setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable))
{ dialog, _ ->
copyPasswordPreference.isChecked = false
dialog.dismiss()
}
show()
}
}
true
}
}
private fun preferenceInDevelopment(preferenceInDev: Preference) {
preferenceInDev.setOnPreferenceClickListener { preference -> preferenceInDev.setOnPreferenceClickListener { preference ->
fragmentManager?.let { fragmentManager -> fragmentManager?.let { fragmentManager ->
try { // don't check if we can try { // don't check if we can
@@ -613,295 +62,22 @@ class NestedSettingsFragment : PreferenceFragmentCompat() {
} }
} }
override fun onStop() {
super.onStop()
activity?.let { activity ->
if (mCount == 10) {
Education.getEducationSharedPreferences(activity).edit()
.putBoolean(getString(R.string.education_screen_reclicked_key), true).apply()
}
}
}
private val colorSelectedListener: ((Boolean, Int)-> Unit)? = { enable, color ->
dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
if (enable) {
dbCustomColorPref?.color = color
} else {
dbCustomColorPref?.color = DISABLE_COLOR
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = super.onCreateView(inflater, container, savedInstanceState)
try {
// To reassign color listener after orientation change
val chromaDialog = fragmentManager?.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
chromaDialog?.onColorSelectedListener = colorSelectedListener
} catch (e: Exception) {}
return view
}
fun onProgressDialogThreadResult(actionTask: String,
result: ActionRunnable.Result) {
result.data?.let { data ->
if (data.containsKey(OLD_ELEMENT_KEY)
&& data.containsKey(NEW_ELEMENT_KEY)) {
when (actionTask) {
/*
--------
Main preferences
--------
*/
ACTION_DATABASE_SAVE_NAME_TASK -> {
val oldName = data.getString(OLD_ELEMENT_KEY)!!
val newName = data.getString(NEW_ELEMENT_KEY)!!
val nameToShow =
if (result.isSuccess) {
newName
} else {
mDatabase.name = oldName
oldName
}
dbNamePref?.summary = nameToShow
}
ACTION_DATABASE_SAVE_DESCRIPTION_TASK -> {
val oldDescription = data.getString(OLD_ELEMENT_KEY)!!
val newDescription = data.getString(NEW_ELEMENT_KEY)!!
val descriptionToShow =
if (result.isSuccess) {
newDescription
} else {
mDatabase.description = oldDescription
oldDescription
}
dbDescriptionPref?.summary = descriptionToShow
}
ACTION_DATABASE_SAVE_DEFAULT_USERNAME_TASK -> {
val oldDefaultUsername = data.getString(OLD_ELEMENT_KEY)!!
val newDefaultUsername = data.getString(NEW_ELEMENT_KEY)!!
val defaultUsernameToShow =
if (result.isSuccess) {
newDefaultUsername
} else {
mDatabase.defaultUsername = oldDefaultUsername
oldDefaultUsername
}
dbDefaultUsername?.summary = defaultUsernameToShow
}
ACTION_DATABASE_SAVE_COLOR_TASK -> {
val oldColor = data.getString(OLD_ELEMENT_KEY)!!
val newColor = data.getString(NEW_ELEMENT_KEY)!!
val defaultColorToShow =
if (result.isSuccess) {
newColor
} else {
mDatabase.customColor = oldColor
oldColor
}
dbCustomColorPref?.summary = defaultColorToShow
}
ACTION_DATABASE_SAVE_COMPRESSION_TASK -> {
val oldCompression = data.getSerializable(OLD_ELEMENT_KEY) as PwCompressionAlgorithm
val newCompression = data.getSerializable(NEW_ELEMENT_KEY) as PwCompressionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newCompression
} else {
mDatabase.compressionAlgorithm = oldCompression
oldCompression
}
dbDataCompressionPref?.summary = algorithmToShow.getName(resources)
}
ACTION_DATABASE_SAVE_MAX_HISTORY_ITEMS_TASK -> {
val oldMaxHistoryItems = data.getInt(OLD_ELEMENT_KEY)
val newMaxHistoryItems = data.getInt(NEW_ELEMENT_KEY)
val maxHistoryItemsToShow =
if (result.isSuccess) {
newMaxHistoryItems
} else {
mDatabase.historyMaxItems = oldMaxHistoryItems
oldMaxHistoryItems
}
dbMaxHistoryItemsPref?.summary = maxHistoryItemsToShow.toString()
}
ACTION_DATABASE_SAVE_MAX_HISTORY_SIZE_TASK -> {
val oldMaxHistorySize = data.getLong(OLD_ELEMENT_KEY)
val newMaxHistorySize = data.getLong(NEW_ELEMENT_KEY)
val maxHistorySizeToShow =
if (result.isSuccess) {
newMaxHistorySize
} else {
mDatabase.historyMaxSize = oldMaxHistorySize
oldMaxHistorySize
}
dbMaxHistorySizePref?.summary = maxHistorySizeToShow.toString()
}
/*
--------
Security
--------
*/
ACTION_DATABASE_SAVE_ENCRYPTION_TASK -> {
val oldEncryption = data.getSerializable(OLD_ELEMENT_KEY) as PwEncryptionAlgorithm
val newEncryption = data.getSerializable(NEW_ELEMENT_KEY) as PwEncryptionAlgorithm
val algorithmToShow =
if (result.isSuccess) {
newEncryption
} else {
mDatabase.encryptionAlgorithm = oldEncryption
oldEncryption
}
mEncryptionAlgorithmPref?.summary = algorithmToShow.getName(resources)
}
ACTION_DATABASE_SAVE_KEY_DERIVATION_TASK -> {
val oldKeyDerivationEngine = data.getSerializable(OLD_ELEMENT_KEY) as KdfEngine
val newKeyDerivationEngine = data.getSerializable(NEW_ELEMENT_KEY) as KdfEngine
val kdfEngineToShow =
if (result.isSuccess) {
newKeyDerivationEngine
} else {
mDatabase.kdfEngine = oldKeyDerivationEngine
oldKeyDerivationEngine
}
mKeyDerivationPref?.summary = kdfEngineToShow.getName(resources)
mRoundPref?.summary = kdfEngineToShow.defaultKeyRounds.toString()
// Disable memory and parallelism if not available
mMemoryPref?.summary = kdfEngineToShow.defaultMemoryUsage.toString()
mParallelismPref?.summary = kdfEngineToShow.defaultParallelism.toString()
}
ACTION_DATABASE_SAVE_ITERATIONS_TASK -> {
val oldIterations = data.getLong(OLD_ELEMENT_KEY)
val newIterations = data.getLong(NEW_ELEMENT_KEY)
val roundsToShow =
if (result.isSuccess) {
newIterations
} else {
mDatabase.numberKeyEncryptionRounds = oldIterations
oldIterations
}
mRoundPref?.summary = roundsToShow.toString()
}
ACTION_DATABASE_SAVE_MEMORY_USAGE_TASK -> {
val oldMemoryUsage = data.getLong(OLD_ELEMENT_KEY)
val newMemoryUsage = data.getLong(NEW_ELEMENT_KEY)
val memoryToShow =
if (result.isSuccess) {
newMemoryUsage
} else {
mDatabase.memoryUsage = oldMemoryUsage
oldMemoryUsage
}
mMemoryPref?.summary = memoryToShow.toString()
}
ACTION_DATABASE_SAVE_PARALLELISM_TASK -> {
val oldParallelism = data.getInt(OLD_ELEMENT_KEY)
val newParallelism = data.getInt(NEW_ELEMENT_KEY)
val parallelismToShow =
if (result.isSuccess) {
newParallelism
} else {
mDatabase.parallelism = oldParallelism
oldParallelism
}
mParallelismPref?.summary = parallelismToShow.toString()
}
}
}
}
}
override fun onDisplayPreferenceDialog(preference: Preference?) {
var otherDialogFragment = false
fragmentManager?.let { fragmentManager ->
preference?.let { preference ->
var dialogFragment: DialogFragment? = null
when {
// Main Preferences
preference.key == getString(R.string.database_name_key) -> {
dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_description_key) -> {
dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_default_username_key) -> {
dialogFragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.database_custom_color_key) -> {
dialogFragment = DatabaseColorPreferenceDialogFragmentCompat.newInstance(preference.key).apply {
onColorSelectedListener = colorSelectedListener
}
}
preference.key == getString(R.string.database_data_compression_key) -> {
dialogFragment = DatabaseDataCompressionPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.max_history_items_key) -> {
dialogFragment = MaxHistoryItemsPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.max_history_size_key) -> {
dialogFragment = MaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key)
}
// Security
preference.key == getString(R.string.encryption_algorithm_key) -> {
dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.key_derivation_function_key) -> {
val keyDerivationDialogFragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat.newInstance(preference.key)
// Add other prefs to manage
keyDerivationDialogFragment.setRoundPreference(mRoundPref)
keyDerivationDialogFragment.setMemoryPreference(mMemoryPref)
keyDerivationDialogFragment.setParallelismPreference(mParallelismPref)
dialogFragment = keyDerivationDialogFragment
}
preference.key == getString(R.string.transform_rounds_key) -> {
dialogFragment = RoundsPreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.memory_usage_key) -> {
dialogFragment = MemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.key)
}
preference.key == getString(R.string.parallelism_key) -> {
dialogFragment = ParallelismPreferenceDialogFragmentCompat.newInstance(preference.key)
}
else -> otherDialogFragment = true
}
if (dialogFragment != null && !mDatabaseReadOnly) {
dialogFragment.setTargetFragment(this, 0)
dialogFragment.show(fragmentManager, TAG_PREF_FRAGMENT)
}
// Could not be handled here. Try with the super method.
else if (otherDialogFragment) {
super.onDisplayPreferenceDialog(preference)
}
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
ReadOnlyHelper.onSaveInstanceState(outState, mDatabaseReadOnly)
super.onSaveInstanceState(outState)
}
companion object { companion object {
private const val TAG_KEY = "NESTED_KEY" private const val TAG_KEY = "NESTED_KEY"
private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
private const val REQUEST_CODE_AUTOFILL = 5201
@JvmOverloads @JvmOverloads
fun newInstance(key: Screen, databaseReadOnly: Boolean = ReadOnlyHelper.READ_ONLY_DEFAULT) fun newInstance(key: Screen, databaseReadOnly: Boolean = ReadOnlyHelper.READ_ONLY_DEFAULT)
: NestedSettingsFragment { : NestedSettingsFragment {
val fragment = NestedSettingsFragment() val fragment: NestedSettingsFragment = when (key) {
Screen.APPLICATION,
Screen.FORM_FILLING,
Screen.ADVANCED_UNLOCK,
Screen.APPEARANCE -> NestedAppSettingsFragment()
Screen.DATABASE,
Screen.DATABASE_SECURITY,
Screen.DATABASE_MASTER_KEY -> NestedDatabaseSettingsFragment()
}
// supply arguments to bundle. // supply arguments to bundle.
val args = Bundle() val args = Bundle()
args.putInt(TAG_KEY, key.ordinal) args.putInt(TAG_KEY, key.ordinal)

View File

@@ -134,6 +134,12 @@ object PreferencesUtil {
context.resources.getBoolean(R.bool.lock_database_back_root_default)) context.resources.getBoolean(R.bool.lock_database_back_root_default))
} }
fun isAutoSaveDatabaseEnabled(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.enable_auto_save_database_key),
context.resources.getBoolean(R.bool.enable_auto_save_database_default))
}
fun isPersistentNotificationEnable(context: Context): Boolean { fun isPersistentNotificationEnable(context: Context): Boolean {
val prefs = PreferenceManager.getDefaultSharedPreferences(context) val prefs = PreferenceManager.getDefaultSharedPreferences(context)
return prefs.getBoolean(context.getString(R.string.persistent_notification_key), return prefs.getBoolean(context.getString(R.string.persistent_notification_key),

View File

@@ -33,7 +33,6 @@ import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.timeout.TimeoutHelper import com.kunzisoft.keepass.timeout.TimeoutHelper
@@ -46,8 +45,6 @@ open class SettingsActivity
private var toolbar: Toolbar? = null private var toolbar: Toolbar? = null
var progressDialogThread: ProgressDialogThread? = null
companion object { companion object {
private const val TAG_NESTED = "TAG_NESTED" private const val TAG_NESTED = "TAG_NESTED"
@@ -90,7 +87,7 @@ open class SettingsActivity
backupManager = BackupManager(this) backupManager = BackupManager(this)
progressDialogThread = ProgressDialogThread(this) { actionTask, result -> mProgressDialogThread?.onActionFinish = { actionTask, result ->
// Call result in fragment // Call result in fragment
(supportFragmentManager (supportFragmentManager
.findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?) .findFragmentByTag(TAG_NESTED) as NestedSettingsFragment?)
@@ -98,19 +95,6 @@ open class SettingsActivity
} }
} }
override fun onResume() {
super.onResume()
progressDialogThread?.registerProgressTask()
}
override fun onPause() {
progressDialogThread?.unregisterProgressTask()
super.onPause()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> onBackPressed() android.R.id.home -> onBackPressed()
@@ -132,7 +116,7 @@ open class SettingsActivity
database.fileUri?.let { databaseUri -> database.fileUri?.let { databaseUri ->
// Show the progress dialog now or after dialog confirmation // Show the progress dialog now or after dialog confirmation
if (database.validatePasswordEncoding(masterPassword, keyFileChecked)) { if (database.validatePasswordEncoding(masterPassword, keyFileChecked)) {
progressDialogThread?.startDatabaseAssignPassword( mProgressDialogThread?.startDatabaseAssignPassword(
databaseUri, databaseUri,
masterPasswordChecked, masterPasswordChecked,
masterPassword, masterPassword,
@@ -142,7 +126,7 @@ open class SettingsActivity
} else { } else {
PasswordEncodingDialogFragment().apply { PasswordEncodingDialogFragment().apply {
positiveButtonClickListener = DialogInterface.OnClickListener { _, _ -> positiveButtonClickListener = DialogInterface.OnClickListener { _, _ ->
progressDialogThread?.startDatabaseAssignPassword( mProgressDialogThread?.startDatabaseAssignPassword(
databaseUri, databaseUri,
masterPasswordChecked, masterPasswordChecked,
masterPassword, masterPassword,

View File

@@ -87,7 +87,7 @@ class DatabaseColorPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialog
} }
val oldColor = database.customColor val oldColor = database.customColor
database.customColor = newColor database.customColor = newColor
progressDialogThread?.startDatabaseSaveColor(oldColor, newColor, true) mProgressDialogThread?.startDatabaseSaveColor(oldColor, newColor, mDatabaseAutoSaveEnable)
} }
onDialogClosed(true) onDialogClosed(true)

View File

@@ -64,7 +64,7 @@ class DatabaseDataCompressionPreferenceDialogFragmentCompat
database.compressionAlgorithm = newCompression database.compressionAlgorithm = newCompression
if (oldCompression != null && newCompression != null) if (oldCompression != null && newCompression != null)
progressDialogThread?.startDatabaseSaveCompression(oldCompression, newCompression, true) mProgressDialogThread?.startDatabaseSaveCompression(oldCompression, newCompression, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -36,7 +36,7 @@ class DatabaseDefaultUsernamePreferenceDialogFragmentCompat : DatabaseSavePrefer
val newDefaultUsername = inputText val newDefaultUsername = inputText
val oldDefaultUsername = database.defaultUsername val oldDefaultUsername = database.defaultUsername
database.defaultUsername = newDefaultUsername database.defaultUsername = newDefaultUsername
progressDialogThread?.startDatabaseSaveDefaultUsername(oldDefaultUsername, newDefaultUsername, true) mProgressDialogThread?.startDatabaseSaveDefaultUsername(oldDefaultUsername, newDefaultUsername, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -36,7 +36,7 @@ class DatabaseDescriptionPreferenceDialogFragmentCompat : DatabaseSavePreference
val newDescription = inputText val newDescription = inputText
val oldDescription = database.description val oldDescription = database.description
database.description = newDescription database.description = newDescription
progressDialogThread?.startDatabaseSaveDescription(oldDescription, newDescription, true) mProgressDialogThread?.startDatabaseSaveDescription(oldDescription, newDescription, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -65,7 +65,7 @@ class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
database.encryptionAlgorithm = newAlgorithm database.encryptionAlgorithm = newAlgorithm
if (oldAlgorithm != null && newAlgorithm != null) if (oldAlgorithm != null && newAlgorithm != null)
progressDialogThread?.startDatabaseSaveEncryption(oldAlgorithm, newAlgorithm, true) mProgressDialogThread?.startDatabaseSaveEncryption(oldAlgorithm, newAlgorithm, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -66,7 +66,7 @@ class DatabaseKeyDerivationPreferenceDialogFragmentCompat
val oldKdfEngine = database.kdfEngine val oldKdfEngine = database.kdfEngine
if (newKdfEngine != null && oldKdfEngine != null) { if (newKdfEngine != null && oldKdfEngine != null) {
database.kdfEngine = newKdfEngine database.kdfEngine = newKdfEngine
progressDialogThread?.startDatabaseSaveKeyDerivation(oldKdfEngine, newKdfEngine, true) mProgressDialogThread?.startDatabaseSaveKeyDerivation(oldKdfEngine, newKdfEngine, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -36,7 +36,7 @@ class DatabaseNamePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogF
val newName = inputText val newName = inputText
val oldName = database.name val oldName = database.name
database.name = newName database.name = newName
progressDialogThread?.startDatabaseSaveName(oldName, newName, true) mProgressDialogThread?.startDatabaseSaveName(oldName, newName, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -23,13 +23,14 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import com.kunzisoft.keepass.database.action.ProgressDialogThread import com.kunzisoft.keepass.database.action.ProgressDialogThread
import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.settings.SettingsActivity import com.kunzisoft.keepass.settings.SettingsActivity
abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialogFragmentCompat() { abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialogFragmentCompat() {
protected var database: Database? = null protected var database: Database? = null
protected var mDatabaseAutoSaveEnable = true
protected var progressDialogThread: ProgressDialogThread? = null protected var mProgressDialogThread: ProgressDialogThread? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -41,8 +42,10 @@ abstract class DatabaseSavePreferenceDialogFragmentCompat : InputPreferenceDialo
super.onAttach(context) super.onAttach(context)
// Attach dialog thread to start action // Attach dialog thread to start action
if (context is SettingsActivity) { if (context is SettingsActivity) {
progressDialogThread = context.progressDialogThread mProgressDialogThread = context.mProgressDialogThread
} }
this.mDatabaseAutoSaveEnable = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
} }
companion object { companion object {

View File

@@ -60,7 +60,7 @@ class MaxHistoryItemsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDial
// Remove all history items // Remove all history items
database.removeOldestHistoryForEachEntry() database.removeOldestHistoryForEachEntry()
progressDialogThread?.startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems, maxHistoryItems, true) mProgressDialogThread?.startDatabaseSaveMaxHistoryItems(oldMaxHistoryItems, maxHistoryItems, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -56,7 +56,7 @@ class MaxHistorySizePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialo
val oldMaxHistorySize = database.historyMaxSize val oldMaxHistorySize = database.historyMaxSize
database.historyMaxSize = maxHistorySize database.historyMaxSize = maxHistorySize
progressDialogThread?.startDatabaseSaveMaxHistorySize(oldMaxHistorySize, maxHistorySize, true) mProgressDialogThread?.startDatabaseSaveMaxHistorySize(oldMaxHistorySize, maxHistorySize, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -48,7 +48,7 @@ class MemoryUsagePreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
val oldMemoryUsage = database.memoryUsage val oldMemoryUsage = database.memoryUsage
database.memoryUsage = memoryUsage database.memoryUsage = memoryUsage
progressDialogThread?.startDatabaseSaveMemoryUsage(oldMemoryUsage, memoryUsage, true) mProgressDialogThread?.startDatabaseSaveMemoryUsage(oldMemoryUsage, memoryUsage, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -48,7 +48,7 @@ class ParallelismPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFr
val oldParallelism = database.parallelism val oldParallelism = database.parallelism
database.parallelism = parallelism database.parallelism = parallelism
progressDialogThread?.startDatabaseSaveParallelism(oldParallelism, parallelism, true) mProgressDialogThread?.startDatabaseSaveParallelism(oldParallelism, parallelism, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -54,7 +54,7 @@ class RoundsPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmen
database.numberKeyEncryptionRounds = Long.MAX_VALUE database.numberKeyEncryptionRounds = Long.MAX_VALUE
} }
progressDialogThread?.startDatabaseSaveIterations(oldRounds, rounds, true) mProgressDialogThread?.startDatabaseSaveIterations(oldRounds, rounds, mDatabaseAutoSaveEnable)
} }
} }
} }

View File

@@ -19,6 +19,8 @@
*/ */
package com.kunzisoft.keepass.utils package com.kunzisoft.keepass.utils
import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
@@ -27,10 +29,8 @@ import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.AboutActivity import com.kunzisoft.keepass.activities.AboutActivity
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper.READ_ONLY_DEFAULT import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper.READ_ONLY_DEFAULT
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.settings.SettingsActivity import com.kunzisoft.keepass.settings.SettingsActivity
object MenuUtil { object MenuUtil {
fun contributionMenuInflater(inflater: MenuInflater, menu: Menu) { fun contributionMenuInflater(inflater: MenuInflater, menu: Menu) {
@@ -43,33 +43,33 @@ object MenuUtil {
inflater.inflate(R.menu.default_menu, menu) inflater.inflate(R.menu.default_menu, menu)
} }
fun onContributionItemSelected(activity: StylishActivity) { fun onContributionItemSelected(context: Context) {
UriUtil.gotoUrl(activity, R.string.contribution_url) UriUtil.gotoUrl(context, R.string.contribution_url)
} }
/* /*
* @param checkLock Check the time lock before launch settings in LockingActivity * @param checkLock Check the time lock before launch settings in LockingActivity
*/ */
@JvmOverloads @JvmOverloads
fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, timeoutEnable: Boolean = false): Boolean { fun onDefaultMenuOptionsItemSelected(activity: Activity,
item: MenuItem,
readOnly: Boolean = READ_ONLY_DEFAULT,
timeoutEnable: Boolean = false): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_contribute -> { R.id.menu_contribute -> {
onContributionItemSelected(activity) onContributionItemSelected(activity)
return true return true
} }
R.id.menu_app_settings -> { R.id.menu_app_settings -> {
// To avoid flickering when launch settings in a LockingActivity // To avoid flickering when launch settings in a LockingActivity
SettingsActivity.launch(activity, readOnly, timeoutEnable) SettingsActivity.launch(activity, readOnly, timeoutEnable)
return true return true
} }
R.id.menu_about -> { R.id.menu_about -> {
val intent = Intent(activity, AboutActivity::class.java) val intent = Intent(activity, AboutActivity::class.java)
activity.startActivity(intent) activity.startActivity(intent)
return true return true
} }
else -> return true else -> return true
} }
} }

View File

@@ -24,4 +24,9 @@
android:title="@string/menu_lock" android:title="@string/menu_lock"
android:orderInCategory="81" android:orderInCategory="81"
app:showAsAction="always" /> app:showAsAction="always" />
<item android:id="@+id/menu_save_database"
android:icon="@drawable/ic_save_white_24dp"
android:title="@string/menu_save_database"
android:orderInCategory="95"
app:showAsAction="ifRoom" />
</menu> </menu>

View File

@@ -63,10 +63,12 @@
<string name="allow_no_password_key" translatable="false">allow_no_password_key</string> <string name="allow_no_password_key" translatable="false">allow_no_password_key</string>
<bool name="allow_no_password_default" translatable="false">false</bool> <bool name="allow_no_password_default" translatable="false">false</bool>
<string name="enable_read_only_key" translatable="false">enable_read_only_key</string>
<bool name="enable_read_only_default" translatable="false">false</bool>
<string name="delete_entered_password_key" translatable="false">delete_entered_password_key</string> <string name="delete_entered_password_key" translatable="false">delete_entered_password_key</string>
<bool name="delete_entered_password_default" translatable="false">true</bool> <bool name="delete_entered_password_default" translatable="false">true</bool>
<string name="enable_read_only_key" translatable="false">enable_read_only_key</string>
<bool name="enable_read_only_default" translatable="false">false</bool>
<string name="enable_auto_save_database_key" translatable="false">enable_auto_save_database_key</string>
<bool name="enable_auto_save_database_default" translatable="false">true</bool>
<string name="omitbackup_key" translatable="false">omitbackup</string> <string name="omitbackup_key" translatable="false">omitbackup</string>
<bool name="omitbackup_default" translatable="false">true</bool> <bool name="omitbackup_default" translatable="false">true</bool>
<string name="app_timeout_key" translatable="false">app_timeout_key</string> <string name="app_timeout_key" translatable="false">app_timeout_key</string>

View File

@@ -176,6 +176,7 @@
<string name="menu_cancel">Cancel</string> <string name="menu_cancel">Cancel</string>
<string name="menu_hide_password">Hide password</string> <string name="menu_hide_password">Hide password</string>
<string name="menu_lock">Lock database</string> <string name="menu_lock">Lock database</string>
<string name="menu_save_database">Save database</string>
<string name="menu_open">Open</string> <string name="menu_open">Open</string>
<string name="menu_search">Search</string> <string name="menu_search">Search</string>
<string name="menu_showpass">Show password</string> <string name="menu_showpass">Show password</string>
@@ -217,6 +218,7 @@
<string name="parallelism">Parallelism</string> <string name="parallelism">Parallelism</string>
<string name="parallelism_explanation">Degree of parallelism (i.e. number of threads) used by the key derivation function.</string> <string name="parallelism_explanation">Degree of parallelism (i.e. number of threads) used by the key derivation function.</string>
<string name="saving_database">Saving database…</string> <string name="saving_database">Saving database…</string>
<string name="command_execution">Executing the command…</string>
<string name="do_not_kill_app">Do not kill the app…</string> <string name="do_not_kill_app">Do not kill the app…</string>
<string name="space">Space</string> <string name="space">Space</string>
<string name="search_label">Search</string> <string name="search_label">Search</string>
@@ -365,10 +367,12 @@
<string name="keyboard_key_sound_title">Sound on keypress</string> <string name="keyboard_key_sound_title">Sound on keypress</string>
<string name="allow_no_password_title">Allow no master key</string> <string name="allow_no_password_title">Allow no master key</string>
<string name="allow_no_password_summary">Enable the \"Open\" button if no credentials are selected</string> <string name="allow_no_password_summary">Enable the \"Open\" button if no credentials are selected</string>
<string name="enable_read_only_title">Write-protected</string>
<string name="enable_read_only_summary">Open your database read-only by default</string>
<string name="delete_entered_password_title">Delete password</string> <string name="delete_entered_password_title">Delete password</string>
<string name="delete_entered_password_summary">Deletes the password entered after a connection attempt</string> <string name="delete_entered_password_summary">Deletes the password entered after a connection attempt</string>
<string name="enable_read_only_title">Write-protected</string>
<string name="enable_read_only_summary">Open your database read-only by default</string>
<string name="enable_auto_save_database_title">Auto save database</string>
<string name="enable_auto_save_database_summary">Automatically save the database after an important action (only in \"Modifiable\" mode)</string>
<string name="enable_education_screens_title">Educational screens</string> <string name="enable_education_screens_title">Educational screens</string>
<string name="enable_education_screens_summary">Highlight the elements to learn how the app works</string> <string name="enable_education_screens_summary">Highlight the elements to learn how the app works</string>
<string name="reset_education_screens_title">Reset educational screens</string> <string name="reset_education_screens_title">Reset educational screens</string>

View File

@@ -28,16 +28,21 @@
android:title="@string/allow_no_password_title" android:title="@string/allow_no_password_title"
android:summary="@string/allow_no_password_summary" android:summary="@string/allow_no_password_summary"
android:defaultValue="@bool/allow_no_password_default"/> android:defaultValue="@bool/allow_no_password_default"/>
<SwitchPreference
android:key="@string/delete_entered_password_key"
android:title="@string/delete_entered_password_title"
android:summary="@string/delete_entered_password_summary"
android:defaultValue="@bool/delete_entered_password_default"/>
<SwitchPreference <SwitchPreference
android:key="@string/enable_read_only_key" android:key="@string/enable_read_only_key"
android:title="@string/enable_read_only_title" android:title="@string/enable_read_only_title"
android:summary="@string/enable_read_only_summary" android:summary="@string/enable_read_only_summary"
android:defaultValue="@bool/enable_read_only_default"/> android:defaultValue="@bool/enable_read_only_default"/>
<SwitchPreference <SwitchPreference
android:key="@string/delete_entered_password_key" android:key="@string/enable_auto_save_database_key"
android:title="@string/delete_entered_password_title" android:title="@string/enable_auto_save_database_title"
android:summary="@string/delete_entered_password_summary" android:summary="@string/enable_auto_save_database_summary"
android:defaultValue="@bool/delete_entered_password_default"/> android:defaultValue="@bool/enable_auto_save_database_default"/>
</PreferenceCategory> </PreferenceCategory>