simplify :core:interfaces

This commit is contained in:
Milos Kozak 2023-09-18 14:23:00 +02:00
parent 91d19b51dc
commit 46c2abdc5b
25 changed files with 154 additions and 121 deletions

View file

@ -0,0 +1,7 @@
package info.nightscout.interfaces.maintenance
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class PrefMetadata(var value: String, var status: PrefsStatus, var info: String? = null) : Parcelable

View file

@ -0,0 +1,11 @@
package info.nightscout.interfaces.maintenance
import android.content.Context
interface PrefsMetadataKey {
val key: String
val icon: Int
val label: Int
fun formatForDisplay(context: Context, value: String): String
}

View file

@ -0,0 +1,8 @@
package info.nightscout.interfaces.maintenance
import android.os.Parcelable
interface PrefsStatus : Parcelable {
val icon: Int
}

View file

@ -1,17 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- Maintenance -->
<string name="metadata_label_format">File format</string>
<string name="metadata_label_created_at">Created at</string>
<string name="metadata_label_aaps_version">AAPS Version</string>
<string name="metadata_label_aaps_flavour">Build Variant</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_encryption">File encryption</string>
<string name="metadata_format_new">New encrypted format</string>
<string name="metadata_format_debug">New debug format (unencrypted)</string>
<string name="metadata_format_other">Unknown export format</string>
<!-- PumpPluginBase --> <!-- PumpPluginBase -->
<string name="pump_driver_changed" comment="26 characters max for translation">Pump driver changed.</string> <string name="pump_driver_changed" comment="26 characters max for translation">Pump driver changed.</string>

View file

@ -1,6 +1,5 @@
package info.nightscout.implementation.utils package info.nightscout.implementation.utils
import info.nightscout.interfaces.R
import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import java.text.DecimalFormat import java.text.DecimalFormat

View file

@ -3,6 +3,7 @@ plugins {
id 'kotlin-android' id 'kotlin-android'
id 'kotlin-kapt' id 'kotlin-kapt'
id 'kotlin-allopen' id 'kotlin-allopen'
id 'kotlin-parcelize'
} }
apply from: "${project.rootDir}/core/main/android_dependencies.gradle" apply from: "${project.rootDir}/core/main/android_dependencies.gradle"

View file

