remove ClassicPrefsFormat
This commit is contained in:
parent
fb49c93104
commit
29da63d264
13 changed files with 67 additions and 264 deletions
|
@ -3,7 +3,6 @@ package info.nightscout.androidaps.dependencyInjection
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.android.ContributesAndroidInjector
|
import dagger.android.ContributesAndroidInjector
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat
|
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat
|
import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat
|
||||||
import info.nightscout.androidaps.utils.CryptoUtil
|
import info.nightscout.androidaps.utils.CryptoUtil
|
||||||
|
|
||||||
|
@ -13,6 +12,5 @@ abstract class PreferencesModule {
|
||||||
|
|
||||||
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
|
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
|
||||||
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
|
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
|
||||||
@ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat
|
|
||||||
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider
|
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider
|
||||||
}
|
}
|
|
@ -14,11 +14,11 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.BuildConfig
|
import info.nightscout.androidaps.BuildConfig
|
||||||
import info.nightscout.androidaps.MainApp
|
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
|
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
|
||||||
import info.nightscout.androidaps.activities.PreferencesActivity
|
import info.nightscout.androidaps.activities.PreferencesActivity
|
||||||
import info.nightscout.androidaps.database.AppRepository
|
import info.nightscout.androidaps.database.AppRepository
|
||||||
|
import info.nightscout.androidaps.database.entities.UserEntry
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
import info.nightscout.androidaps.database.entities.UserEntry.Action
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
import info.nightscout.androidaps.database.entities.UserEntry.Sources
|
||||||
import info.nightscout.androidaps.events.EventAppExit
|
import info.nightscout.androidaps.events.EventAppExit
|
||||||
|
@ -42,6 +42,8 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||||
import info.nightscout.androidaps.utils.protection.PasswordCheck
|
import info.nightscout.androidaps.utils.protection.PasswordCheck
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import info.nightscout.androidaps.utils.storage.Storage
|
||||||
|
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -63,7 +65,6 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
private val passwordCheck: PasswordCheck,
|
private val passwordCheck: PasswordCheck,
|
||||||
private val config: Config,
|
private val config: Config,
|
||||||
private val androidPermission: AndroidPermission,
|
private val androidPermission: AndroidPermission,
|
||||||
private val classicPrefsFormat: ClassicPrefsFormat,
|
|
||||||
private val encryptedPrefsFormat: EncryptedPrefsFormat,
|
private val encryptedPrefsFormat: EncryptedPrefsFormat,
|
||||||
private val prefFileList: PrefFileListProvider,
|
private val prefFileList: PrefFileListProvider,
|
||||||
private val uel: UserEntryLogger,
|
private val uel: UserEntryLogger,
|
||||||
|
@ -103,12 +104,7 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(BuildConfig.VERSION_NAME, PrefsStatus.OK)
|
metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(BuildConfig.VERSION_NAME, PrefsStatus.OK)
|
||||||
metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(BuildConfig.FLAVOR, PrefsStatus.OK)
|
metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(BuildConfig.FLAVOR, PrefsStatus.OK)
|
||||||
metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatus.OK)
|
metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatus.OK)
|
||||||
|
metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Enabled", PrefsStatus.OK)
|
||||||
if (prefsEncryptionIsDisabled()) {
|
|
||||||
metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Disabled", PrefsStatus.DISABLED)
|
|
||||||
} else {
|
|
||||||
metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Enabled", PrefsStatus.OK)
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
@ -132,9 +128,6 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
return if (patientName.isNotEmpty() && patientName != defaultPatientName) patientName else systemName
|
return if (patientName.isNotEmpty() && patientName != defaultPatientName) patientName else systemName
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prefsEncryptionIsDisabled() =
|
|
||||||
buildHelper.isEngineeringMode() && !sp.getBoolean(R.string.key_maintenance_encrypt_exported_prefs, true)
|
|
||||||
|
|
||||||
private fun askForMasterPass(activity: FragmentActivity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
private fun askForMasterPass(activity: FragmentActivity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
||||||
passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { password ->
|
passwordCheck.queryPassword(activity, R.string.master_password, R.string.key_master_password, { password ->
|
||||||
then(password)
|
then(password)
|
||||||
|
@ -155,11 +148,7 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
|
|
||||||
@Suppress("SameParameterValue")
|
@Suppress("SameParameterValue")
|
||||||
private fun askForMasterPassIfNeeded(activity: FragmentActivity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
private fun askForMasterPassIfNeeded(activity: FragmentActivity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
|
||||||
if (prefsEncryptionIsDisabled()) {
|
|
||||||
then("")
|
|
||||||
} else {
|
|
||||||
askForMasterPass(activity, canceledMsg, then)
|
askForMasterPass(activity, canceledMsg, then)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assureMasterPasswordSet(activity: FragmentActivity, @StringRes wrongPwdTitle: Int): Boolean {
|
private fun assureMasterPasswordSet(activity: FragmentActivity, @StringRes wrongPwdTitle: Int): Boolean {
|
||||||
|
@ -179,7 +168,7 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun askToConfirmExport(activity: FragmentActivity, fileToExport: File, then: ((password: String) -> Unit)) {
|
private fun askToConfirmExport(activity: FragmentActivity, fileToExport: File, then: ((password: String) -> Unit)) {
|
||||||
if (!prefsEncryptionIsDisabled() && !assureMasterPasswordSet(activity, R.string.nav_export)) return
|
if (!assureMasterPasswordSet(activity, R.string.nav_export)) return
|
||||||
|
|
||||||
TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_export),
|
TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_export),
|
||||||
resourceHelper.gs(R.string.export_to) + " " + fileToExport.name + " ?",
|
resourceHelper.gs(R.string.export_to) + " " + fileToExport.name + " ?",
|
||||||
|
@ -189,20 +178,12 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun askToConfirmImport(activity: FragmentActivity, fileToImport: PrefsFile, then: ((password: String) -> Unit)) {
|
private fun askToConfirmImport(activity: FragmentActivity, fileToImport: PrefsFile, then: ((password: String) -> Unit)) {
|
||||||
|
if (!assureMasterPasswordSet(activity, R.string.nav_import)) return
|
||||||
if (fileToImport.handler == PrefsFormatsHandler.ENCRYPTED) {
|
TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_import),
|
||||||
if (!assureMasterPasswordSet(activity, R.string.nav_import)) return
|
resourceHelper.gs(R.string.import_from) + " " + fileToImport.name + " ?",
|
||||||
TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_import),
|
resourceHelper.gs(R.string.password_preferences_decrypt_prompt), {
|
||||||
resourceHelper.gs(R.string.import_from) + " " + fileToImport.name + " ?",
|
askForMasterPass(activity, R.string.preferences_import_canceled, then)
|
||||||
resourceHelper.gs(R.string.password_preferences_decrypt_prompt), {
|
}, null, R.drawable.ic_header_import)
|
||||||
askForMasterPass(activity, R.string.preferences_import_canceled, then)
|
|
||||||
}, null, R.drawable.ic_header_import)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.nav_import),
|
|
||||||
resourceHelper.gs(R.string.import_from) + " " + fileToImport.file + " ?",
|
|
||||||
Runnable { then("") })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptForDecryptionPasswordIfNeeded(activity: FragmentActivity, prefs: Prefs, importOk: Boolean,
|
private fun promptForDecryptionPasswordIfNeeded(activity: FragmentActivity, prefs: Prefs, importOk: Boolean,
|
||||||
|
@ -230,7 +211,6 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
private fun exportSharedPreferences(activity: FragmentActivity) {
|
private fun exportSharedPreferences(activity: FragmentActivity) {
|
||||||
|
|
||||||
prefFileList.ensureExportDirExists()
|
prefFileList.ensureExportDirExists()
|
||||||
val legacyFile = prefFileList.legacyFile()
|
|
||||||
val newFile = prefFileList.newExportFile()
|
val newFile = prefFileList.newExportFile()
|
||||||
|
|
||||||
askToConfirmExport(activity, newFile) { password ->
|
askToConfirmExport(activity, newFile) { password ->
|
||||||
|
@ -242,9 +222,6 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
|
|
||||||
val prefs = Prefs(entries, prepareMetadata(activity))
|
val prefs = Prefs(entries, prepareMetadata(activity))
|
||||||
|
|
||||||
if (BuildConfig.DEBUG && buildHelper.isEngineeringMode()) {
|
|
||||||
classicPrefsFormat.savePreferences(legacyFile, prefs)
|
|
||||||
}
|
|
||||||
encryptedPrefsFormat.savePreferences(newFile, prefs, password)
|
encryptedPrefsFormat.savePreferences(newFile, prefs, password)
|
||||||
|
|
||||||
ToastUtils.okToast(activity, resourceHelper.gs(R.string.exported))
|
ToastUtils.okToast(activity, resourceHelper.gs(R.string.exported))
|
||||||
|
@ -292,10 +269,7 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
|
|
||||||
askToConfirmImport(activity, importFile) { password ->
|
askToConfirmImport(activity, importFile) { password ->
|
||||||
|
|
||||||
val format: PrefsFormat = when (importFile.handler) {
|
val format: PrefsFormat = encryptedPrefsFormat
|
||||||
PrefsFormatsHandler.CLASSIC -> classicPrefsFormat
|
|
||||||
PrefsFormatsHandler.ENCRYPTED -> encryptedPrefsFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
@ -380,10 +354,11 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
@Inject lateinit var injector: HasAndroidInjector
|
@Inject lateinit var injector: HasAndroidInjector
|
||||||
@Inject lateinit var aapsLogger: AAPSLogger
|
@Inject lateinit var aapsLogger: AAPSLogger
|
||||||
@Inject lateinit var repository: AppRepository
|
@Inject lateinit var repository: AppRepository
|
||||||
@Inject lateinit var classicPrefsFormat: ClassicPrefsFormat
|
|
||||||
@Inject lateinit var resourceHelper: ResourceHelper
|
@Inject lateinit var resourceHelper: ResourceHelper
|
||||||
@Inject lateinit var prefFileList: PrefFileListProvider
|
@Inject lateinit var prefFileList: PrefFileListProvider
|
||||||
@Inject lateinit var mainApp: MainApp
|
@Inject lateinit var context: Context
|
||||||
|
@Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper
|
||||||
|
@Inject lateinit var storage: Storage
|
||||||
|
|
||||||
init {
|
init {
|
||||||
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
|
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
|
||||||
|
@ -395,18 +370,30 @@ class ImportExportPrefsImpl @Inject constructor(
|
||||||
val newFile = prefFileList.newExportCsvFile()
|
val newFile = prefFileList.newExportCsvFile()
|
||||||
var ret = Result.success()
|
var ret = Result.success()
|
||||||
try {
|
try {
|
||||||
classicPrefsFormat.saveCsv(newFile, entries)
|
saveCsv(newFile, entries)
|
||||||
ToastUtils.okToast(mainApp, resourceHelper.gs(R.string.ue_exported))
|
ToastUtils.okToast(context, resourceHelper.gs(R.string.ue_exported))
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
ToastUtils.errorToast(mainApp, resourceHelper.gs(R.string.filenotfound) + " " + newFile)
|
ToastUtils.errorToast(context, resourceHelper.gs(R.string.filenotfound) + " " + newFile)
|
||||||
aapsLogger.error(LTag.CORE, "Unhandled exception", e)
|
aapsLogger.error(LTag.CORE, "Unhandled exception", e)
|
||||||
ret = Result.failure(workDataOf("Error" to "Error FileNotFoundException"))
|
ret = Result.failure(workDataOf("Error" to "Error FileNotFoundException"))
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
ToastUtils.errorToast(mainApp, e.message)
|
ToastUtils.errorToast(context, e.message)
|
||||||
aapsLogger.error(LTag.CORE, "Unhandled exception", e)
|
aapsLogger.error(LTag.CORE, "Unhandled exception", e)
|
||||||
ret = Result.failure(workDataOf("Error" to "Error IOException"))
|
ret = Result.failure(workDataOf("Error" to "Error IOException"))
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveCsv(file: File, userEntries: List<UserEntry>) {
|
||||||
|
try {
|
||||||
|
val contents = userEntryPresentationHelper.userEntriesToCsv(userEntries)
|
||||||
|
storage.putFileContents(file, contents)
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
throw PrefFileNotFoundError(file.absolutePath)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
throw PrefIOError(file.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,8 +4,6 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.SwitchPreference
|
|
||||||
import dagger.android.HasAndroidInjector
|
import dagger.android.HasAndroidInjector
|
||||||
import info.nightscout.androidaps.BuildConfig
|
import info.nightscout.androidaps.BuildConfig
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
|
@ -212,14 +210,4 @@ class MaintenancePlugin @Inject constructor(
|
||||||
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
return emailIntent
|
return emailIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) {
|
|
||||||
super.preprocessPreferences(preferenceFragment)
|
|
||||||
val encryptSwitch =
|
|
||||||
preferenceFragment.findPreference(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs)) as SwitchPreference?
|
|
||||||
?: return
|
|
||||||
encryptSwitch.isVisible = buildHelper.isEngineeringMode()
|
|
||||||
encryptSwitch.isEnabled = buildHelper.isEngineeringMode()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -827,8 +827,6 @@
|
||||||
<string name="error_adding_treatment_title">Treatment data incomplete</string>
|
<string name="error_adding_treatment_title">Treatment data incomplete</string>
|
||||||
<string name="maintenance_settings">Maintenance Settings</string>
|
<string name="maintenance_settings">Maintenance Settings</string>
|
||||||
<string name="maintenance_email">Email recipient</string>
|
<string name="maintenance_email">Email recipient</string>
|
||||||
<string name="key_maintenance_encrypt_exported_prefs" translatable="false">maintenance_encrypt_exported_prefs</string>
|
|
||||||
<string name="maintenance_encrypt_exported_prefs">Encrypt exported settings</string>
|
|
||||||
<string name="key_maintenance_logs_email" translatable="false">maintenance_logs_email</string>
|
<string name="key_maintenance_logs_email" translatable="false">maintenance_logs_email</string>
|
||||||
<string name="key_maintenance_logs_amount" translatable="false">maintenance_logs_amount</string>
|
<string name="key_maintenance_logs_amount" translatable="false">maintenance_logs_amount</string>
|
||||||
<string name="key_logshipper_amount" translatable="false">logshipper_amount</string>
|
<string name="key_logshipper_amount" translatable="false">logshipper_amount</string>
|
||||||
|
|
|
@ -26,11 +26,6 @@
|
||||||
validate:minNumber="1"
|
validate:minNumber="1"
|
||||||
validate:testType="numericRange"/>
|
validate:testType="numericRange"/>
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:key="@string/key_maintenance_encrypt_exported_prefs"
|
|
||||||
android:title="@string/maintenance_encrypt_exported_prefs" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
|
@ -24,7 +24,6 @@ import kotlin.math.abs
|
||||||
class PrefFileListProvider @Inject constructor(
|
class PrefFileListProvider @Inject constructor(
|
||||||
private val resourceHelper: ResourceHelper,
|
private val resourceHelper: ResourceHelper,
|
||||||
private val config: Config,
|
private val config: Config,
|
||||||
private val classicPrefsFormat: ClassicPrefsFormat,
|
|
||||||
private val encryptedPrefsFormat: EncryptedPrefsFormat,
|
private val encryptedPrefsFormat: EncryptedPrefsFormat,
|
||||||
private val storage: Storage,
|
private val storage: Storage,
|
||||||
private val versionCheckerUtils: VersionCheckerUtils
|
private val versionCheckerUtils: VersionCheckerUtils
|
||||||
|
@ -55,10 +54,8 @@ class PrefFileListProvider @Inject constructor(
|
||||||
path.walk().maxDepth(1).filter { it.isFile && (it.name.endsWith(".json") || it.name.contains("Preferences")) }.forEach {
|
path.walk().maxDepth(1).filter { it.isFile && (it.name.endsWith(".json") || it.name.contains("Preferences")) }.forEach {
|
||||||
val contents = storage.getFileContents(it)
|
val contents = storage.getFileContents(it)
|
||||||
val detectedNew = encryptedPrefsFormat.isPreferencesFile(it, contents)
|
val detectedNew = encryptedPrefsFormat.isPreferencesFile(it, contents)
|
||||||
val detectedOld = !detectedNew && classicPrefsFormat.isPreferencesFile(it, contents)
|
if (detectedNew) {
|
||||||
if (detectedNew || detectedOld) {
|
prefFiles.add(PrefsFile(it.name, it, path, PrefsImportDir.ROOT_DIR, metadataFor(loadMetadata, contents)))
|
||||||
val formatHandler = if (detectedNew) PrefsFormatsHandler.ENCRYPTED else PrefsFormatsHandler.CLASSIC
|
|
||||||
prefFiles.add(PrefsFile(it.name, it, path, PrefsImportDir.ROOT_DIR, formatHandler, metadataFor(loadMetadata, formatHandler, contents)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,15 +63,14 @@ class PrefFileListProvider @Inject constructor(
|
||||||
aapsPath.walk().filter { it.isFile && it.name.endsWith(".json") }.forEach {
|
aapsPath.walk().filter { it.isFile && it.name.endsWith(".json") }.forEach {
|
||||||
val contents = storage.getFileContents(it)
|
val contents = storage.getFileContents(it)
|
||||||
if (encryptedPrefsFormat.isPreferencesFile(it, contents)) {
|
if (encryptedPrefsFormat.isPreferencesFile(it, contents)) {
|
||||||
prefFiles.add(PrefsFile(it.name, it, aapsPath, PrefsImportDir.AAPS_DIR, PrefsFormatsHandler.ENCRYPTED, metadataFor(loadMetadata, PrefsFormatsHandler.ENCRYPTED, contents)))
|
prefFiles.add(PrefsFile(it.name, it, aapsPath, PrefsImportDir.AAPS_DIR, metadataFor(loadMetadata, contents)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we sort only if we have metadata to be used for that
|
// we sort only if we have metadata to be used for that
|
||||||
if (loadMetadata) {
|
if (loadMetadata) {
|
||||||
prefFiles.sortWith(
|
prefFiles.sortWith(
|
||||||
compareByDescending<PrefsFile> { it.handler }
|
compareByDescending<PrefsFile> { it.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.status }
|
||||||
.thenBy { it.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.status }
|
|
||||||
.thenByDescending { it.metadata[PrefsMetadataKey.CREATED_AT]?.value }
|
.thenByDescending { it.metadata[PrefsMetadataKey.CREATED_AT]?.value }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -82,14 +78,11 @@ class PrefFileListProvider @Inject constructor(
|
||||||
return prefFiles
|
return prefFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun metadataFor(loadMetadata: Boolean, formatHandler: PrefsFormatsHandler, contents: String): PrefMetadataMap {
|
private fun metadataFor(loadMetadata: Boolean, contents: String): PrefMetadataMap {
|
||||||
if (!loadMetadata) {
|
if (!loadMetadata) {
|
||||||
return mapOf()
|
return mapOf()
|
||||||
}
|
}
|
||||||
return checkMetadata(when (formatHandler) {
|
return checkMetadata(encryptedPrefsFormat.loadMetadata(contents))
|
||||||
PrefsFormatsHandler.CLASSIC -> classicPrefsFormat.loadMetadata(contents)
|
|
||||||
PrefsFormatsHandler.ENCRYPTED -> encryptedPrefsFormat.loadMetadata(contents)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun legacyFile(): File {
|
fun legacyFile(): File {
|
||||||
|
|
|
@ -3,8 +3,8 @@ package info.nightscout.androidaps.plugins.general.maintenance
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefMetadata
|
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefMetadata
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsMetadataKey
|
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsMetadataKey
|
||||||
import kotlinx.parcelize.RawValue
|
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import kotlinx.parcelize.RawValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -13,7 +13,6 @@ data class PrefsFile(
|
||||||
val file: File,
|
val file: File,
|
||||||
val baseDir: File,
|
val baseDir: File,
|
||||||
val dirKind: PrefsImportDir,
|
val dirKind: PrefsImportDir,
|
||||||
val handler: PrefsFormatsHandler,
|
|
||||||
|
|
||||||
// metadata here is used only for list display
|
// metadata here is used only for list display
|
||||||
val metadata: @RawValue Map<PrefsMetadataKey, PrefMetadata>
|
val metadata: @RawValue Map<PrefsMetadataKey, PrefMetadata>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.general.maintenance
|
|
||||||
|
|
||||||
enum class PrefsFormatsHandler {
|
|
||||||
CLASSIC,
|
|
||||||
ENCRYPTED
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
@ -16,10 +17,8 @@ import info.nightscout.androidaps.core.databinding.MaintenanceImportListItemBind
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFile
|
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFile
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
|
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFormatsHandler
|
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsMetadataKey
|
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsMetadataKey
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsStatus
|
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsStatus
|
||||||
import info.nightscout.androidaps.extensions.toVisibility
|
|
||||||
import info.nightscout.androidaps.utils.locale.LocaleHelper
|
import info.nightscout.androidaps.utils.locale.LocaleHelper
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -83,38 +82,30 @@ class PrefImportListActivity : DaggerAppCompatActivity() {
|
||||||
|
|
||||||
filelistDir.text = resourceHelper.gs(R.string.in_directory, prefFile.file.parentFile?.absolutePath)
|
filelistDir.text = resourceHelper.gs(R.string.in_directory, prefFile.file.parentFile?.absolutePath)
|
||||||
|
|
||||||
val visible = (prefFile.handler != PrefsFormatsHandler.CLASSIC).toVisibility()
|
metalineName.visibility = View.VISIBLE
|
||||||
metalineName.visibility = visible
|
metaDateTimeIcon.visibility = View.VISIBLE
|
||||||
metaDateTimeIcon.visibility = visible
|
metaAppVersion.visibility = View.VISIBLE
|
||||||
metaAppVersion.visibility = visible
|
|
||||||
|
|
||||||
if (prefFile.handler == PrefsFormatsHandler.CLASSIC) {
|
|
||||||
metaVariantFormat.text = resourceHelper.gs(R.string.metadata_format_old)
|
|
||||||
metaVariantFormat.setTextColor(resourceHelper.gc(R.color.metadataTextWarning))
|
|
||||||
metaDateTime.text = " "
|
|
||||||
} else {
|
|
||||||
|
|
||||||
prefFile.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.let {
|
|
||||||
metaVariantFormat.text = it.value
|
|
||||||
val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning
|
|
||||||
metaVariantFormat.setTextColor(resourceHelper.gc(color))
|
|
||||||
}
|
|
||||||
|
|
||||||
prefFile.metadata[PrefsMetadataKey.CREATED_AT]?.let {
|
|
||||||
metaDateTime.text = prefFileListProvider.formatExportedAgo(it.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
prefFile.metadata[PrefsMetadataKey.AAPS_VERSION]?.let {
|
|
||||||
metaAppVersion.text = it.value
|
|
||||||
val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning
|
|
||||||
metaAppVersion.setTextColor(resourceHelper.gc(color))
|
|
||||||
}
|
|
||||||
|
|
||||||
prefFile.metadata[PrefsMetadataKey.DEVICE_NAME]?.let {
|
|
||||||
metaDeviceName.text = it.value
|
|
||||||
}
|
|
||||||
|
|
||||||
|
prefFile.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.let {
|
||||||
|
metaVariantFormat.text = it.value
|
||||||
|
val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning
|
||||||
|
metaVariantFormat.setTextColor(resourceHelper.gc(color))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefFile.metadata[PrefsMetadataKey.CREATED_AT]?.let {
|
||||||
|
metaDateTime.text = prefFileListProvider.formatExportedAgo(it.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
prefFile.metadata[PrefsMetadataKey.AAPS_VERSION]?.let {
|
||||||
|
metaAppVersion.text = it.value
|
||||||
|
val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning
|
||||||
|
metaAppVersion.setTextColor(resourceHelper.gc(color))
|
||||||
|
}
|
||||||
|
|
||||||
|
prefFile.metadata[PrefsMetadataKey.DEVICE_NAME]?.let {
|
||||||
|
metaDeviceName.text = it.value
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.general.maintenance.formats
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.Constants
|
|
||||||
import info.nightscout.androidaps.core.R
|
|
||||||
import info.nightscout.androidaps.database.entities.UserEntry
|
|
||||||
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
|
||||||
import info.nightscout.androidaps.utils.storage.Storage
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileNotFoundException
|
|
||||||
import java.io.IOException
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class ClassicPrefsFormat @Inject constructor(
|
|
||||||
private var resourceHelper: ResourceHelper,
|
|
||||||
private var userEntryPresentationHelper: UserEntryPresentationHelper,
|
|
||||||
private var storage: Storage
|
|
||||||
) : PrefsFormat {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
val FORMAT_KEY = "aaps_old"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun isPreferencesFile(file: File, preloadedContents: String?): Boolean {
|
|
||||||
val contents = preloadedContents ?: storage.getFileContents(file)
|
|
||||||
return contents.contains("units::" + Constants.MGDL) || contents.contains("units::" + Constants.MMOL) || contents.contains("language::") || contents.contains("I_understand::")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun savePreferences(file: File, prefs: Prefs, masterPassword: String?) {
|
|
||||||
try {
|
|
||||||
val contents = prefs.values.entries.joinToString("\n") { entry ->
|
|
||||||
"${entry.key}::${entry.value}"
|
|
||||||
}
|
|
||||||
storage.putFileContents(file, contents)
|
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
throw PrefFileNotFoundError(file.absolutePath)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw PrefIOError(file.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadPreferences(file: File, masterPassword: String?): Prefs {
|
|
||||||
var lineParts: Array<String>
|
|
||||||
val entries: MutableMap<String, String> = mutableMapOf()
|
|
||||||
try {
|
|
||||||
|
|
||||||
val rawLines = storage.getFileContents(file).split("\n")
|
|
||||||
rawLines.forEach { line ->
|
|
||||||
lineParts = line.split("::").toTypedArray()
|
|
||||||
if (lineParts.size == 2) {
|
|
||||||
entries[lineParts[0]] = lineParts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Prefs(entries, loadMetadata())
|
|
||||||
|
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
throw PrefFileNotFoundError(file.absolutePath)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw PrefIOError(file.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadMetadata(contents: String?): PrefMetadataMap {
|
|
||||||
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
|
|
||||||
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN, resourceHelper.gs(R.string.metadata_warning_outdated_format))
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveCsv(file: File, userEntries: List<UserEntry>) {
|
|
||||||
try {
|
|
||||||
val contents = userEntryPresentationHelper.userEntriesToCsv(userEntries)
|
|
||||||
storage.putFileContents(file, contents)
|
|
||||||
} catch (e: FileNotFoundException) {
|
|
||||||
throw PrefFileNotFoundError(file.absolutePath)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw PrefIOError(file.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,28 +19,25 @@ enum class PrefsMetadataKey(val key: String, @DrawableRes val icon: Int, @String
|
||||||
ENCRYPTION("encryption", R.drawable.ic_meta_encryption, R.string.metadata_label_encryption);
|
ENCRYPTION("encryption", R.drawable.ic_meta_encryption, R.string.metadata_label_encryption);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val keyToEnumMap = HashMap<String, PrefsMetadataKey>()
|
private val keyToEnumMap = HashMap<String, PrefsMetadataKey>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (value in values()) {
|
for (value in values()) keyToEnumMap[value.key] = value
|
||||||
keyToEnumMap.put(value.key, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromKey(key: String): PrefsMetadataKey? {
|
fun fromKey(key: String): PrefsMetadataKey? =
|
||||||
if (keyToEnumMap.containsKey(key)) {
|
if (keyToEnumMap.containsKey(key)) {
|
||||||
return keyToEnumMap.get(key)
|
keyToEnumMap[key]
|
||||||
} else {
|
} else {
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatForDisplay(context: Context, value: String): String {
|
fun formatForDisplay(context: Context, value: String): String {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
FILE_FORMAT -> when (value) {
|
FILE_FORMAT -> when (value) {
|
||||||
ClassicPrefsFormat.FORMAT_KEY -> context.getString(R.string.metadata_format_old)
|
|
||||||
EncryptedPrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new)
|
EncryptedPrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new)
|
||||||
EncryptedPrefsFormat.FORMAT_KEY_NOENC -> context.getString(R.string.metadata_format_debug)
|
EncryptedPrefsFormat.FORMAT_KEY_NOENC -> context.getString(R.string.metadata_format_debug)
|
||||||
else -> context.getString(R.string.metadata_format_other)
|
else -> context.getString(R.string.metadata_format_other)
|
||||||
|
@ -60,6 +57,7 @@ typealias PrefMetadataMap = Map<PrefsMetadataKey, PrefMetadata>
|
||||||
data class Prefs(val values: Map<String, String>, var metadata: PrefMetadataMap)
|
data class Prefs(val values: Map<String, String>, var metadata: PrefMetadataMap)
|
||||||
|
|
||||||
interface PrefsFormat {
|
interface PrefsFormat {
|
||||||
|
|
||||||
fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null)
|
fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null)
|
||||||
fun loadPreferences(file: File, masterPassword: String? = null): Prefs
|
fun loadPreferences(file: File, masterPassword: String? = null): Prefs
|
||||||
fun loadMetadata(contents: String? = null): PrefMetadataMap
|
fun loadMetadata(contents: String? = null): PrefMetadataMap
|
||||||
|
|
|
@ -345,7 +345,6 @@
|
||||||
<string name="metadata_label_device_name">Exporting device patient name</string>
|
<string name="metadata_label_device_name">Exporting device patient name</string>
|
||||||
<string name="metadata_label_device_model">Exporting device model</string>
|
<string name="metadata_label_device_model">Exporting device model</string>
|
||||||
<string name="metadata_label_encryption">File encryption</string>
|
<string name="metadata_label_encryption">File encryption</string>
|
||||||
<string name="metadata_format_old">Old export format</string>
|
|
||||||
<string name="metadata_format_new">New encrypted format</string>
|
<string name="metadata_format_new">New encrypted format</string>
|
||||||
<string name="metadata_format_debug">New debug format (unencrypted)</string>
|
<string name="metadata_format_debug">New debug format (unencrypted)</string>
|
||||||
<string name="metadata_format_other">Unknown export format</string>
|
<string name="metadata_format_other">Unknown export format</string>
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.general.maintenance.formats
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.TestBase
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
|
||||||
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
|
|
||||||
import org.junit.Assert
|
|
||||||
import org.junit.Test
|
|
||||||
import org.mockito.Mock
|
|
||||||
import org.mockito.Mockito.`when`
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class ClassicPrefsFormatTest : TestBase() {
|
|
||||||
|
|
||||||
@Mock lateinit var resourceHelper: ResourceHelper
|
|
||||||
@Mock lateinit var userEntryPresentationHelper: UserEntryPresentationHelper
|
|
||||||
@Mock lateinit var file: MockedFile
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun preferenceLoadingTest() {
|
|
||||||
val test = "key1::val1\nkeyB::valB"
|
|
||||||
|
|
||||||
val classicFormat = ClassicPrefsFormat(resourceHelper, userEntryPresentationHelper, SingleStringStorage(test))
|
|
||||||
val prefs = classicFormat.loadPreferences(getMockedFile(), "")
|
|
||||||
|
|
||||||
Assert.assertEquals(prefs.values.size, 2)
|
|
||||||
Assert.assertEquals(prefs.values["key1"], "val1")
|
|
||||||
Assert.assertEquals(prefs.values["keyB"], "valB")
|
|
||||||
Assert.assertNull(prefs.values["key3"])
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun preferenceSavingTest() {
|
|
||||||
val storage = SingleStringStorage("")
|
|
||||||
val classicFormat = ClassicPrefsFormat(resourceHelper, userEntryPresentationHelper, storage)
|
|
||||||
val prefs = Prefs(
|
|
||||||
mapOf(
|
|
||||||
"key1" to "A",
|
|
||||||
"keyB" to "2"
|
|
||||||
),
|
|
||||||
mapOf()
|
|
||||||
)
|
|
||||||
|
|
||||||
classicFormat.savePreferences(getMockedFile(), prefs)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MockedFile(s: String) : File(s)
|
|
||||||
|
|
||||||
private fun getMockedFile(): File {
|
|
||||||
`when`(file.exists()).thenReturn(true)
|
|
||||||
`when`(file.canRead()).thenReturn(true)
|
|
||||||
`when`(file.canWrite()).thenReturn(true)
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue