/* * Copyright 2019 Jeremy Jamet / Kunzisoft. * * This file is part of KeePassDX. * * KeePassDX is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * KeePassDX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with KeePassDX. If not, see . * */ package com.kunzisoft.keepass.settings import android.graphics.Color import android.os.Bundle import android.util.Log import android.view.* import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.SwitchPreference import com.kunzisoft.androidclearchroma.ChromaUtil import com.kunzisoft.keepass.R import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine import com.kunzisoft.keepass.database.element.Database import com.kunzisoft.keepass.database.element.Group import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm import com.kunzisoft.keepass.database.element.template.TemplateEngine import com.kunzisoft.keepass.services.DatabaseTaskNotificationService import com.kunzisoft.keepass.settings.preference.* import com.kunzisoft.keepass.settings.preferencedialogfragment.* import com.kunzisoft.keepass.tasks.ActionRunnable import com.kunzisoft.keepass.viewmodels.DatabaseViewModel class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetrieval { private val mDatabaseViewModel: DatabaseViewModel by activityViewModels() private var mDatabase: Database? = null private var mDatabaseReadOnly: Boolean = false private var mMergeDataAllowed: Boolean = false private var mDatabaseAutoSaveEnabled: Boolean = true private var mScreen: Screen? = null private var dbNamePref: InputTextPreference? = null private var dbDescriptionPref: InputTextPreference? = null private var dbDefaultUsernamePref: InputTextPreference? = null private var dbCustomColorPref: DialogColorPreference? = null private var dbDataCompressionPref: Preference? = null private var recycleBinGroupPref: DialogListExplanationPreference? = null private var templatesGroupPref: DialogListExplanationPreference? = 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: InputKdfSizePreference? = null private var mParallelismPref: InputKdfNumberPreference? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mDatabaseViewModel.database.observe(viewLifecycleOwner) { database -> mDatabase = database view.resetAppTimeoutWhenViewTouchedOrFocused(requireContext(), database?.loaded) onDatabaseRetrieved(database) } mDatabaseViewModel.actionFinished.observe(viewLifecycleOwner) { onDatabaseActionFinished(it.database, it.actionTask, it.result) } } override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) { setHasOptionsMenu(true) mScreen = screen val database = mDatabase // Load the preferences from an XML resource when (screen) { Screen.DATABASE -> { setPreferencesFromResource(R.xml.preferences_database, rootKey) if (database?.loaded == true) onCreateDatabasePreference(database) } Screen.DATABASE_SECURITY -> { setPreferencesFromResource(R.xml.preferences_database_security, rootKey) if (database?.loaded == true) onCreateDatabaseSecurityPreference(database) } Screen.DATABASE_MASTER_KEY -> { setPreferencesFromResource(R.xml.preferences_database_master_key, rootKey) if (database?.loaded == true) onCreateDatabaseMasterKeyPreference(database) } else -> { } } } private fun saveDatabase(save: Boolean) { mDatabaseViewModel.saveDatabase(save) } private fun mergeDatabase() { mDatabaseViewModel.mergeDatabase(false) } private fun reloadDatabase() { mDatabaseViewModel.reloadDatabase(false) } override fun onDatabaseRetrieved(database: Database?) { mDatabase = database mDatabaseReadOnly = database?.isReadOnly == true mMergeDataAllowed = database?.isMergeDataAllowed() == true mDatabase?.let { if (it.loaded) { when (mScreen) { Screen.DATABASE -> { onCreateDatabasePreference(it) } Screen.DATABASE_SECURITY -> { onCreateDatabaseSecurityPreference(it) } Screen.DATABASE_MASTER_KEY -> { onCreateDatabaseMasterKeyPreference(it) } else -> { } } } else { Log.e(javaClass.name, "Database isn't ready") } } } private fun onCreateDatabasePreference(database: Database) { val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key)) // Database name dbNamePref = findPreference(getString(R.string.database_name_key)) dbNamePref?.let { namePreference -> if (database.allowName) { namePreference.summary = database.name } else { dbGeneralPrefCategory?.removePreference(namePreference) } } // Database description dbDescriptionPref = findPreference(getString(R.string.database_description_key)) dbDescriptionPref?.let { descriptionPreference -> if (database.allowDescription) { dbDescriptionPref?.summary = database.description } else { dbGeneralPrefCategory?.removePreference(descriptionPreference) } } // Database default username dbDefaultUsernamePref = findPreference(getString(R.string.database_default_username_key)) dbDefaultUsernamePref?.summary = database.defaultUsername // Database custom color dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key)) dbCustomColorPref?.apply { val customColor = database.customColor if (customColor != null) { color = customColor summary = ChromaUtil.getFormattedColorString(customColor, false) } else{ color = DialogColorPreference.DISABLE_COLOR summary = "" } } // Version findPreference(getString(R.string.database_version_key)) ?.summary = database.version val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_data_key)) // Database compression dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key)) if (database.allowDataCompression) { dbDataCompressionPref?.summary = (database.compressionAlgorithm ?: CompressionAlgorithm.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 (database.allowConfigurableRecycleBin) { val recycleBinEnablePref: SwitchPreference? = findPreference(getString(R.string.recycle_bin_enable_key)) recycleBinEnablePref?.apply { isChecked = database.isRecycleBinEnabled isEnabled = if (!mDatabaseReadOnly) { setOnPreferenceChangeListener { _, newValue -> val recycleBinEnabled = newValue as Boolean database.enableRecycleBin(recycleBinEnabled, resources) refreshRecycleBinGroup(database) // Save the database if not in readonly mode saveDatabase(mDatabaseAutoSaveEnabled) true } true } else { false } } // Change the recycle bin group recycleBinGroupPref?.setOnPreferenceClickListener { true } // Recycle Bin group refreshRecycleBinGroup(database) } else { recycleBinGroupPref?.onPreferenceClickListener = null dbRecycleBinPrefCategory?.isVisible = false } // Templates val templatesGroupPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_templates_key)) templatesGroupPref = findPreference(getString(R.string.templates_group_uuid_key)) if (database.allowTemplatesGroup) { val templatesEnablePref: SwitchPreference? = findPreference(getString(R.string.templates_group_enable_key)) templatesEnablePref?.apply { isChecked = database.isTemplatesEnabled isEnabled = if (!mDatabaseReadOnly) { setOnPreferenceChangeListener { _, newValue -> val templatesEnabled = newValue as Boolean database.enableTemplates(templatesEnabled, TemplateEngine.getDefaultTemplateGroupName(resources) ) refreshTemplatesGroup(database) // Save the database if not in readonly mode saveDatabase(mDatabaseAutoSaveEnabled) true } true } else { false } } // Refresh templates group refreshTemplatesGroup(database) } else { templatesGroupPrefCategory?.isVisible = false } // History findPreference(getString(R.string.database_category_history_key)) ?.isVisible = database.manageHistory == true // Max history items dbMaxHistoryItemsPref = findPreference(getString(R.string.max_history_items_key))?.apply { summary = database.historyMaxItems.toString() } // Max history size dbMaxHistorySizePref = findPreference(getString(R.string.max_history_size_key))?.apply { summary = database.historyMaxSize.toString() } } private fun refreshRecycleBinGroup(database: Database?) { recycleBinGroupPref?.apply { if (database?.isRecycleBinEnabled == true) { summary = database.recycleBin?.toString() isEnabled = true } else { summary = null isEnabled = false } } } private fun refreshTemplatesGroup(database: Database?) { templatesGroupPref?.apply { if (database?.isTemplatesEnabled == true) { summary = database.templatesGroup?.toString() isEnabled = true } else { summary = null isEnabled = false } } } private fun onCreateDatabaseSecurityPreference(database: Database) { // Encryption Algorithm mEncryptionAlgorithmPref = findPreference(getString(R.string.encryption_algorithm_key))?.apply { summary = database.getEncryptionAlgorithmName() } // Key derivation function mKeyDerivationPref = findPreference(getString(R.string.key_derivation_function_key))?.apply { summary = database.getKeyDerivationName() } // Round encryption mRoundPref = findPreference(getString(R.string.transform_rounds_key))?.apply { summary = database.numberKeyEncryptionRounds.toString() } // Memory Usage mMemoryPref = findPreference(getString(R.string.memory_usage_key))?.apply { summary = database.memoryUsage.toString() } // Parallelism mParallelismPref = findPreference(getString(R.string.parallelism_key))?.apply { summary = database.parallelism.toString() } } private fun onCreateDatabaseMasterKeyPreference(database: Database) { findPreference(getString(R.string.settings_database_change_credentials_key))?.apply { isEnabled = if (!mDatabaseReadOnly) { onPreferenceClickListener = Preference.OnPreferenceClickListener { AssignMasterKeyDialogFragment.getInstance(database.allowNoMasterKey) .show(parentFragmentManager, "passwordDialog") false } true } else { false } } } private val colorSelectedListener: ((Int?)-> Unit) = { color -> if (color != null) { dbCustomColorPref?.color = color dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false) } else { dbCustomColorPref?.color = DialogColorPreference.DISABLE_COLOR dbCustomColorPref?.summary = "" } } 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 = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat? chromaDialog?.onColorSelectedListener = colorSelectedListener } catch (e: Exception) {} return view } // TODO check error override fun onDatabaseActionFinished(database: Database, 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 { database.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 { database.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 } dbDefaultUsernamePref?.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 = Color.parseColor(oldColor) oldColor } dbCustomColorPref?.summary = defaultColorToShow } DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> { val oldCompression = data.getSerializable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) as CompressionAlgorithm val newCompression = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as CompressionAlgorithm val algorithmToShow = if (result.isSuccess) { newCompression } else { mDatabase?.compressionAlgorithm = oldCompression oldCompression } dbDataCompressionPref?.summary = algorithmToShow.getName(resources) } DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK -> { val oldRecycleBin = data.getParcelable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) val newRecycleBin = data.getParcelable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) val recycleBinToShow = if (result.isSuccess) { newRecycleBin } else { oldRecycleBin } mDatabase?.setRecycleBin(recycleBinToShow) refreshRecycleBinGroup(database) } DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK -> { val oldTemplatesGroup = data.getParcelable(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) val newTemplatesGroup = data.getParcelable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) val templatesGroupToShow = if (result.isSuccess) { newTemplatesGroup } else { oldTemplatesGroup } mDatabase?.setTemplatesGroup(templatesGroupToShow) refreshTemplatesGroup(database) } 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 EncryptionAlgorithm val newEncryption = data.getSerializable(DatabaseTaskNotificationService.NEW_ELEMENT_KEY) as EncryptionAlgorithm val algorithmToShow = if (result.isSuccess) { newEncryption } else { mDatabase?.encryptionAlgorithm = oldEncryption oldEncryption } mEncryptionAlgorithmPref?.summary = algorithmToShow.toString() } 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.toString() 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.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY) val newParallelism = data.getLong(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 var dialogFragment: DialogFragment? = null // Main Preferences when (preference.key) { getString(R.string.database_name_key) -> { dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.database_description_key) -> { dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.database_default_username_key) -> { dialogFragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.database_custom_color_key) -> { dialogFragment = DatabaseColorPreferenceDialogFragmentCompat.newInstance(preference.key).apply { onColorSelectedListener = colorSelectedListener } } getString(R.string.database_data_compression_key) -> { dialogFragment = DatabaseDataCompressionPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.database_data_remove_unlinked_attachments_key) -> { dialogFragment = DatabaseRemoveUnlinkedDataPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.recycle_bin_group_key) -> { dialogFragment = DatabaseRecycleBinGroupPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.templates_group_uuid_key) -> { dialogFragment = DatabaseTemplatesGroupPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.max_history_items_key) -> { dialogFragment = DatabaseMaxHistoryItemsPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.max_history_size_key) -> { dialogFragment = DatabaseMaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key) } // Security getString(R.string.encryption_algorithm_key) -> { dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(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 } getString(R.string.transform_rounds_key) -> { dialogFragment = DatabaseRoundsPreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.memory_usage_key) -> { dialogFragment = DatabaseMemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.key) } getString(R.string.parallelism_key) -> { dialogFragment = DatabaseParallelismPreferenceDialogFragmentCompat.newInstance(preference.key) } else -> otherDialogFragment = true } if (dialogFragment != null && !mDatabaseReadOnly) { @Suppress("DEPRECATION") dialogFragment.setTargetFragment(this, 0) dialogFragment.show(parentFragmentManager, 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 menu.findItem(R.id.menu_merge_database)?.isVisible = false } if (!mMergeDataAllowed) { menu.findItem(R.id.menu_merge_database)?.isVisible = false } } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_save_database -> { saveDatabase(!mDatabaseReadOnly) true } R.id.menu_merge_database -> { mergeDatabase() true } R.id.menu_reload_database -> { reloadDatabase() true } R.id.menu_app_settings -> { // Check the time lock before launching settings // TODO activity menu (activity as SettingsActivity?)?.let { SettingsActivity.launch(it, true) } true } else -> { super.onOptionsItemSelected(item) } } } override fun onPreferenceTreeClick(preference: Preference): Boolean { // To reload group when database settings are modified when (preference.key) { getString(R.string.database_name_key), getString(R.string.database_description_key), getString(R.string.database_default_username_key), getString(R.string.database_custom_color_key), getString(R.string.database_data_compression_key), getString(R.string.database_data_remove_unlinked_attachments_key), getString(R.string.recycle_bin_enable_key), getString(R.string.recycle_bin_group_key), getString(R.string.templates_group_enable_key), getString(R.string.templates_group_uuid_key), getString(R.string.max_history_items_key), getString(R.string.max_history_size_key) -> { NestedAppSettingsFragment.DATABASE_PREFERENCE_CHANGED = true } } return super.onPreferenceTreeClick(preference) } companion object { private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT" } }