First commit to import and export app properties

This commit is contained in:
J-Jamet
2021-04-07 16:13:40 +02:00
parent 1c341c34a3
commit de69a78a98
12 changed files with 140 additions and 21 deletions

View File

@@ -505,7 +505,7 @@ class EntryEditActivity : LockingActivity(),
entryEditFragment?.icon = icon
}
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
uri?.let { attachmentToUploadUri ->
UriUtil.getFileData(this, attachmentToUploadUri)?.also { documentFile ->
documentFile.name?.let { fileName ->

View File

@@ -355,7 +355,7 @@ class FileDatabaseSelectActivity : SpecialModeActivity(),
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
}
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
if (uri != null) {
launchPasswordActivityWithPath(uri)
}

View File

@@ -276,7 +276,7 @@ class IconPickerActivity : LockingActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
addCustomIcon(uri)
}
}

View File

@@ -695,7 +695,7 @@ open class PasswordActivity : SpecialModeActivity(), AdvancedUnlockFragment.Buil
var keyFileResult = false
mExternalFileHelper?.let {
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data
keyFileResult = it.onOpenDocumentResult(requestCode, resultCode, data
) { uri ->
if (uri != null) {
mDatabaseKeyFileUri = uri

View File

@@ -286,7 +286,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
mExternalFileHelper?.onActivityResultCallback(requestCode, resultCode, data) { uri ->
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { uri ->
uri?.let { pathUri ->
UriUtil.getFileData(requireContext(), uri)?.length()?.let { lengthFile ->
keyFileSelectionView?.error = null

View File

@@ -104,11 +104,11 @@ class ExternalFileHelper {
/**
* To use in onActivityResultCallback in Fragment or Activity
* @param keyFileCallback Callback retrieve from data
* @return true if requestCode was captured, false elsechere
* @param onFileSelected Callback retrieve from data
* @return true if requestCode was captured, false elsewhere
*/
fun onActivityResultCallback(requestCode: Int, resultCode: Int, data: Intent?,
keyFileCallback: ((uri: Uri?) -> Unit)?): Boolean {
fun onOpenDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
onFileSelected: ((uri: Uri?) -> Unit)?): Boolean {
when (requestCode) {
FILE_BROWSE -> {
@@ -118,7 +118,7 @@ class ExternalFileHelper {
if (filename != null) {
keyUri = UriUtil.parse(filename)
}
keyFileCallback?.invoke(keyUri)
onFileSelected?.invoke(keyUri)
}
return true
}
@@ -138,7 +138,7 @@ class ExternalFileHelper {
} catch (e: Exception) {
// nop
}
keyFileCallback?.invoke(uri)
onFileSelected?.invoke(uri)
}
}
}
@@ -190,11 +190,16 @@ class ExternalFileHelper {
return null
}
/**
* To use in onActivityResultCallback in Fragment or Activity
* @param onFileCreated Callback retrieve from data
* @return true if requestCode was captured, false elsewhere
*/
fun onCreateDocumentResult(requestCode: Int, resultCode: Int, data: Intent?,
action: (fileCreated: Uri?)->Unit) {
onFileCreated: (fileCreated: Uri?)->Unit) {
// Retrieve the created URI from the file manager
if (fileRequestCodes.contains(requestCode) && resultCode == RESULT_OK) {
action.invoke(data?.data)
onFileCreated.invoke(data?.data)
fileRequestCodes.remove(requestCode)
}
}

View File

@@ -90,6 +90,20 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
}
true
}
findPreference<Preference>(getString(R.string.import_app_properties_key))?.setOnPreferenceClickListener { _ ->
(activity as? SettingsActivity?)?.apply {
importAppProperties()
}
true
}
findPreference<Preference>(getString(R.string.export_app_properties_key))?.setOnPreferenceClickListener { _ ->
(activity as? SettingsActivity?)?.apply {
exportAppProperties()
}
true
}
}
}
@@ -388,10 +402,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
Stylish.assignStyle(activity, styleIdString)
// Relaunch the current activity to redraw theme
(activity as? SettingsActivity?)?.apply {
keepCurrentScreen()
startActivity(intent)
finish()
activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
relaunchCurrentScreen()
}
}
styleEnabled
@@ -399,10 +410,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
findPreference<ListPreference>(getString(R.string.setting_style_brightness_key))?.setOnPreferenceChangeListener { _, _ ->
(activity as? SettingsActivity?)?.apply {
keepCurrentScreen()
startActivity(intent)
finish()
activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
relaunchCurrentScreen()
}
true
}

View File

@@ -517,4 +517,21 @@ object PreferencesUtil {
.putStringSet(context.getString(R.string.autofill_web_domain_blocklist_key), setItems)
.apply()
}
fun getAppProperties(context: Context): Properties {
val allPreferences = PreferenceManager.getDefaultSharedPreferences(context).all
val properties = Properties()
for ((name, value) in allPreferences) {
properties[name] = value.toString()
}
return properties
}
fun setAppProperties(context: Context, properties: Properties) {
PreferenceManager.getDefaultSharedPreferences(context).edit().also {
for ((name, value) in properties) {
// TODO Set app properties
}
}.apply()
}
}

View File

@@ -24,14 +24,17 @@ import android.app.backup.BackupManager
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
import com.kunzisoft.keepass.activities.lock.LockingActivity
import com.kunzisoft.keepass.activities.lock.resetAppTimeoutWhenViewFocusedOrChanged
@@ -40,6 +43,7 @@ import com.kunzisoft.keepass.model.MainCredential
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
import java.util.*
open class SettingsActivity
: LockingActivity(),
@@ -48,6 +52,8 @@ open class SettingsActivity
PasswordEncodingDialogFragment.Listener {
private var backupManager: BackupManager? = null
private var mExternalFileHelper: ExternalFileHelper? = null
private var appPropertiesFileCreationRequestCode: Int? = null
private var coordinatorLayout: CoordinatorLayout? = null
private var toolbar: Toolbar? = null
@@ -70,6 +76,8 @@ open class SettingsActivity
coordinatorLayout = findViewById(R.id.toolbar_coordinator)
toolbar = findViewById(R.id.toolbar)
mExternalFileHelper = ExternalFileHelper(this)
if (savedInstanceState?.getString(TITLE_KEY).isNullOrEmpty())
toolbar?.setTitle(R.string.settings)
else
@@ -216,6 +224,13 @@ open class SettingsActivity
hideOrShowLockButton(key)
}
fun relaunchCurrentScreen() {
keepCurrentScreen()
startActivity(intent)
finish()
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
}
/**
* To keep the current screen when activity is reloaded
*/
@@ -235,6 +250,53 @@ open class SettingsActivity
replaceFragment(key, reload)
}
fun importAppProperties() {
mExternalFileHelper?.openDocument()
}
fun exportAppProperties() {
appPropertiesFileCreationRequestCode = mExternalFileHelper?.createDocument(getString(R.string.app_properties_file_name))
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Import app properties result
try {
mExternalFileHelper?.onOpenDocumentResult(requestCode, resultCode, data) { selectedfileUri ->
selectedfileUri?.let { uri ->
val appProperties = Properties()
contentResolver?.openInputStream(uri)?.use { inputStream ->
appProperties.load(inputStream)
}
PreferencesUtil.setAppProperties(this, appProperties)
relaunchCurrentScreen()
}
}
} catch (e: Exception) {
Log.e(TAG, "Unable to import app properties", e)
}
// Export app properties result
try {
if (requestCode == appPropertiesFileCreationRequestCode) {
mExternalFileHelper?.onCreateDocumentResult(requestCode, resultCode, data) { createdFileUri ->
createdFileUri?.let { uri ->
contentResolver?.openOutputStream(uri)?.use { outputStream ->
PreferencesUtil
.getAppProperties(this)
.store(outputStream, getString(R.string.description_app_properties))
}
Toast.makeText(this, R.string.export_app_properties_success, Toast.LENGTH_LONG).show()
}
}
appPropertiesFileCreationRequestCode = null
}
} catch (e: Exception) {
Log.e(LockingActivity.TAG, "Unable to export app properties", e)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
@@ -244,6 +306,8 @@ open class SettingsActivity
companion object {
private val TAG = SettingsActivity::class.java.name
private const val SHOW_LOCK = "SHOW_LOCK"
private const val TITLE_KEY = "TITLE_KEY"
private const val TAG_NESTED = "TAG_NESTED"

View File

@@ -47,6 +47,7 @@
<string name="database_file_name_default" translatable="false">keepass</string>
<string name="database_file_extension_default" translatable="false">.kdbx</string>
<string name="database_default_name" translatable="false">KeePassDX Database</string>
<string name="app_properties_file_name" translatable="false">keepassdx.properties</string>
<!--
*******************
@@ -105,6 +106,9 @@
<string name="temp_advanced_unlock_timeout_default" translatable="false">36000000</string>
<string name="biometric_delete_all_key_key" translatable="false">biometric_delete_all_key_key</string>
<string name="import_app_properties_key" translatable="false">import_app_properties_key</string>
<string name="export_app_properties_key" translatable="false">export_app_properties_key</string>
<!-- Form Filling Settings -->
<string name="settings_form_filling_key" translatable="false">settings_form_filling_key</string>

View File

@@ -234,6 +234,12 @@
<string name="show_recent_files_summary">Show locations of recent databases</string>
<string name="hide_broken_locations_title">Hide broken database links</string>
<string name="hide_broken_locations_summary">Hide broken links in the list of recent databases</string>
<string name="import_app_properties_title">Import app properties</string>
<string name="import_app_properties_summary">Select a file to import app properties</string>
<string name="export_app_properties_title">Export app properties</string>
<string name="export_app_properties_summary">Create a file to export app properties</string>
<string name="description_app_properties">KeePassDX properties to manage app settings</string>
<string name="export_app_properties_success">App properties exported</string>
<string name="root">Root</string>
<string name="encryption_explanation">Database encryption algorithm used for all data.</string>
<string name="kdf_explanation">To generate the key for the encryption algorithm, the master key is transformed using a randomly salted key derivation function.</string>
@@ -302,6 +308,7 @@
<string name="advanced_unlock_prompt_not_initialized">Unable to initialize advanced unlock prompt.</string>
<string name="credential_before_click_advanced_unlock_button">Type in the password, and then click this button.</string>
<string name="database_history">History</string>
<string name="properties">Properties</string>
<string name="menu_appearance_settings">Appearance</string>
<string name="biometric">Biometric</string>
<string name="device_credential">Device credential</string>

View File

@@ -150,4 +150,18 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/properties">
<Preference
android:key="@string/import_app_properties_key"
android:title="@string/import_app_properties_title"
android:summary="@string/import_app_properties_summary"/>
<Preference
android:key="@string/export_app_properties_key"
android:title="@string/export_app_properties_title"
android:summary="@string/export_app_properties_summary"/>
</PreferenceCategory>
</PreferenceScreen>