refactor import/export to create DaggerAppCompatActivityWithResult

This commit is contained in:
Milos Kozak 2021-01-18 17:36:18 +01:00
parent 3f18f94c79
commit 65a39d91be
45 changed files with 491 additions and 499 deletions

View file

@ -5,11 +5,13 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class Config @Inject constructor(): ConfigInterface{
class Config @Inject constructor() : ConfigInterface {
override val SUPPORTEDNSVERSION = 1002 // 0.10.00
override val APS = BuildConfig.FLAVOR == "full"
override val NSCLIENT = BuildConfig.FLAVOR == "nsclient" || BuildConfig.FLAVOR == "nsclient2"
override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol"
override val PUMPDRIVERS = BuildConfig.FLAVOR == "full" || BuildConfig.FLAVOR == "pumpcontrol"
override val FLAVOR = BuildConfig.FLAVOR
override val VERSION_NAME = BuildConfig.VERSION_NAME
}

View file

@ -47,8 +47,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
@ -92,18 +90,11 @@ class MainActivity : NoSplashAppCompatActivity() {
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
@Inject lateinit var config: Config
@Inject lateinit var importExportPrefs: ImportExportPrefs
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null
val callForPrefFile = registerForActivityResult(PrefsFileContract()) {
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Iconify.with(FontAwesomeModule())
@ -203,10 +194,9 @@ class MainActivity : NoSplashAppCompatActivity() {
if (p.isEnabled() && p.hasFragment() && !p.isFragmentVisible() && !p.pluginDescription.neverVisible) {
val menuItem = menu.add(p.name)
menuItem.isCheckable = true
if(p.menuIcon != -1) {
if (p.menuIcon != -1) {
menuItem.setIcon(p.menuIcon)
} else
{
} else {
menuItem.setIcon(R.drawable.ic_settings)
}
menuItem.setOnMenuItemClickListener {
@ -283,7 +273,7 @@ class MainActivity : NoSplashAppCompatActivity() {
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
this.menu = menu
this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu)
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)
setPluginPreferenceMenuName()

View file

@ -5,29 +5,20 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import javax.inject.Inject
class SingleFragmentActivity : DaggerAppCompatActivity() {
class SingleFragmentActivity : DaggerAppCompatActivityWithResult() {
@Inject lateinit var pluginStore: PluginStore
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var importExportPrefs: ImportExportPrefs
private var plugin: PluginBase? = null
val callForPrefFile = registerForActivityResult(PrefsFileContract()) {
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_single_fragment)

View file

@ -6,7 +6,6 @@ import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.PrefImportListActivity
import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansLoginActivity
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity
@ -40,7 +39,6 @@ abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
@ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity
@ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity
}

View file

@ -12,6 +12,7 @@ import info.nightscout.androidaps.db.DatabaseHelperProvider
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue
@ -58,5 +59,6 @@ open class AppModule {
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
@Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface
}
}

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
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
@ -13,7 +12,6 @@ import info.nightscout.androidaps.utils.CryptoUtil
abstract class PreferencesModule {
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
@ContributesAndroidInjector abstract fun importExportPrefsInjector(): ImportExportPrefs
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
@ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.text.util.Linkify;
@ -185,9 +184,9 @@ public abstract class Objective {
int days = (int) Math.floor((double) duration / T.days(1).msecs());
int hours = (int) Math.floor((double) duration / T.hours(1).msecs());
int minutes = (int) Math.floor((double) duration / T.mins(1).msecs());
if (days > 0) return resourceHelper.gq(R.plurals.objective_days, days, days);
else if (hours > 0) return resourceHelper.gq(R.plurals.objective_hours, hours, hours);
else return resourceHelper.gq(R.plurals.objective_minutes, minutes, minutes);
if (days > 0) return resourceHelper.gq(R.plurals.days, days, days);
else if (hours > 0) return resourceHelper.gq(R.plurals.hours, hours, hours);
else return resourceHelper.gq(R.plurals.minutes, minutes, minutes);
}
}

View file

@ -12,16 +12,15 @@ import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
import info.nightscout.androidaps.setupwizard.SetupWizardActivity
import info.nightscout.androidaps.utils.AndroidPermission
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.ToastUtils
@ -40,6 +39,7 @@ import java.io.IOException
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.system.exitProcess
/**
* Created by mike on 03.07.2016.
@ -57,19 +57,17 @@ class ImportExportPrefs @Inject constructor(
private val classicPrefsFormat: ClassicPrefsFormat,
private val encryptedPrefsFormat: EncryptedPrefsFormat,
private val prefFileList: PrefFileListProvider
) {
) : ImportExportPrefsInterface {
val TAG = LTag.CORE
fun prefsFileExists(): Boolean {
override fun prefsFileExists(): Boolean {
return prefFileList.listPreferenceFiles().size > 0
}
fun exportSharedPreferences(f: Fragment) {
override fun exportSharedPreferences(f: Fragment) {
f.activity?.let { exportSharedPreferences(it) }
}
fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable) {
override fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable) {
fragment.context?.let { ctx ->
val permission = ContextCompat.checkSelfPermission(ctx,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
@ -119,8 +117,7 @@ class ImportExportPrefs @Inject constructor(
// name we detect from OS
val systemName = n1 ?: n2 ?: n3 ?: n4 ?: n5 ?: n6 ?: defaultPatientName
val name = if (patientName.isNotEmpty() && patientName != defaultPatientName) patientName else systemName
return name
return if (patientName.isNotEmpty() && patientName != defaultPatientName) patientName else systemName
}
private fun prefsEncryptionIsDisabled() =
@ -134,6 +131,7 @@ class ImportExportPrefs @Inject constructor(
})
}
@Suppress("SameParameterValue")
private fun askForEncryptionPass(activity: FragmentActivity, @StringRes canceledMsg: Int, @StringRes passwordName: Int, @StringRes passwordExplanation: Int?,
@StringRes passwordWarning: Int?, then: ((password: String) -> Unit)) {
passwordCheck.queryAnyPassword(activity, passwordName, R.string.key_master_password, passwordExplanation, passwordWarning, { password ->
@ -143,6 +141,7 @@ class ImportExportPrefs @Inject constructor(
})
}
@Suppress("SameParameterValue")
private fun askForMasterPassIfNeeded(activity: FragmentActivity, @StringRes canceledMsg: Int, then: ((password: String) -> Unit)) {
if (prefsEncryptionIsDisabled()) {
then("")
@ -239,49 +238,45 @@ class ImportExportPrefs @Inject constructor(
ToastUtils.okToast(activity, resourceHelper.gs(R.string.exported))
} catch (e: FileNotFoundException) {
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + newFile)
log.error(TAG, "Unhandled exception", e)
log.error(LTag.CORE, "Unhandled exception", e)
} catch (e: IOException) {
ToastUtils.errorToast(activity, e.message)
log.error(TAG, "Unhandled exception", e)
log.error(LTag.CORE, "Unhandled exception", e)
} catch (e: PrefFileNotFoundError) {
ToastUtils.Long.errorToast(activity, resourceHelper.gs(R.string.preferences_export_canceled)
+ "\n\n" + resourceHelper.gs(R.string.filenotfound)
+ ": " + e.message
+ "\n\n" + resourceHelper.gs(R.string.needstoragepermission))
log.error(TAG, "File system exception", e)
log.error(LTag.CORE, "File system exception", e)
} catch (e: PrefIOError) {
ToastUtils.Long.errorToast(activity, resourceHelper.gs(R.string.preferences_export_canceled)
+ "\n\n" + resourceHelper.gs(R.string.needstoragepermission)
+ ": " + e.message)
log.error(TAG, "File system exception", e)
log.error(LTag.CORE, "File system exception", e)
}
}
}
fun importSharedPreferences(fragment: Fragment) {
override fun importSharedPreferences(fragment: Fragment) {
fragment.activity?.let { fragmentAct ->
importSharedPreferences(fragmentAct)
}
}
fun importSharedPreferences(activity: FragmentActivity) {
override fun importSharedPreferences(activity: FragmentActivity) {
try {
if (activity is SingleFragmentActivity)
activity.callForPrefFile.launch(null)
if (activity is MainActivity)
activity.callForPrefFile.launch(null)
if (activity is SetupWizardActivity)
if (activity is DaggerAppCompatActivityWithResult)
activity.callForPrefFile.launch(null)
} catch (e: IllegalArgumentException) {
// this exception happens on some early implementations of ActivityResult contracts
// when registered and called for the second time
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.goto_main_try_again))
log.error(TAG, "Internal android framework exception", e)
log.error(LTag.CORE, "Internal android framework exception", e)
}
}
fun importSharedPreferences(activity: FragmentActivity, importFile: PrefsFile) {
override fun importSharedPreferences(activity: FragmentActivity, importFile: PrefsFile) {
askToConfirmImport(activity, importFile) { password ->
@ -301,7 +296,7 @@ class ImportExportPrefs @Inject constructor(
promptForDecryptionPasswordIfNeeded(activity, prefsAttempted, importOkAttempted, format, importFile) { prefs, importOk ->
// if at end we allow to import preferences
val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.size > 0)
val importPossible = (importOk || buildHelper.isEngineeringMode()) && (prefs.values.isNotEmpty())
PrefImportSummaryDialog.showSummary(activity, importOk, importPossible, prefs, {
if (importPossible) {
@ -325,9 +320,9 @@ class ImportExportPrefs @Inject constructor(
} catch (e: PrefFileNotFoundError) {
ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
log.error(TAG, "Unhandled exception", e)
log.error(LTag.CORE, "Unhandled exception", e)
} catch (e: PrefIOError) {
log.error(TAG, "Unhandled exception", e)
log.error(LTag.CORE, "Unhandled exception", e)
ToastUtils.errorToast(activity, e.message)
}
}
@ -346,13 +341,13 @@ class ImportExportPrefs @Inject constructor(
private fun restartAppAfterImport(context: Context) {
sp.putBoolean(R.string.key_setupwizard_processed, true)
show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
log.debug(TAG, "Exiting")
log.debug(LTag.CORE, "Exiting")
rxBus.send(EventAppExit())
if (context is AppCompatActivity) {
context.finish()
}
System.runFinalization()
System.exit(0)
exitProcess(0)
})
}
}

View file

@ -8,6 +8,7 @@ import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
@ -23,7 +24,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var foodPlugin: FoodPlugin
@Inject lateinit var importExportPrefs: ImportExportPrefs
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.maintenance_fragment, container, false)

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
@ -18,7 +19,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
@ -55,7 +55,7 @@ class SWDefinition @Inject constructor(
private val loopPlugin: LoopPlugin,
private val nsClientPlugin: NSClientPlugin,
private val nsProfilePlugin: NSProfilePlugin,
private val importExportPrefs: ImportExportPrefs,
private val importExportPrefs: ImportExportPrefsInterface,
private val androidPermission: AndroidPermission,
private val cryptoUtil: CryptoUtil,
private val config: Config

View file

@ -14,8 +14,6 @@ import info.nightscout.androidaps.events.EventProfileNeedsUpdate
import info.nightscout.androidaps.events.EventProfileStoreChanged
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
@ -44,7 +42,6 @@ class SetupWizardActivity : NoSplashAppCompatActivity() {
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var sp: SP
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var importExportPrefs: ImportExportPrefs
private val disposable = CompositeDisposable()
private lateinit var screens: List<SWScreen>
@ -52,12 +49,6 @@ class SetupWizardActivity : NoSplashAppCompatActivity() {
private val intentMessage = "WIZZARDPAGE"
val callForPrefFile = registerForActivityResult(PrefsFileContract()) {
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
update(applicationContext)
@ -213,7 +204,7 @@ class SetupWizardActivity : NoSplashAppCompatActivity() {
if (permissions.isNotEmpty()) {
if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) {
when (requestCode) {
AndroidPermission.CASE_STORAGE -> //show dialog after permission is granted
AndroidPermission.CASE_STORAGE -> //show dialog after permission is granted
show(this, resourceHelper.gs(R.string.permission), resourceHelper.gs(R.string.alert_dialog_storage_permission_text))
AndroidPermission.CASE_LOCATION, AndroidPermission.CASE_SMS, AndroidPermission.CASE_BATTERY -> {

View file

@ -64,10 +64,6 @@
<color name="splashBackground">#2E2E2E</color>
<color name="metadataOk">#77dd77</color>
<color name="metadataTextWarning">#FF8C00</color>
<color name="metadataTextError">#FF5555</color>
<color name="importListFileName">#FFFFFF</color>
<color name="importListAdditionalInfo">#BBBBBB</color>

View file

@ -66,16 +66,4 @@
<string name="failedretrievetime">Failed retrieve time</string>
<string name="requirementnotmet">Objective requirements not met</string>
<plurals name="objective_days">
<item quantity="one">%1$d day</item>
<item quantity="other">%1$d days</item>
</plurals>
<plurals name="objective_hours">
<item quantity="one">%1$d hour</item>
<item quantity="other">%1$d hours</item>
</plurals>
<plurals name="objective_minutes">
<item quantity="one">%1$d minute</item>
<item quantity="other">%1$d minutes</item>
</plurals>
</resources>

View file

@ -204,8 +204,6 @@
<string name="different_password_used">This file was exported and encrypted with different master password. Provide old master password to decrypt file.</string>
<string name="master_password_will_be_replaced">As a result of successful import current master password WILL BE REPLACED with that old master password!</string>
<string name="preferences_import_list_title">Select file to import</string>
<string name="check_preferences_before_import">Please check preferences before importing:</string>
<string name="check_preferences_cannot_import">Preferences cannot be imported!</string>
<string name="check_preferences_dangerous_import">Preferences should not be imported!</string>
@ -214,45 +212,6 @@
<string name="check_preferences_import_btn">Import</string>
<string name="check_preferences_import_anyway_btn">Import anyway (DANGEROUS!)</string>
<string name="metadata_warning_different_flavour">Preferences were created with different variant of AAPS (%1$s) while you have: %2$s.\n\nSome settings may be missing or invalid - after importing please check and update your preferences.</string>
<string name="metadata_warning_different_device">Preferences were created on a different device. It is OK if you are importing from older/different phone, but make sure imported preferences are correct!</string>
<string name="metadata_warning_outdated_format">You are using the outdated legacy format from old versions of AAPS, which is not secure! Only use it as a last resort, if you do not have an export in current, JSON format.</string>
<string name="metadata_warning_old_export">Imported preferences are already %1$s days old! Maybe you have more up-to-date preferences or you choose the wrong file? Remember to export preferences regularly!</string>
<string name="metadata_warning_date_format">Invalid date-time format!</string>
<string name="metadata_warning_different_version">Preferences from different minor version of application. It is OK if you are importing after upgrade, but check after import if preferences are still correct!</string>
<string name="metadata_urgent_different_version">Preferences from different major version of application. Major versions differ significantly and may have incompatible preferences! Make sure after import that preferences are still correct!</string>
<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_old">Old export 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_other">Unknown export format</string>
<string name="prefdecrypt_settings_tampered">Settings file tampered</string>
<string name="prefdecrypt_settings_secure">Settings file is secure</string>
<string name="prefdecrypt_settings_unencrypted">Using not secure, unencrypted settings format</string>
<string name="prefdecrypt_wrong_json">JSON format error, missing required field (format, content, metadata or security)</string>
<string name="prefdecrypt_wrong_password">Decryption error, the given password cannot decrypt the file</string>
<string name="prefdecrypt_issue_missing_file_hash">File checksum (hash) missing, cannot verify the authenticity of settings!</string>
<string name="prefdecrypt_issue_modified">File was modified after export!</string>
<string name="prefdecrypt_issue_parsing">Decryption error, parsing preferences failed!</string>
<string name="prefdecrypt_issue_wrong_pass">Decryption error, the provided password is invalid or settings file was modified! It may happen that the imported file was exported with a different Master password.</string>
<string name="prefdecrypt_issue_wrong_format">Missing encryption configuration, settings format is invalid!</string>
<string name="prefdecrypt_issue_wrong_algorithm">Unsupported or not specified encryption algorithm!</string>
<string name="exported_ago" comment="at placeholder we add pluralized number of hours/minutes">exported %1$s ago</string>
<string name="exported_at" comment="at placeholder we add export date">exported at %1$s</string>
<string name="exported_less_than_hour_ago">exported less than hour ago</string>
<string name="in_directory" comment="placeholder is for exported file path">in directory: %1$s</string>
<string name="end_user_license_agreement">End User License Agreement</string>
<string name="end_user_license_agreement_text">MUST NOT BE USED TO MAKE MEDICAL DECISIONS. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.</string>
<string name="end_user_license_agreement_i_understand">I UNDERSTAND AND AGREE</string>
@ -977,7 +936,6 @@
<string name="nav_logsettings">Log settings</string>
<string name="resettodefaults">Reset to defaults</string>
<string name="nsmalfunction">NSClient malfunction. Consider NS and NSClient restart.</string>
<string name="versionavailable">Version %1$s available</string>
<string name="time_offset">Time offset</string>
<string name="key_aps_mode" translatable="false">aps_mode</string>
<string name="setupwizard_preferred_aps_mode">Preferred APS mode</string>
@ -1158,12 +1116,6 @@
<string name="copy_short">COPY</string>
<string name="addnew">Add new</string>
<string name="versionChecker">Version Checker</string>
<string name="key_last_time_this_version_detected" translatable="false">last_time_this_version_detected</string>
<string name="key_last_versionchecker_warning" translatable="false">last_versionchecker_waring</string>
<string name="key_last_versionchecker_plugin_warning" translatable="false">last_versionchecker_plugin_waring</string>
<string name="key_last_revoked_certs_check" translatable="false">last_revoked_certs_check</string>
<string name="signature_verifier">Signature verifier</string>
<string name="running_invalid_version">We have detected that you are running an invalid version. Loop disabled!</string>
<string name="old_version">old version</string>
<string name="very_old_version">very old version</string>

View file

@ -0,0 +1,17 @@
package info.nightscout.androidaps.activities
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.interfaces.ImportExportPrefsInterface
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
import javax.inject.Inject
open class DaggerAppCompatActivityWithResult : DaggerAppCompatActivity() {
@Inject lateinit var importExportPrefs: ImportExportPrefsInterface
val callForPrefFile = registerForActivityResult(PrefsFileContract()) {
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
}

View file

@ -2,12 +2,10 @@ package info.nightscout.androidaps.activities
import android.content.Context
import android.os.Bundle
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.locale.LocaleHelper
//@Suppress("registered")
open class NoSplashAppCompatActivity : DaggerAppCompatActivity() {
open class NoSplashAppCompatActivity : DaggerAppCompatActivityWithResult() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -9,11 +9,13 @@ import info.nightscout.androidaps.dialogs.BolusProgressDialog
import info.nightscout.androidaps.dialogs.ErrorDialog
import info.nightscout.androidaps.dialogs.NtpProgressDialog
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.plugins.general.maintenance.activities.PrefImportListActivity
@Module
@Suppress("unused")
abstract class CoreFragmentsModule {
@ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity
@ContributesAndroidInjector abstract fun contributesTDDStatsActivity(): TDDStatsActivity
@ContributesAndroidInjector abstract fun contributeBolusProgressHelperActivity(): BolusProgressHelperActivity
@ContributesAndroidInjector abstract fun contributeErrorHelperActivity(): ErrorHelperActivity

View file

@ -6,4 +6,6 @@ interface ConfigInterface {
val NSCLIENT: Boolean
val PUMPCONTROL: Boolean
val PUMPDRIVERS: Boolean
val FLAVOR: String
val VERSION_NAME: String
}

View file

@ -0,0 +1,15 @@
package info.nightscout.androidaps.interfaces
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFile
interface ImportExportPrefsInterface {
fun importSharedPreferences(activity: FragmentActivity, importFile: PrefsFile)
fun importSharedPreferences(activity: FragmentActivity)
fun importSharedPreferences(fragment: Fragment)
fun prefsFileExists(): Boolean
fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable)
fun exportSharedPreferences(f: Fragment)
}

View file

@ -2,9 +2,8 @@ package info.nightscout.androidaps.plugins.constraints.versionChecker
import android.content.Context
import android.net.ConnectivityManager
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
@ -24,6 +23,7 @@ class VersionCheckerUtils @Inject constructor(
val sp: SP,
val resourceHelper: ResourceHelper,
val rxBus: RxBusWrapper,
private val config: ConfigInterface,
val context: Context
) {
@ -50,7 +50,7 @@ class VersionCheckerUtils @Inject constructor(
Thread {
try {
val version: String? = findVersion(URL("https://raw.githubusercontent.com/nightscout/AndroidAPS/master/app/build.gradle").readText())
compareWithCurrentVersion(version, BuildConfig.VERSION_NAME)
compareWithCurrentVersion(version, config.VERSION_NAME)
} catch (e: IOException) {
aapsLogger.error(LTag.CORE, "Github master version check error: $e")
}
@ -130,6 +130,7 @@ class VersionCheckerUtils @Inject constructor(
}
companion object {
private val CHECK_EVERY = TimeUnit.DAYS.toMillis(1)
private val WARN_EVERY = TimeUnit.DAYS.toMillis(1)
}
@ -148,7 +149,7 @@ fun findVersion(file: String?): String? {
fun String.versionStrip() = this.mapNotNull {
when (it) {
in '0'..'9' -> it
'.' -> it
'.' -> it
else -> null
}
}.joinToString(separator = "")

View file

@ -1,220 +1,178 @@
package info.nightscout.androidaps.plugins.general.maintenance
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.os.Parcelable
import androidx.activity.result.contract.ActivityResultContract
import androidx.fragment.app.FragmentActivity
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.maintenance.activities.PrefImportListActivity
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.storage.Storage
import kotlinx.android.parcel.Parcelize
import kotlinx.android.parcel.RawValue
import org.joda.time.DateTime
import org.joda.time.Days
import org.joda.time.Hours
import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
enum class PrefsImportDir {
ROOT_DIR,
AAPS_DIR
}
@Parcelize
data class PrefsFile(
val name: String,
val file: File,
val baseDir: File,
val dirKind: PrefsImportDir,
val handler: PrefsFormatsHandler,
// metadata here is used only for list display
val metadata: @RawValue Map<PrefsMetadataKey, PrefMetadata>
) : Parcelable
class PrefsFileContract : ActivityResultContract<Void, PrefsFile>() {
companion object {
const val OUTPUT_PARAM = "prefs_file"
}
override fun parseResult(resultCode: Int, intent: Intent?): PrefsFile? {
return when (resultCode) {
FragmentActivity.RESULT_OK -> intent?.getParcelableExtra(OUTPUT_PARAM)
else -> null
}
}
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(context, PrefImportListActivity::class.java)
}
}
fun getCurrentDeviceModelString() =
Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")"
@Singleton
class PrefFileListProvider @Inject constructor(
private val resourceHelper: ResourceHelper,
private val classicPrefsFormat: ClassicPrefsFormat,
private val encryptedPrefsFormat: EncryptedPrefsFormat,
private val storage: Storage,
private val versionCheckerUtils: VersionCheckerUtils
) {
companion object {
private val path = File(Environment.getExternalStorageDirectory().toString())
private val aapsPath = File(path, "AAPS" + File.separator + "preferences")
private const val IMPORT_AGE_NOT_YET_OLD_DAYS = 60
}
/**
* This function tries to list possible preference files from main SDCard root dir and AAPS/preferences dir
* and tries to do quick assessment for preferences format plausibility.
* It does NOT load full metadata or is 100% accurate - it tries to do QUICK detection, based on:
* - file name and extension
* - predicted file contents
*/
fun listPreferenceFiles(loadMetadata: Boolean = false): MutableList<PrefsFile> {
val prefFiles = mutableListOf<PrefsFile>()
// searching rood dir for legacy files
path.walk().maxDepth(1).filter { it.isFile && (it.name.endsWith(".json") || it.name.contains("Preferences")) }.forEach {
val contents = storage.getFileContents(it)
val detectedNew = encryptedPrefsFormat.isPreferencesFile(it, contents)
val detectedOld = !detectedNew && classicPrefsFormat.isPreferencesFile(it, contents)
if (detectedNew || detectedOld) {
val formatHandler = if (detectedNew) PrefsFormatsHandler.ENCRYPTED else PrefsFormatsHandler.CLASSIC
prefFiles.add(PrefsFile(it.name, it, path, PrefsImportDir.ROOT_DIR, formatHandler, metadataFor(loadMetadata, formatHandler, contents)))
}
}
// searching dedicated dir, only for new JSON format
aapsPath.walk().filter { it.isFile && it.name.endsWith(".json") }.forEach {
val contents = storage.getFileContents(it)
if (encryptedPrefsFormat.isPreferencesFile(it, contents)) {
prefFiles.add(PrefsFile(it.name, it, aapsPath, PrefsImportDir.AAPS_DIR, PrefsFormatsHandler.ENCRYPTED, metadataFor(loadMetadata, PrefsFormatsHandler.ENCRYPTED, contents)))
}
}
// we sort only if we have metadata to be used for that
if (loadMetadata) {
prefFiles.sortWith(
compareByDescending<PrefsFile> { it.handler }
.thenBy { it.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.status }
.thenByDescending { it.metadata[PrefsMetadataKey.CREATED_AT]?.value }
)
}
return prefFiles
}
private fun metadataFor(loadMetadata: Boolean, formatHandler: PrefsFormatsHandler, contents: String): PrefMetadataMap {
if (!loadMetadata) {
return mapOf()
}
return checkMetadata(when (formatHandler) {
PrefsFormatsHandler.CLASSIC -> classicPrefsFormat.loadMetadata(contents)
PrefsFormatsHandler.ENCRYPTED -> encryptedPrefsFormat.loadMetadata(contents)
})
}
fun legacyFile(): File {
return File(path, resourceHelper.gs(R.string.app_name) + "Preferences")
}
fun ensureExportDirExists() {
if (!aapsPath.exists()) {
aapsPath.mkdirs()
}
}
fun newExportFile(): File {
val timeLocal = LocalDateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd'_'HHmmss"))
return File(aapsPath, timeLocal + "_" + BuildConfig.FLAVOR + ".json")
}
// check metadata for known issues, change their status and add info with explanations
fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> {
val meta = metadata.toMutableMap()
meta[PrefsMetadataKey.AAPS_FLAVOUR]?.let { flavour ->
val flavourOfPrefs = flavour.value
if (flavour.value != BuildConfig.FLAVOR) {
flavour.status = PrefsStatus.WARN
flavour.info = resourceHelper.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, BuildConfig.FLAVOR)
}
}
meta[PrefsMetadataKey.DEVICE_MODEL]?.let { model ->
if (model.value != getCurrentDeviceModelString()) {
model.status = PrefsStatus.WARN
model.info = resourceHelper.gs(R.string.metadata_warning_different_device)
}
}
meta[PrefsMetadataKey.CREATED_AT]?.let { createdAt ->
try {
val date1 = DateTime.parse(createdAt.value)
val date2 = DateTime.now()
val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).days
if (daysOld > IMPORT_AGE_NOT_YET_OLD_DAYS) {
createdAt.status = PrefsStatus.WARN
createdAt.info = resourceHelper.gs(R.string.metadata_warning_old_export, daysOld.toString())
}
} catch (e: Exception) {
createdAt.status = PrefsStatus.WARN
createdAt.info = resourceHelper.gs(R.string.metadata_warning_date_format)
}
}
meta[PrefsMetadataKey.AAPS_VERSION]?.let { version ->
val currentAppVer = versionCheckerUtils.versionDigits(BuildConfig.VERSION_NAME)
val metadataVer = versionCheckerUtils.versionDigits(version.value)
if ((currentAppVer.size >= 2) && (metadataVer.size >= 2) && (Math.abs(currentAppVer[1] - metadataVer[1]) > 1)) {
version.status = PrefsStatus.WARN
version.info = resourceHelper.gs(R.string.metadata_warning_different_version)
}
if ((currentAppVer.isNotEmpty()) && (metadataVer.isNotEmpty()) && (currentAppVer[0] != metadataVer[0])) {
version.status = PrefsStatus.WARN
version.info = resourceHelper.gs(R.string.metadata_urgent_different_version)
}
}
return meta
}
fun formatExportedAgo(utcTime: String): String {
val refTime = DateTime.now()
val itTime = DateTime.parse(utcTime)
val days = Days.daysBetween(itTime, refTime).days
val hours = Hours.hoursBetween(itTime, refTime).hours
return if (hours == 0) {
resourceHelper.gs(R.string.exported_less_than_hour_ago)
} else if ((hours < 24) && (hours > 0)) {
resourceHelper.gs(R.string.exported_ago, resourceHelper.gq(R.plurals.objective_hours, hours, hours))
} else if ((days < IMPORT_AGE_NOT_YET_OLD_DAYS) && (days > 0)) {
resourceHelper.gs(R.string.exported_ago, resourceHelper.gq(R.plurals.objective_days, days, days))
} else {
resourceHelper.gs(R.string.exported_at, utcTime.substring(0, 10))
}
}
package info.nightscout.androidaps.plugins.general.maintenance
import android.os.Build
import android.os.Environment
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.interfaces.ConfigInterface
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.storage.Storage
import org.joda.time.DateTime
import org.joda.time.Days
import org.joda.time.Hours
import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
import java.io.File
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
fun getCurrentDeviceModelString() =
Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")"
@Singleton
class PrefFileListProvider @Inject constructor(
private val resourceHelper: ResourceHelper,
private val config: ConfigInterface,
private val classicPrefsFormat: ClassicPrefsFormat,
private val encryptedPrefsFormat: EncryptedPrefsFormat,
private val storage: Storage,
private val versionCheckerUtils: VersionCheckerUtils
) {
companion object {
private val path = File(Environment.getExternalStorageDirectory().toString())
private val aapsPath = File(path, "AAPS" + File.separator + "preferences")
private const val IMPORT_AGE_NOT_YET_OLD_DAYS = 60
}
/**
* This function tries to list possible preference files from main SDCard root dir and AAPS/preferences dir
* and tries to do quick assessment for preferences format plausibility.
* It does NOT load full metadata or is 100% accurate - it tries to do QUICK detection, based on:
* - file name and extension
* - predicted file contents
*/
fun listPreferenceFiles(loadMetadata: Boolean = false): MutableList<PrefsFile> {
val prefFiles = mutableListOf<PrefsFile>()
// searching rood dir for legacy files
path.walk().maxDepth(1).filter { it.isFile && (it.name.endsWith(".json") || it.name.contains("Preferences")) }.forEach {
val contents = storage.getFileContents(it)
val detectedNew = encryptedPrefsFormat.isPreferencesFile(it, contents)
val detectedOld = !detectedNew && classicPrefsFormat.isPreferencesFile(it, contents)
if (detectedNew || detectedOld) {
val formatHandler = if (detectedNew) PrefsFormatsHandler.ENCRYPTED else PrefsFormatsHandler.CLASSIC
prefFiles.add(PrefsFile(it.name, it, path, PrefsImportDir.ROOT_DIR, formatHandler, metadataFor(loadMetadata, formatHandler, contents)))
}
}
// searching dedicated dir, only for new JSON format
aapsPath.walk().filter { it.isFile && it.name.endsWith(".json") }.forEach {
val contents = storage.getFileContents(it)
if (encryptedPrefsFormat.isPreferencesFile(it, contents)) {
prefFiles.add(PrefsFile(it.name, it, aapsPath, PrefsImportDir.AAPS_DIR, PrefsFormatsHandler.ENCRYPTED, metadataFor(loadMetadata, PrefsFormatsHandler.ENCRYPTED, contents)))
}
}
// we sort only if we have metadata to be used for that
if (loadMetadata) {
prefFiles.sortWith(
compareByDescending<PrefsFile> { it.handler }
.thenBy { it.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.status }
.thenByDescending { it.metadata[PrefsMetadataKey.CREATED_AT]?.value }
)
}
return prefFiles
}
private fun metadataFor(loadMetadata: Boolean, formatHandler: PrefsFormatsHandler, contents: String): PrefMetadataMap {
if (!loadMetadata) {
return mapOf()
}
return checkMetadata(when (formatHandler) {
PrefsFormatsHandler.CLASSIC -> classicPrefsFormat.loadMetadata(contents)
PrefsFormatsHandler.ENCRYPTED -> encryptedPrefsFormat.loadMetadata(contents)
})
}
fun legacyFile(): File {
return File(path, resourceHelper.gs(R.string.app_name) + "Preferences")
}
fun ensureExportDirExists() {
if (!aapsPath.exists()) {
aapsPath.mkdirs()
}
}
fun newExportFile(): File {
val timeLocal = LocalDateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd'_'HHmmss"))
return File(aapsPath, timeLocal + "_" + config.FLAVOR + ".json")
}
// check metadata for known issues, change their status and add info with explanations
fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> {
val meta = metadata.toMutableMap()
meta[PrefsMetadataKey.AAPS_FLAVOUR]?.let { flavour ->
val flavourOfPrefs = flavour.value
if (flavour.value != config.FLAVOR) {
flavour.status = PrefsStatus.WARN
flavour.info = resourceHelper.gs(R.string.metadata_warning_different_flavour, flavourOfPrefs, config.FLAVOR)
}
}
meta[PrefsMetadataKey.DEVICE_MODEL]?.let { model ->
if (model.value != getCurrentDeviceModelString()) {
model.status = PrefsStatus.WARN
model.info = resourceHelper.gs(R.string.metadata_warning_different_device)
}
}
meta[PrefsMetadataKey.CREATED_AT]?.let { createdAt ->
try {
val date1 = DateTime.parse(createdAt.value)
val date2 = DateTime.now()
val daysOld = Days.daysBetween(date1.toLocalDate(), date2.toLocalDate()).days
if (daysOld > IMPORT_AGE_NOT_YET_OLD_DAYS) {
createdAt.status = PrefsStatus.WARN
createdAt.info = resourceHelper.gs(R.string.metadata_warning_old_export, daysOld.toString())
}
} catch (e: Exception) {
createdAt.status = PrefsStatus.WARN
createdAt.info = resourceHelper.gs(R.string.metadata_warning_date_format)
}
}
meta[PrefsMetadataKey.AAPS_VERSION]?.let { version ->
val currentAppVer = versionCheckerUtils.versionDigits(config.VERSION_NAME)
val metadataVer = versionCheckerUtils.versionDigits(version.value)
if ((currentAppVer.size >= 2) && (metadataVer.size >= 2) && (abs(currentAppVer[1] - metadataVer[1]) > 1)) {
version.status = PrefsStatus.WARN
version.info = resourceHelper.gs(R.string.metadata_warning_different_version)
}
if ((currentAppVer.isNotEmpty()) && (metadataVer.isNotEmpty()) && (currentAppVer[0] != metadataVer[0])) {
version.status = PrefsStatus.WARN
version.info = resourceHelper.gs(R.string.metadata_urgent_different_version)
}
}
return meta
}
fun formatExportedAgo(utcTime: String): String {
val refTime = DateTime.now()
val itTime = DateTime.parse(utcTime)
val days = Days.daysBetween(itTime, refTime).days
val hours = Hours.hoursBetween(itTime, refTime).hours
return if (hours == 0) {
resourceHelper.gs(R.string.exported_less_than_hour_ago)
} else if ((hours < 24) && (hours > 0)) {
resourceHelper.gs(R.string.exported_ago, resourceHelper.gq(R.plurals.hours, hours, hours))
} else if ((days < IMPORT_AGE_NOT_YET_OLD_DAYS) && (days > 0)) {
resourceHelper.gs(R.string.exported_ago, resourceHelper.gq(R.plurals.days, days, days))
} else {
resourceHelper.gs(R.string.exported_at, utcTime.substring(0, 10))
}
}
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.plugins.general.maintenance
import android.os.Parcelable
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefMetadata
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsMetadataKey
import kotlinx.android.parcel.Parcelize
import kotlinx.android.parcel.RawValue
import java.io.File
@Parcelize
data class PrefsFile(
val name: String,
val file: File,
val baseDir: File,
val dirKind: PrefsImportDir,
val handler: PrefsFormatsHandler,
// metadata here is used only for list display
val metadata: @RawValue Map<PrefsMetadataKey, PrefMetadata>
) : Parcelable

View file

@ -0,0 +1,27 @@
package info.nightscout.androidaps.plugins.general.maintenance
import android.content.Context
import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract
import androidx.fragment.app.FragmentActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.PrefImportListActivity
class PrefsFileContract : ActivityResultContract<Void, PrefsFile>() {
companion object {
const val OUTPUT_PARAM = "prefs_file"
}
override fun parseResult(resultCode: Int, intent: Intent?): PrefsFile? {
return when (resultCode) {
FragmentActivity.RESULT_OK -> intent?.getParcelableExtra(OUTPUT_PARAM)
else -> null
}
}
override fun createIntent(context: Context, input: Void?): Intent {
return Intent(context, PrefImportListActivity::class.java)
}
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.maintenance
enum class PrefsFormatsHandler {
CLASSIC,
ENCRYPTED
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.maintenance
enum class PrefsImportDir {
ROOT_DIR,
AAPS_DIR
}

View file

@ -12,11 +12,11 @@ import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFile
import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract
import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsFormatsHandler
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.PrefsStatus
import info.nightscout.androidaps.utils.locale.LocaleHelper
@ -46,6 +46,7 @@ class PrefImportListActivity : DaggerAppCompatActivity() {
inner class RecyclerViewAdapter internal constructor(private var prefFileList: List<PrefsFile>) : RecyclerView.Adapter<RecyclerViewAdapter.PrefFileViewHolder>() {
inner class PrefFileViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var fileName: TextView = itemView.findViewById(R.id.filelist_name)
var fileDir: TextView = itemView.findViewById(R.id.filelist_dir)
var metaDateTime: TextView = itemView.findViewById(R.id.meta_date_time)

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.general.maintenance.formats
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.storage.Storage
import java.io.File
@ -17,6 +17,7 @@ class ClassicPrefsFormat @Inject constructor(
) : PrefsFormat {
companion object {
val FORMAT_KEY = "aaps_old"
}

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.general.maintenance.formats
import info.nightscout.androidaps.R
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.CryptoUtil
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
import info.nightscout.androidaps.utils.extensions.toHex
@ -23,6 +23,7 @@ class EncryptedPrefsFormat @Inject constructor(
) : PrefsFormat {
companion object {
val FORMAT_KEY_ENC = "aaps_encrypted"
val FORMAT_KEY_NOENC = "aaps_structured"

View file

@ -4,7 +4,7 @@ import android.content.Context
import android.os.Parcelable
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import info.nightscout.androidaps.R
import info.nightscout.androidaps.core.R
import kotlinx.android.parcel.Parcelize
import java.io.File
@ -74,11 +74,6 @@ enum class PrefsStatus(@DrawableRes val icon: Int) {
DISABLED(R.drawable.ic_meta_error)
}
enum class PrefsFormatsHandler {
CLASSIC,
ENCRYPTED
}
class PrefFileNotFoundError(message: String) : Exception(message)
class PrefIOError(message: String) : Exception(message)
class PrefFormatError(message: String) : Exception(message)

View file

@ -1,17 +1,17 @@
package info.nightscout.androidaps.utils.storage
import java.io.File
import javax.inject.Singleton
@Singleton
class FileStorage : Storage {
override fun getFileContents(file: File): String {
return file.readText()
}
override fun putFileContents(file: File, contents: String) {
file.writeText(contents)
}
package info.nightscout.androidaps.utils.storage
import java.io.File
import javax.inject.Singleton
@Singleton
class FileStorage : Storage {
override fun getFileContents(file: File): String {
return file.readText()
}
override fun putFileContents(file: File, contents: String) {
file.writeText(contents)
}
}

View file

@ -1,11 +1,11 @@
package info.nightscout.androidaps.utils.storage
import java.io.File
// This may seems unnecessary abstraction - but it will simplify testing
interface Storage {
fun getFileContents(file: File) : String
fun putFileContents(file: File, contents: String)
}
package info.nightscout.androidaps.utils.storage
import java.io.File
// This may seems unnecessary abstraction - but it will simplify testing
interface Storage {
fun getFileContents(file: File): String
fun putFileContents(file: File, contents: String)
}

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M15,13H16.5V15.82L18.94,17.23L18.19,18.53L15,16.69V13M19,8H5V19H9.67C9.24,18.09 9,17.07 9,16A7,7 0 0,1 16,9C17.07,9 18.09,9.24 19,9.67V8M5,21C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H6V1H8V3H16V1H18V3H19A2,2 0 0,1 21,5V11.1C22.24,12.36 23,14.09 23,16A7,7 0 0,1 16,23C14.09,23 12.36,22.24 11.1,21H5M16,11.15A4.85,4.85 0 0,0 11.15,16C11.15,18.68 13.32,20.85 16,20.85A4.85,4.85 0 0,0 20.85,16C20.85,13.32 18.68,11.15 16,11.15Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M15,13H16.5V15.82L18.94,17.23L18.19,18.53L15,16.69V13M19,8H5V19H9.67C9.24,18.09 9,17.07 9,16A7,7 0 0,1 16,9C17.07,9 18.09,9.24 19,9.67V8M5,21C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H6V1H8V3H16V1H18V3H19A2,2 0 0,1 21,5V11.1C22.24,12.36 23,14.09 23,16A7,7 0 0,1 16,23C14.09,23 12.36,22.24 11.1,21H5M16,11.15A4.85,4.85 0 0,0 11.15,16C11.15,18.68 13.32,20.85 16,20.85A4.85,4.85 0 0,0 20.85,16C20.85,13.32 18.68,11.15 16,11.15Z" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M21,11C21,16.55 17.16,21.74 12,23C6.84,21.74 3,16.55 3,11V5L12,1L21,5V11M12,21C15.75,20 19,15.54 19,11.22V6.3L12,3.18L5,6.3V11.22C5,15.54 8.25,20 12,21M14.8,11V9.5C14.8,8.1 13.4,7 12,7C10.6,7 9.2,8.1 9.2,9.5V11C8.6,11 8,11.6 8,12.2V15.7C8,16.4 8.6,17 9.2,17H14.7C15.4,17 16,16.4 16,15.8V12.3C16,11.6 15.4,11 14.8,11M13.5,11H10.5V9.5C10.5,8.7 11.2,8.2 12,8.2C12.8,8.2 13.5,8.7 13.5,9.5V11Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M21,11C21,16.55 17.16,21.74 12,23C6.84,21.74 3,16.55 3,11V5L12,1L21,5V11M12,21C15.75,20 19,15.54 19,11.22V6.3L12,3.18L5,6.3V11.22C5,15.54 8.25,20 12,21M14.8,11V9.5C14.8,8.1 13.4,7 12,7C10.6,7 9.2,8.1 9.2,9.5V11C8.6,11 8,11.6 8,12.2V15.7C8,16.4 8.6,17 9.2,17H14.7C15.4,17 16,16.4 16,15.8V12.3C16,11.6 15.4,11 14.8,11M13.5,11H10.5V9.5C10.5,8.7 11.2,8.2 12,8.2C12.8,8.2 13.5,8.7 13.5,9.5V11Z" />
</vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/metadataTextError"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M8.27,3L3,8.27V15.73L8.27,21H15.73L21,15.73V8.27L15.73,3M8.41,7L12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/metadataTextError"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M8.27,3L3,8.27V15.73L8.27,21H15.73L21,15.73V8.27L15.73,3M8.41,7L12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,10.84 21.79,9.69 21.39,8.61L19.79,10.21C19.93,10.8 20,11.4 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.6,4 13.2,4.07 13.79,4.21L15.4,2.6C14.31,2.21 13.16,2 12,2M19,2L15,6V7.5L12.45,10.05C12.3,10 12.15,10 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12C14,11.85 14,11.7 13.95,11.55L16.5,9H18L22,5H19V2M12,6A6,6 0 0,0 6,12A6,6 0 0,0 12,18A6,6 0 0,0 18,12H16A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8V6Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,10.84 21.79,9.69 21.39,8.61L19.79,10.21C19.93,10.8 20,11.4 20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.6,4 13.2,4.07 13.79,4.21L15.4,2.6C14.31,2.21 13.16,2 12,2M19,2L15,6V7.5L12.45,10.05C12.3,10 12.15,10 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12C14,11.85 14,11.7 13.95,11.55L16.5,9H18L22,5H19V2M12,6A6,6 0 0,0 6,12A6,6 0 0,0 12,18A6,6 0 0,0 18,12H16A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8V6Z" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M6 2C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H12V20H6V4H13V9H18V12H20V8L14 2M18 14C17.87 14 17.76 14.09 17.74 14.21L17.55 15.53C17.25 15.66 16.96 15.82 16.7 16L15.46 15.5C15.35 15.5 15.22 15.5 15.15 15.63L14.15 17.36C14.09 17.47 14.11 17.6 14.21 17.68L15.27 18.5C15.25 18.67 15.24 18.83 15.24 19C15.24 19.17 15.25 19.33 15.27 19.5L14.21 20.32C14.12 20.4 14.09 20.53 14.15 20.64L15.15 22.37C15.21 22.5 15.34 22.5 15.46 22.5L16.7 22C16.96 22.18 17.24 22.35 17.55 22.47L17.74 23.79C17.76 23.91 17.86 24 18 24H20C20.11 24 20.22 23.91 20.24 23.79L20.43 22.47C20.73 22.34 21 22.18 21.27 22L22.5 22.5C22.63 22.5 22.76 22.5 22.83 22.37L23.83 20.64C23.89 20.53 23.86 20.4 23.77 20.32L22.7 19.5C22.72 19.33 22.74 19.17 22.74 19C22.74 18.83 22.73 18.67 22.7 18.5L23.76 17.68C23.85 17.6 23.88 17.47 23.82 17.36L22.82 15.63C22.76 15.5 22.63 15.5 22.5 15.5L21.27 16C21 15.82 20.73 15.65 20.42 15.53L20.23 14.21C20.22 14.09 20.11 14 20 14M19 17.5C19.83 17.5 20.5 18.17 20.5 19C20.5 19.83 19.83 20.5 19 20.5C18.16 20.5 17.5 19.83 17.5 19C17.5 18.17 18.17 17.5 19 17.5Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M6 2C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H12V20H6V4H13V9H18V12H20V8L14 2M18 14C17.87 14 17.76 14.09 17.74 14.21L17.55 15.53C17.25 15.66 16.96 15.82 16.7 16L15.46 15.5C15.35 15.5 15.22 15.5 15.15 15.63L14.15 17.36C14.09 17.47 14.11 17.6 14.21 17.68L15.27 18.5C15.25 18.67 15.24 18.83 15.24 19C15.24 19.17 15.25 19.33 15.27 19.5L14.21 20.32C14.12 20.4 14.09 20.53 14.15 20.64L15.15 22.37C15.21 22.5 15.34 22.5 15.46 22.5L16.7 22C16.96 22.18 17.24 22.35 17.55 22.47L17.74 23.79C17.76 23.91 17.86 24 18 24H20C20.11 24 20.22 23.91 20.24 23.79L20.43 22.47C20.73 22.34 21 22.18 21.27 22L22.5 22.5C22.63 22.5 22.76 22.5 22.83 22.37L23.83 20.64C23.89 20.53 23.86 20.4 23.77 20.32L22.7 19.5C22.72 19.33 22.74 19.17 22.74 19C22.74 18.83 22.73 18.67 22.7 18.5L23.76 17.68C23.85 17.6 23.88 17.47 23.82 17.36L22.82 15.63C22.76 15.5 22.63 15.5 22.5 15.5L21.27 16C21 15.82 20.73 15.65 20.42 15.53L20.23 14.21C20.22 14.09 20.11 14 20 14M19 17.5C19.83 17.5 20.5 18.17 20.5 19C20.5 19.83 19.83 20.5 19 20.5C18.16 20.5 17.5 19.83 17.5 19C17.5 18.17 18.17 17.5 19 17.5Z" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13 7H11V9H13V7M13 11H11V17H13V11M17 1H7C5.9 1 5 1.9 5 3V21C5 22.1 5.9 23 7 23H17C18.1 23 19 22.1 19 21V3C19 1.9 18.1 1 17 1M17 19H7V5H17V19Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13 7H11V9H13V7M13 11H11V17H13V11M17 1H7C5.9 1 5 1.9 5 3V21C5 22.1 5.9 23 7 23H17C18.1 23 19 22.1 19 21V3C19 1.9 18.1 1 17 1M17 19H7V5H17V19Z" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M22,3H2C0.91,3.04 0.04,3.91 0,5V19C0.04,20.09 0.91,20.96 2,21H22C23.09,20.96 23.96,20.09 24,19V5C23.96,3.91 23.09,3.04 22,3M22,19H2V5H22V19M14,17V15.75C14,14.09 10.66,13.25 9,13.25C7.34,13.25 4,14.09 4,15.75V17H14M9,7A2.5,2.5 0 0,0 6.5,9.5A2.5,2.5 0 0,0 9,12A2.5,2.5 0 0,0 11.5,9.5A2.5,2.5 0 0,0 9,7M14,7V8H20V7H14M14,9V10H20V9H14M14,11V12H18V11H14" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M22,3H2C0.91,3.04 0.04,3.91 0,5V19C0.04,20.09 0.91,20.96 2,21H22C23.09,20.96 23.96,20.09 24,19V5C23.96,3.91 23.09,3.04 22,3M22,19H2V5H22V19M14,17V15.75C14,14.09 10.66,13.25 9,13.25C7.34,13.25 4,14.09 4,15.75V17H14M9,7A2.5,2.5 0 0,0 6.5,9.5A2.5,2.5 0 0,0 9,12A2.5,2.5 0 0,0 11.5,9.5A2.5,2.5 0 0,0 9,7M14,7V8H20V7H14M14,9V10H20V9H14M14,11V12H18V11H14" />
</vector>

View file

@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M2,10.96C1.5,10.68 1.35,10.07 1.63,9.59L3.13,7C3.24,6.8 3.41,6.66 3.6,6.58L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.66,6.72 20.82,6.88 20.91,7.08L22.36,9.6C22.64,10.08 22.47,10.69 22,10.96L21,11.54V16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V10.96C2.7,11.13 2.32,11.14 2,10.96M12,4.15V4.15L12,10.85V10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V12.69L14,15.59C13.67,15.77 13.3,15.76 13,15.6V19.29L19,15.91M13.85,13.36L20.13,9.73L19.55,8.72L13.27,12.35L13.85,13.36Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M2,10.96C1.5,10.68 1.35,10.07 1.63,9.59L3.13,7C3.24,6.8 3.41,6.66 3.6,6.58L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.66,6.72 20.82,6.88 20.91,7.08L22.36,9.6C22.64,10.08 22.47,10.69 22,10.96L21,11.54V16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V10.96C2.7,11.13 2.32,11.14 2,10.96M12,4.15V4.15L12,10.85V10.85L17.96,7.5L12,4.15M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V12.69L14,15.59C13.67,15.77 13.3,15.76 13,15.6V19.29L19,15.91M13.85,13.36L20.13,9.73L19.55,8.72L13.27,12.35L13.85,13.36Z" />
</vector>

View file

@ -1,10 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/metadataTextWarning"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z" />
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/metadataTextWarning"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z" />
</vector>

View file

@ -77,5 +77,10 @@
<color name="white_alpha_80">#CCFFFFFF</color>
<color name="white_alpha_90">#E6FFFFFF</color>
<!-- Maintenance -->
<color name="metadataOk">#77dd77</color>
<color name="metadataTextWarning">#FF8C00</color>
<color name="metadataTextError">#FF5555</color>
</resources>

View file

@ -266,29 +266,62 @@
<string name="history_group_glucose">Glucose</string>
<string name="mute5min">Mute for 5 minutes</string>
<!-- <string name="medtronic_pump_status_never_contacted">Never contacted</string>-->
<!-- <string name="medtronic_pump_status_waking_up">Waking up</string>-->
<!-- <string name="medtronic_pump_status_error_comm">Error with communication</string>-->
<!-- <string name="medtronic_pump_status_timeout_comm">Timeout on communication</string>-->
<!-- <string name="medtronic_pump_status_pump_unreachable">Pump unreachable</string>-->
<!-- <string name="medtronic_pump_status_invalid_config">Invalid configuration</string>-->
<!-- <string name="medtronic_pump_status_active">Active</string>-->
<!-- <string name="medtronic_pump_status_sleeping">Sleeping</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_old">Old export 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_other">Unknown export format</string>
<string name="exported_ago" comment="at placeholder we add pluralized number of hours/minutes">exported %1$s ago</string>
<string name="exported_at" comment="at placeholder we add export date">exported at %1$s</string>
<string name="exported_less_than_hour_ago">exported less than hour ago</string>
<string name="in_directory" comment="placeholder is for exported file path">in directory: %1$s</string>
<string name="preferences_import_list_title">Select file to import</string>
<string name="metadata_warning_different_flavour">Preferences were created with different variant of AAPS (%1$s) while you have: %2$s.\n\nSome settings may be missing or invalid - after importing please check and update your preferences.</string>
<string name="metadata_warning_different_device">Preferences were created on a different device. It is OK if you are importing from older/different phone, but make sure imported preferences are correct!</string>
<string name="metadata_warning_outdated_format">You are using the outdated legacy format from old versions of AAPS, which is not secure! Only use it as a last resort, if you do not have an export in current, JSON format.</string>
<string name="metadata_warning_old_export">Imported preferences are already %1$s days old! Maybe you have more up-to-date preferences or you choose the wrong file? Remember to export preferences regularly!</string>
<string name="metadata_warning_date_format">Invalid date-time format!</string>
<string name="metadata_warning_different_version">Preferences from different minor version of application. It is OK if you are importing after upgrade, but check after import if preferences are still correct!</string>
<string name="metadata_urgent_different_version">Preferences from different major version of application. Major versions differ significantly and may have incompatible preferences! Make sure after import that preferences are still correct!</string>
<string name="prefdecrypt_settings_tampered">Settings file tampered</string>
<string name="prefdecrypt_settings_secure">Settings file is secure</string>
<string name="prefdecrypt_settings_unencrypted">Using not secure, unencrypted settings format</string>
<string name="prefdecrypt_wrong_json">JSON format error, missing required field (format, content, metadata or security)</string>
<string name="prefdecrypt_wrong_password">Decryption error, the given password cannot decrypt the file</string>
<string name="prefdecrypt_issue_missing_file_hash">File checksum (hash) missing, cannot verify the authenticity of settings!</string>
<string name="prefdecrypt_issue_modified">File was modified after export!</string>
<string name="prefdecrypt_issue_parsing">Decryption error, parsing preferences failed!</string>
<string name="prefdecrypt_issue_wrong_pass">Decryption error, the provided password is invalid or settings file was modified! It may happen that the imported file was exported with a different Master password.</string>
<string name="prefdecrypt_issue_wrong_format">Missing encryption configuration, settings format is invalid!</string>
<string name="prefdecrypt_issue_wrong_algorithm">Unsupported or not specified encryption algorithm!</string>
<!-- <string name="danar_history_alarm">Alarms</string>-->
<!-- <string name="danar_history_glucose">Glucose</string>-->
<!-- All(R.string.medtronic_history_group_all), //-->
<!-- Bolus(R.string.danar_history_bolus), //-->
<!-- Basal(R.string.medtronic_history_group_basal), //-->
<!-- Prime(R.string.danar_history_prime), //-->
<!-- Configuration(R.string.medtronic_history_group_configuration), //-->
<!-- Alarm(R.string.danar_history_alarm), //-->
<!-- Glucose(R.string.danar_history_glucose), //-->
<!-- Notification(R.string.medtronic_history_group_notification), //-->
<!-- Statistic(R.string.medtronic_history_group_statistic),-->
<!-- Unknown(R.string.medtronic_history_group_unknown), //-->
<!-- VersionChecker -->
<string name="key_last_time_this_version_detected" translatable="false">last_time_this_version_detected</string>
<string name="key_last_versionchecker_warning" translatable="false">last_versionchecker_waring</string>
<string name="key_last_versionchecker_plugin_warning" translatable="false">last_versionchecker_plugin_waring</string>
<string name="key_last_revoked_certs_check" translatable="false">last_revoked_certs_check</string>
<string name="signature_verifier">Signature verifier</string>
<string name="running_invalid_version">We have detected that you are running an invalid version. Loop disabled!</string>
<string name="versionavailable">Version %1$s available</string>
<plurals name="days">
<item quantity="one">%1$d day</item>
<item quantity="other">%1$d days</item>
</plurals>
<plurals name="hours">
<item quantity="one">%1$d hour</item>
<item quantity="other">%1$d hours</item>
</plurals>
<plurals name="minutes">
<item quantity="one">%1$d minute</item>
<item quantity="other">%1$d minutes</item>
</plurals>
</resources>