@ -44,7 +44,7 @@ import info.nightscout.interfaces.maintenance.Prefs
import info.nightscout.interfaces.maintenance.PrefsFile import info.nightscout.interfaces.maintenance.PrefsFile
import info.nightscout.interfaces.maintenance.PrefsFormat import info.nightscout.interfaces.maintenance.PrefsFormat
import info.nightscout.interfaces.maintenance.PrefsMetadataKey import info.nightscout.interfaces.maintenance.PrefsMetadataKey
import info.nightscout.interfaces.maintenance.PrefsStatus import info.nightscout.interfaces.maintenance.PrefsStatusImpl
import info.nightscout.interfaces.protection.PasswordCheck import info.nightscout.interfaces.protection.PasswordCheck
import info.nightscout.interfaces.storage.Storage import info.nightscout.interfaces.storage.Storage
import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.interfaces.ui.UiInteraction
@ -114,12 +114,12 @@ class ImportExportPrefsImpl @Inject constructor(
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf() val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
metadata[PrefsMetadataKey.DEVICE_NAME] = PrefMetadata(detectUserName(context), PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.DEVICE_NAME] = PrefMetadata(detectUserName(context), PrefsStatusImpl.OK)
metadata[PrefsMetadataKey.CREATED_AT] = PrefMetadata(dateUtil.toISOString(dateUtil.now()), PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.CREATED_AT] = PrefMetadata(dateUtil.toISOString(dateUtil.now()), PrefsStatusImpl.OK)
metadata[PrefsMetadataKey.AAPS_VERSION] = PrefMetadata(config.VERSION_NAME, PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.AAPS_VERSION] = PrefMetadata(config.VERSION_NAME, PrefsStatusImpl.OK)
metadata[PrefsMetadataKey.AAPS_FLAVOUR] = PrefMetadata(config.FLAVOR, PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.AAPS_FLAVOUR] = PrefMetadata(config.FLAVOR, PrefsStatusImpl.OK)
metadata[PrefsMetadataKey.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.DEVICE_MODEL] = PrefMetadata(config.currentDeviceModelString, PrefsStatusImpl.OK)
metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata("Enabled", PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.ENCRYPTION] = PrefMetadata("Enabled", PrefsStatusImpl.OK)
return metadata return metadata
} }
@ -219,7 +219,7 @@ class ImportExportPrefsImpl @Inject constructor(
) { ) {
// current master password was not the one used for decryption, so we prompt for old password... // current master password was not the one used for decryption, so we prompt for old password...
if (!importOk && (prefs.metadata[PrefsMetadataKey.ENCRYPTION]?.status == PrefsStatus.ERROR)) { if (!importOk && (prefs.metadata[PrefsMetadataKeyImpl.ENCRYPTION]?.status == PrefsStatusImpl.ERROR)) {
askForEncryptionPass( askForEncryptionPass(
activity, R.string.preferences_import_canceled, R.string.old_master_password, activity, R.string.preferences_import_canceled, R.string.old_master_password,
R.string.different_password_used, R.string.master_password_will_be_replaced R.string.different_password_used, R.string.master_password_will_be_replaced
@ -303,6 +303,7 @@ class ImportExportPrefsImpl @Inject constructor(
override fun importCustomWatchface(fragment: Fragment) { override fun importCustomWatchface(fragment: Fragment) {
fragment.activity?.let { importCustomWatchface(it) } fragment.activity?.let { importCustomWatchface(it) }
} }
override fun importCustomWatchface(activity: FragmentActivity) { override fun importCustomWatchface(activity: FragmentActivity) {
try { try {
if (activity is DaggerAppCompatActivityWithResult) if (activity is DaggerAppCompatActivityWithResult)
@ -374,7 +375,7 @@ class ImportExportPrefsImpl @Inject constructor(
var importOk = true var importOk = true
for ((_, value) in prefs.metadata) { for ((_, value) in prefs.metadata) {
if (value.status == PrefsStatus.ERROR) if (value.status == PrefsStatusImpl.ERROR)
importOk = false importOk = false
} }
return importOk return importOk

View file

@ -14,7 +14,7 @@ import info.nightscout.interfaces.maintenance.PrefMetadataMap
import info.nightscout.interfaces.maintenance.PrefsFile import info.nightscout.interfaces.maintenance.PrefsFile
import info.nightscout.interfaces.maintenance.PrefsImportDir import info.nightscout.interfaces.maintenance.PrefsImportDir
import info.nightscout.interfaces.maintenance.PrefsMetadataKey import info.nightscout.interfaces.maintenance.PrefsMetadataKey
import info.nightscout.interfaces.maintenance.PrefsStatus import info.nightscout.interfaces.maintenance.PrefsStatusImpl
import info.nightscout.interfaces.storage.Storage import info.nightscout.interfaces.storage.Storage
import info.nightscout.interfaces.versionChecker.VersionCheckerUtils import info.nightscout.interfaces.versionChecker.VersionCheckerUtils
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
@ -89,8 +89,8 @@ class PrefFileListProviderImpl @Inject constructor(
// 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.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.status } compareByDescending<PrefsFile> { it.metadata[PrefsMetadataKeyImpl.AAPS_FLAVOUR]?.status as PrefsStatusImpl }
.thenByDescending { it.metadata[PrefsMetadataKey.CREATED_AT]?.value } .thenByDescending { it.metadata[PrefsMetadataKeyImpl.CREATED_AT]?.value }
) )
} }
@ -176,22 +176,22 @@ class PrefFileListProviderImpl @Inject constructor(
override fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> { override fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> {
val meta = metadata.toMutableMap() val meta = metadata.toMutableMap()
meta[PrefsMetadataKey.AAPS_FLAVOUR]?.let { flavour -> meta[PrefsMetadataKeyImpl.AAPS_FLAVOUR]?.let { flavour ->
val flavourOfPrefs = flavour.value val flavourOfPrefs = flavour.value
if (flavour.value != config.get().FLAVOR) { if (flavour.value != config.get().FLAVOR) {
flavour.status = PrefsStatus.WARN flavour.status = PrefsStatusImpl.WARN
flavour.info = rh.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, config.get().FLAVOR) flavour.info = rh.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, config.get().FLAVOR)
} }
} }
meta[PrefsMetadataKey.DEVICE_MODEL]?.let { model -> meta[PrefsMetadataKeyImpl.DEVICE_MODEL]?.let { model ->
if (model.value != config.get().currentDeviceModelString) { if (model.value != config.get().currentDeviceModelString) {
model.status = PrefsStatus.WARN model.status = PrefsStatusImpl.WARN
model.info = rh.gs(R.string.metadata_warning_different_device) model.info = rh.gs(R.string.metadata_warning_different_device)
} }
} }
meta[PrefsMetadataKey.CREATED_AT]?.let { createdAt -> meta[PrefsMetadataKeyImpl.CREATED_AT]?.let { createdAt ->
try { try {
val date1 = DateTime.parse(createdAt.value) val date1 = DateTime.parse(createdAt.value)
val date2 = DateTime.now() val date2 = DateTime.now()
@ -199,26 +199,26 @@ class PrefFileListProviderImpl @Inject constructor(
val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).days val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).days
if (daysOld > IMPORT_AGE_NOT_YET_OLD_DAYS) { if (daysOld > IMPORT_AGE_NOT_YET_OLD_DAYS) {
createdAt.status = PrefsStatus.WARN createdAt.status = PrefsStatusImpl.WARN
createdAt.info = rh.gs(R.string.metadata_warning_old_export, daysOld.toString()) createdAt.info = rh.gs(R.string.metadata_warning_old_export, daysOld.toString())
} }
} catch (e: Exception) { } catch (e: Exception) {
createdAt.status = PrefsStatus.WARN createdAt.status = PrefsStatusImpl.WARN
createdAt.info = rh.gs(R.string.metadata_warning_date_format) createdAt.info = rh.gs(R.string.metadata_warning_date_format)
} }
} }
meta[PrefsMetadataKey.AAPS_VERSION]?.let { version -> meta[PrefsMetadataKeyImpl.AAPS_VERSION]?.let { version ->
val currentAppVer = versionCheckerUtils.versionDigits(config.get().VERSION_NAME) val currentAppVer = versionCheckerUtils.versionDigits(config.get().VERSION_NAME)
val metadataVer = versionCheckerUtils.versionDigits(version.value) val metadataVer = versionCheckerUtils.versionDigits(version.value)
if ((currentAppVer.size >= 2) && (metadataVer.size >= 2) && (abs(currentAppVer[1] - metadataVer[1]) > 1)) { if ((currentAppVer.size >= 2) && (metadataVer.size >= 2) && (abs(currentAppVer[1] - metadataVer[1]) > 1)) {
version.status = PrefsStatus.WARN version.status = PrefsStatusImpl.WARN
version.info = rh.gs(R.string.metadata_warning_different_version) version.info = rh.gs(R.string.metadata_warning_different_version)
} }
if ((currentAppVer.isNotEmpty()) && (metadataVer.isNotEmpty()) && (currentAppVer[0] != metadataVer[0])) { if ((currentAppVer.isNotEmpty()) && (metadataVer.isNotEmpty()) && (currentAppVer[0] != metadataVer[0])) {
version.status = PrefsStatus.WARN version.status = PrefsStatusImpl.WARN
version.info = rh.gs(R.string.metadata_urgent_different_version) version.info = rh.gs(R.string.metadata_urgent_different_version)
} }
} }

View file

@ -1,14 +1,13 @@
package info.nightscout.interfaces.maintenance package info.nightscout.configuration.maintenance
import android.content.Context import android.content.Context
import android.os.Parcelable
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import info.nightscout.interfaces.R import info.nightscout.configuration.R
import kotlinx.parcelize.Parcelize import info.nightscout.interfaces.maintenance.PrefsFormat
import java.io.File import info.nightscout.interfaces.maintenance.PrefsMetadataKey
enum class PrefsMetadataKey(val key: String, @DrawableRes val icon: Int, @StringRes val label: Int) { enum class PrefsMetadataKeyImpl(override val key: String, @DrawableRes override val icon: Int, @StringRes override val label: Int) : PrefsMetadataKey {
FILE_FORMAT("format", R.drawable.ic_meta_format, R.string.metadata_label_format), FILE_FORMAT("format", R.drawable.ic_meta_format, R.string.metadata_label_format),
CREATED_AT("created_at", R.drawable.ic_meta_date, R.string.metadata_label_created_at), CREATED_AT("created_at", R.drawable.ic_meta_date, R.string.metadata_label_created_at),
@ -35,48 +34,16 @@ enum class PrefsMetadataKey(val key: String, @DrawableRes val icon: Int, @String
} }
fun formatForDisplay(context: Context, value: String): String { override fun formatForDisplay(context: Context, value: String): String {
return when (this) { return when (this) {
FILE_FORMAT -> when (value) { FILE_FORMAT -> when (value) {
PrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new) PrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new)
PrefsFormat.FORMAT_KEY_NOENC -> context.getString(R.string.metadata_format_debug) PrefsFormat.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)
} }
CREATED_AT -> value.replace("T", " ").replace("Z", " (UTC)") CREATED_AT -> value.replace("T", " ").replace("Z", " (UTC)")
else -> value else -> value
} }
} }
} }
@Parcelize
data class PrefMetadata(var value: String, var status: PrefsStatus, var info: String? = null) : Parcelable
typealias PrefMetadataMap = Map<PrefsMetadataKey, PrefMetadata>
data class Prefs(val values: Map<String, String>, var metadata: PrefMetadataMap)
interface PrefsFormat {
companion object {
const val FORMAT_KEY_ENC = "aaps_encrypted"
const val FORMAT_KEY_NOENC = "aaps_structured"
}
fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null)
fun loadPreferences(file: File, masterPassword: String? = null): Prefs
fun loadMetadata(contents: String? = null): PrefMetadataMap
fun isPreferencesFile(file: File, preloadedContents: String? = null): Boolean
}
enum class PrefsStatus(@DrawableRes val icon: Int) {
OK(R.drawable.ic_meta_ok),
WARN(R.drawable.ic_meta_warning),
ERROR(R.drawable.ic_meta_error),
UNKNOWN(R.drawable.ic_meta_error),
DISABLED(R.drawable.ic_meta_error)
}
class PrefFileNotFoundError(message: String) : Exception(message)
class PrefIOError(message: String) : Exception(message)
class PrefFormatError(message: String) : Exception(message)

View file

@ -12,11 +12,11 @@ import info.nightscout.configuration.R
import info.nightscout.configuration.databinding.MaintenanceImportListActivityBinding import info.nightscout.configuration.databinding.MaintenanceImportListActivityBinding
import info.nightscout.configuration.databinding.MaintenanceImportListItemBinding import info.nightscout.configuration.databinding.MaintenanceImportListItemBinding
import info.nightscout.configuration.maintenance.PrefsFileContract import info.nightscout.configuration.maintenance.PrefsFileContract
import info.nightscout.configuration.maintenance.PrefsMetadataKeyImpl
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
import info.nightscout.interfaces.maintenance.PrefFileListProvider import info.nightscout.interfaces.maintenance.PrefFileListProvider
import info.nightscout.interfaces.maintenance.PrefsFile import info.nightscout.interfaces.maintenance.PrefsFile
import info.nightscout.interfaces.maintenance.PrefsMetadataKey import info.nightscout.interfaces.maintenance.PrefsStatusImpl
import info.nightscout.interfaces.maintenance.PrefsStatus
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
@ -82,23 +82,23 @@ class PrefImportListActivity : TranslatedDaggerAppCompatActivity() {
metaDateTimeIcon.visibility = View.VISIBLE metaDateTimeIcon.visibility = View.VISIBLE
metaAppVersion.visibility = View.VISIBLE metaAppVersion.visibility = View.VISIBLE
prefFile.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.let { prefFile.metadata[PrefsMetadataKeyImpl.AAPS_FLAVOUR]?.let {
metaVariantFormat.text = it.value metaVariantFormat.text = it.value
val colorAttr = if (it.status == PrefsStatus.OK) info.nightscout.core.ui.R.attr.metadataTextOkColor else info.nightscout.core.ui.R.attr.metadataTextWarningColor val colorAttr = if (it.status == PrefsStatusImpl.OK) info.nightscout.core.ui.R.attr.metadataTextOkColor else info.nightscout.core.ui.R.attr.metadataTextWarningColor
metaVariantFormat.setTextColor(rh.gac(metaVariantFormat.context, colorAttr)) metaVariantFormat.setTextColor(rh.gac(metaVariantFormat.context, colorAttr))
} }
prefFile.metadata[PrefsMetadataKey.CREATED_AT]?.let { prefFile.metadata[PrefsMetadataKeyImpl.CREATED_AT]?.let {
metaDateTime.text = prefFileListProvider.formatExportedAgo(it.value) metaDateTime.text = prefFileListProvider.formatExportedAgo(it.value)
} }
prefFile.metadata[PrefsMetadataKey.AAPS_VERSION]?.let { prefFile.metadata[PrefsMetadataKeyImpl.AAPS_VERSION]?.let {
metaAppVersion.text = it.value metaAppVersion.text = it.value
val colorAttr = if (it.status == PrefsStatus.OK) info.nightscout.core.ui.R.attr.metadataTextOkColor else info.nightscout.core.ui.R.attr.metadataTextWarningColor val colorAttr = if (it.status == PrefsStatusImpl.OK) info.nightscout.core.ui.R.attr.metadataTextOkColor else info.nightscout.core.ui.R.attr.metadataTextWarningColor
metaAppVersion.setTextColor(rh.gac(metaVariantFormat.context, colorAttr)) metaAppVersion.setTextColor(rh.gac(metaVariantFormat.context, colorAttr))
} }
prefFile.metadata[PrefsMetadataKey.DEVICE_NAME]?.let { prefFile.metadata[PrefsMetadataKeyImpl.DEVICE_NAME]?.let {
metaDeviceName.text = it.value metaDeviceName.text = it.value
} }

View file

@ -20,7 +20,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import info.nightscout.configuration.R import info.nightscout.configuration.R
import info.nightscout.core.ui.toast.ToastUtils import info.nightscout.core.ui.toast.ToastUtils
import info.nightscout.interfaces.maintenance.Prefs import info.nightscout.interfaces.maintenance.Prefs
import info.nightscout.interfaces.maintenance.PrefsStatus import info.nightscout.interfaces.maintenance.PrefsStatusImpl
import info.nightscout.shared.extensions.runOnUiThread import info.nightscout.shared.extensions.runOnUiThread
import java.util.LinkedList import java.util.LinkedList
@ -57,8 +57,8 @@ object PrefImportSummaryDialog {
(rowLayout.findViewById<View>(R.id.summary_icon) as ImageView).setImageResource(metaKey.icon) (rowLayout.findViewById<View>(R.id.summary_icon) as ImageView).setImageResource(metaKey.icon)
(rowLayout.findViewById<View>(R.id.status_icon) as ImageView).setImageResource(metaEntry.status.icon) (rowLayout.findViewById<View>(R.id.status_icon) as ImageView).setImageResource(metaEntry.status.icon)
if (metaEntry.status == PrefsStatus.WARN) label.setTextColor(themedCtx.getColor(info.nightscout.interfaces.R.color.metadataTextWarning)) if (metaEntry.status == PrefsStatusImpl.WARN) label.setTextColor(themedCtx.getColor(info.nightscout.interfaces.R.color.metadataTextWarning))
else if (metaEntry.status == PrefsStatus.ERROR) label.setTextColor(themedCtx.getColor(info.nightscout.interfaces.R.color.metadataTextError)) else if (metaEntry.status == PrefsStatusImpl.ERROR) label.setTextColor(themedCtx.getColor(info.nightscout.interfaces.R.color.metadataTextError))
if (metaEntry.info != null) { if (metaEntry.info != null) {
details.add("<b>${context.getString(metaKey.label)}</b>: ${metaEntry.value}<br/><i style=\"color:silver\">${metaEntry.info}</i>") details.add("<b>${context.getString(metaKey.label)}</b>: ${metaEntry.value}<br/><i style=\"color:silver\">${metaEntry.info}</i>")
@ -66,8 +66,8 @@ object PrefImportSummaryDialog {
rowLayout.setOnClickListener { rowLayout.setOnClickListener {
val msg = "[${context.getString(metaKey.label)}] ${metaEntry.info}" val msg = "[${context.getString(metaKey.label)}] ${metaEntry.info}"
when (metaEntry.status) { when (metaEntry.status) {
PrefsStatus.WARN -> ToastUtils.Long.warnToast(context, msg) PrefsStatusImpl.WARN -> ToastUtils.Long.warnToast(context, msg)
PrefsStatus.ERROR -> ToastUtils.Long.errorToast(context, msg) PrefsStatusImpl.ERROR -> ToastUtils.Long.errorToast(context, msg)
else -> ToastUtils.Long.infoToast(context, msg) else -> ToastUtils.Long.infoToast(context, msg)
} }
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.configuration.maintenance.formats package info.nightscout.configuration.maintenance.formats
import info.nightscout.configuration.R import info.nightscout.configuration.R
import info.nightscout.configuration.maintenance.PrefsMetadataKeyImpl
import info.nightscout.core.utils.CryptoUtil import info.nightscout.core.utils.CryptoUtil
import info.nightscout.core.utils.hexStringToByteArray import info.nightscout.core.utils.hexStringToByteArray
import info.nightscout.core.utils.toHex import info.nightscout.core.utils.toHex
@ -13,6 +14,7 @@ import info.nightscout.interfaces.maintenance.Prefs
import info.nightscout.interfaces.maintenance.PrefsFormat import info.nightscout.interfaces.maintenance.PrefsFormat
import info.nightscout.interfaces.maintenance.PrefsMetadataKey import info.nightscout.interfaces.maintenance.PrefsMetadataKey
import info.nightscout.interfaces.maintenance.PrefsStatus import info.nightscout.interfaces.maintenance.PrefsStatus
import info.nightscout.interfaces.maintenance.PrefsStatusImpl
import info.nightscout.interfaces.storage.Storage import info.nightscout.interfaces.storage.Storage
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import org.json.JSONException import org.json.JSONException
@ -59,8 +61,8 @@ class EncryptedPrefsFormat @Inject constructor(
val content = JSONObject() val content = JSONObject()
val meta = JSONObject() val meta = JSONObject()
val encStatus = prefs.metadata[PrefsMetadataKey.ENCRYPTION]?.status ?: PrefsStatus.OK val encStatus = prefs.metadata[PrefsMetadataKeyImpl.ENCRYPTION]?.status ?: PrefsStatusImpl.OK
var encrypted = encStatus == PrefsStatus.OK && masterPassword != null var encrypted = encStatus == PrefsStatusImpl.OK && masterPassword != null
try { try {
for ((key, value) in prefs.values.toSortedMap()) { for ((key, value) in prefs.values.toSortedMap()) {
@ -68,14 +70,14 @@ class EncryptedPrefsFormat @Inject constructor(
} }
for ((metaKey, metaEntry) in prefs.metadata) { for ((metaKey, metaEntry) in prefs.metadata) {
if (metaKey == PrefsMetadataKey.FILE_FORMAT) if (metaKey == PrefsMetadataKeyImpl.FILE_FORMAT)
continue continue
if (metaKey == PrefsMetadataKey.ENCRYPTION) if (metaKey == PrefsMetadataKeyImpl.ENCRYPTION)
continue continue
meta.put(metaKey.key, metaEntry.value) meta.put(metaKey.key, metaEntry.value)
} }
container.put(PrefsMetadataKey.FILE_FORMAT.key, if (encrypted) PrefsFormat.FORMAT_KEY_ENC else PrefsFormat.FORMAT_KEY_NOENC) container.put(PrefsMetadataKeyImpl.FILE_FORMAT.key, if (encrypted) PrefsFormat.FORMAT_KEY_ENC else PrefsFormat.FORMAT_KEY_NOENC)
container.put("metadata", meta) container.put("metadata", meta)
val security = JSONObject() val security = JSONObject()
@ -130,22 +132,22 @@ class EncryptedPrefsFormat @Inject constructor(
val container = JSONObject(jsonBody) val container = JSONObject(jsonBody)
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = loadMetadata(container) val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = loadMetadata(container)
if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content")) { if (container.has(PrefsMetadataKeyImpl.FILE_FORMAT.key) && container.has("security") && container.has("content")) {
val fileFormat = container.getString(PrefsMetadataKey.FILE_FORMAT.key) val fileFormat = container.getString(PrefsMetadataKeyImpl.FILE_FORMAT.key)
val security = container.getJSONObject("security") val security = container.getJSONObject("security")
val encrypted = fileFormat == PrefsFormat.FORMAT_KEY_ENC val encrypted = fileFormat == PrefsFormat.FORMAT_KEY_ENC
var secure: PrefsStatus = PrefsStatus.OK var secure: PrefsStatus = PrefsStatusImpl.OK
var decryptedOk = false var decryptedOk = false
var contentJsonObj: JSONObject? = null var contentJsonObj: JSONObject? = null
var insecurityReason = rh.gs(R.string.prefdecrypt_settings_tampered) var insecurityReason = rh.gs(R.string.prefdecrypt_settings_tampered)
if (security.has("file_hash")) { if (security.has("file_hash")) {
if (calculatedFileHash != security.getString("file_hash")) { if (calculatedFileHash != security.getString("file_hash")) {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_modified)) issues.add(rh.gs(R.string.prefdecrypt_issue_modified))
} }
} else { } else {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_missing_file_hash)) issues.add(rh.gs(R.string.prefdecrypt_issue_missing_file_hash))
} }
@ -164,38 +166,38 @@ class EncryptedPrefsFormat @Inject constructor(
contentJsonObj = JSONObject(decrypted) contentJsonObj = JSONObject(decrypted)
decryptedOk = true decryptedOk = true
} else { } else {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_modified)) issues.add(rh.gs(R.string.prefdecrypt_issue_modified))
} }
} catch (e: JSONException) { } catch (e: JSONException) {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_parsing)) issues.add(rh.gs(R.string.prefdecrypt_issue_parsing))
} }
} else { } else {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_pass)) issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_pass))
insecurityReason = rh.gs(R.string.prefdecrypt_wrong_password) insecurityReason = rh.gs(R.string.prefdecrypt_wrong_password)
} }
} else { } else {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_format)) issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_format))
} }
} else { } else {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_algorithm)) issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_algorithm))
} }
} else { } else {
if (secure == PrefsStatus.OK) { if (secure == PrefsStatusImpl.OK) {
secure = PrefsStatus.WARN secure = PrefsStatusImpl.WARN
} }
if (!(security.has("algorithm") && security.get("algorithm") == "none")) { if (!(security.has("algorithm") && security.get("algorithm") == "none")) {
secure = PrefsStatus.ERROR secure = PrefsStatusImpl.ERROR
issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_algorithm)) issues.add(rh.gs(R.string.prefdecrypt_issue_wrong_algorithm))
} }
@ -211,12 +213,12 @@ class EncryptedPrefsFormat @Inject constructor(
val issuesStr: String? = if (issues.size > 0) issues.joinToString("\n") else null val issuesStr: String? = if (issues.size > 0) issues.joinToString("\n") else null
val encryptionDescStr = if (encrypted) { val encryptionDescStr = if (encrypted) {
if (secure == PrefsStatus.OK) rh.gs(R.string.prefdecrypt_settings_secure) else insecurityReason if (secure == PrefsStatusImpl.OK) rh.gs(R.string.prefdecrypt_settings_secure) else insecurityReason
} else { } else {
if (secure != PrefsStatus.ERROR) rh.gs(R.string.prefdecrypt_settings_unencrypted) else rh.gs(R.string.prefdecrypt_settings_tampered) if (secure != PrefsStatusImpl.ERROR) rh.gs(R.string.prefdecrypt_settings_unencrypted) else rh.gs(R.string.prefdecrypt_settings_tampered)
} }
metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata(encryptionDescStr, secure, issuesStr) metadata[PrefsMetadataKeyImpl.ENCRYPTION] = PrefMetadata(encryptionDescStr, secure, issuesStr)
} }
return Prefs(entries, metadata) return Prefs(entries, metadata)
@ -243,22 +245,22 @@ class EncryptedPrefsFormat @Inject constructor(
private fun loadMetadata(container: JSONObject): MutableMap<PrefsMetadataKey, PrefMetadata> { private fun loadMetadata(container: JSONObject): MutableMap<PrefsMetadataKey, PrefMetadata> {
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf() val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { if (container.has(PrefsMetadataKeyImpl.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) {
val fileFormat = container.getString(PrefsMetadataKey.FILE_FORMAT.key) val fileFormat = container.getString(PrefsMetadataKeyImpl.FILE_FORMAT.key)
if ((fileFormat != PrefsFormat.FORMAT_KEY_ENC) && (fileFormat != PrefsFormat.FORMAT_KEY_NOENC)) { if ((fileFormat != PrefsFormat.FORMAT_KEY_ENC) && (fileFormat != PrefsFormat.FORMAT_KEY_NOENC)) {
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(rh.gs(info.nightscout.interfaces.R.string.metadata_format_other), PrefsStatus.ERROR) metadata[PrefsMetadataKeyImpl.FILE_FORMAT] = PrefMetadata(rh.gs(R.string.metadata_format_other), PrefsStatusImpl.ERROR)
} else { } else {
val meta = container.getJSONObject("metadata") val meta = container.getJSONObject("metadata")
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(fileFormat, PrefsStatus.OK) metadata[PrefsMetadataKeyImpl.FILE_FORMAT] = PrefMetadata(fileFormat, PrefsStatusImpl.OK)
for (key in meta.keys()) { for (key in meta.keys()) {
val metaKey = PrefsMetadataKey.fromKey(key) val metaKey = PrefsMetadataKeyImpl.fromKey(key)
if (metaKey != null) { if (metaKey != null) {
metadata[metaKey] = PrefMetadata(meta.getString(key), PrefsStatus.OK) metadata[metaKey] = PrefMetadata(meta.getString(key), PrefsStatusImpl.OK)
} }
} }
} }
} else { } else {
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(rh.gs(R.string.prefdecrypt_wrong_json), PrefsStatus.ERROR) metadata[PrefsMetadataKeyImpl.FILE_FORMAT] = PrefMetadata(rh.gs(R.string.prefdecrypt_wrong_json), PrefsStatusImpl.ERROR)
} }
return metadata return metadata

View file

@ -0,0 +1,37 @@
package info.nightscout.interfaces.maintenance
import androidx.annotation.DrawableRes
import info.nightscout.configuration.R
import kotlinx.parcelize.Parcelize
import java.io.File
typealias PrefMetadataMap = Map<PrefsMetadataKey, PrefMetadata>
data class Prefs(val values: Map<String, String>, var metadata: PrefMetadataMap)
interface PrefsFormat {
companion object {
const val FORMAT_KEY_ENC = "aaps_encrypted"
const val FORMAT_KEY_NOENC = "aaps_structured"
}
fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null)
fun loadPreferences(file: File, masterPassword: String? = null): Prefs
fun loadMetadata(contents: String? = null): PrefMetadataMap
fun isPreferencesFile(file: File, preloadedContents: String? = null): Boolean
}
@Parcelize
enum class PrefsStatusImpl(@DrawableRes override val icon: Int) : PrefsStatus {
OK(R.drawable.ic_meta_ok),
WARN(R.drawable.ic_meta_warning),
ERROR(R.drawable.ic_meta_error),
UNKNOWN(R.drawable.ic_meta_error),
DISABLED(R.drawable.ic_meta_error)
}
class PrefFileNotFoundError(message: String) : Exception(message)
class PrefIOError(message: String) : Exception(message)
class PrefFormatError(message: String) : Exception(message)

View file

@ -170,4 +170,16 @@
<!-- Permissions --> <!-- Permissions -->
<string name="alert_dialog_storage_permission_text">Please reboot your phone or restart AAPS from the System Settings \notherwise Android APS will not have logging (important to track and verify that the algorithms are working correctly)!</string> <string name="alert_dialog_storage_permission_text">Please reboot your phone or restart AAPS from the System Settings \notherwise Android APS will not have logging (important to track and verify that the algorithms are working correctly)!</string>
<!-- Maintenance -->
<string name="metadata_label_format">File format</string>
<string name="metadata_label_created_at">Created at</string>
<string name="metadata_label_aaps_version">AAPS Version</string>
<string name="metadata_label_aaps_flavour">Build Variant</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_encryption">File encryption</string>
<string name="metadata_format_new">New encrypted format</string>
<string name="metadata_format_debug">New debug format (unencrypted)</string>
<string name="metadata_format_other">Unknown export format</string>
</resources> </resources>

View file

@ -120,7 +120,7 @@ class TreatmentsUserEntryFragment : DaggerFragment(), MenuProvider {
_binding = null _binding = null
} }
inner class UserEntryAdapter internal constructor(var entries: List<UserEntry>) : RecyclerView.Adapter<UserEntryAdapter.UserEntryViewHolder>() { inner class UserEntryAdapter internal constructor(private var entries: List<UserEntry>) : RecyclerView.Adapter<UserEntryAdapter.UserEntryViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserEntryViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserEntryViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.treatments_user_entry_item, parent, false) val view: View = LayoutInflater.from(parent.context).inflate(R.layout.treatments_user_entry_item, parent, false)