diff --git a/app/build.gradle b/app/build.gradle index 453bbeb071..e3666c9961 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,8 +29,7 @@ ext { powermockVersion = "1.7.3" dexmakerVersion = "1.2" retrofit2Version = '2.8.1' - okhttp3Version = '4.5.0' - coroutinesVersion = '1.3.5' + okhttp3Version = '4.6.0' } @@ -165,6 +164,7 @@ android { } firebaseDisable { System.setProperty("disableFirebase", "true") + ext.enableCrashlytics = false } } productFlavors { @@ -252,9 +252,9 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.android.gms:play-services-wearable:17.0.0' implementation "com.google.android.gms:play-services-location:17.0.0" - implementation 'com.google.firebase:firebase-core:17.3.0' - implementation 'com.google.firebase:firebase-auth:19.3.0' - implementation 'com.google.firebase:firebase-database:19.2.1' + implementation 'com.google.firebase:firebase-core:17.4.0' + implementation 'com.google.firebase:firebase-auth:19.3.1' + implementation 'com.google.firebase:firebase-database:19.3.0' implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { transitive = true; } @@ -267,7 +267,9 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation 'androidx.percentlayout:percentlayout:1.0.0' - implementation "androidx.preference:preference-ktx:1.1.0" + implementation "androidx.preference:preference-ktx:1.1.1" + implementation "androidx.activity:activity:${activityVersion}" + implementation "androidx.activity:activity-ktx:${activityVersion}" implementation 'com.google.android.material:material:1.1.0' implementation 'com.wdullaer:materialdatetimepicker:4.2.3' @@ -278,7 +280,7 @@ dependencies { implementation("com.github.tony19:logback-android-classic:1.1.1-6") { exclude group: "com.google.android", module: "android" } - implementation "org.apache.commons:commons-lang3:3.9" + implementation 'org.apache.commons:commons-lang3:3.10' implementation 'org.slf4j:slf4j-api:1.7.30' // Graphview cannot be upgraded implementation "com.jjoe64:graphview:4.0.1" @@ -295,28 +297,28 @@ dependencies { exclude group: "org.json", module: "json" } implementation "com.google.code.gson:gson:2.8.6" - implementation ("com.google.guava:guava:24.1-jre") { + implementation('com.google.guava:guava:29.0-jre') { exclude group: "com.google.code.findbugs", module: "jsr305" } implementation 'com.google.code.findbugs:jsr305:3.0.2' - implementation "net.danlew:android.joda:2.10.3" + implementation 'net.danlew:android.joda:2.10.6' - implementation 'org.mozilla:rhino:1.7.11' + implementation 'org.mozilla:rhino:1.7.12' implementation 'com.github.DavidProdinger:weekdays-selector:1.1.0' implementation 'com.github.kenglxn.QRGen:android:2.6.0' implementation 'com.eatthepath:java-otp:0.2.0' - testImplementation "junit:junit:4.12" + testImplementation "junit:junit:4.13" testImplementation "org.json:json:20190722" testImplementation "org.mockito:mockito-core:2.8.47" testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" - testImplementation "joda-time:joda-time:2.10.5" + testImplementation 'joda-time:joda-time:2.10.6' testImplementation('com.google.truth:truth:1.0.1') { exclude group: "com.google.guava", module: "guava" exclude group: "com.google.code.findbugs", module: "jsr305" @@ -337,24 +339,24 @@ dependencies { implementation "com.squareup.retrofit2:converter-gson:$retrofit2Version" // Phone checker - implementation 'com.scottyab:rootbeer-lib:0.0.7' + implementation 'com.scottyab:rootbeer-lib:0.0.8' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03' androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test:rules:1.3.0-alpha03' + androidTestImplementation 'androidx.test:rules:1.3.0-beta01' androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2' /* Dagger2 - We are going to use dagger.android which includes * support for Activity and fragment injection so we need to include * the following dependencies */ - implementation 'com.google.dagger:dagger-android:2.25.2' - implementation 'com.google.dagger:dagger-android-support:2.25.2' - annotationProcessor 'com.google.dagger:dagger-compiler:2.25.2' - annotationProcessor 'com.google.dagger:dagger-android-processor:2.25.2' - kapt 'com.google.dagger:dagger-android-processor:2.25.2' + implementation "com.google.dagger:dagger-android:$dagger_version" + implementation "com.google.dagger:dagger-android-support:$dagger_version" + annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" + annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version" + kapt "com.google.dagger:dagger-android-processor:$dagger_version" /* Dagger2 - default dependency */ - kapt 'com.google.dagger:dagger-compiler:2.25.2' + kapt "com.google.dagger:dagger-compiler:$dagger_version" androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d46a35c1bf..cd4b814b6c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -82,6 +82,7 @@ + diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index 4bcfd5611a..4e58a1f21f 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -47,7 +47,6 @@ import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionChec import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.setupwizard.SetupWizardActivity -import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.AndroidPermission import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.LocaleHelper @@ -58,6 +57,8 @@ import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.IconsProvider import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.tabs.TabPageAdapter +import info.nightscout.androidaps.utils.ui.UIRunnable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.activity_main.* @@ -160,12 +161,9 @@ class MainActivity : NoSplashAppCompatActivity() { override fun onResume() { super.onResume() protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null, - Runnable { - OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed), Runnable { finish() }) - }, - Runnable { - OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed), Runnable { finish() }) - }) + UIRunnable(Runnable { OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed), Runnable { finish() }) }), + UIRunnable(Runnable { OKDialog.show(this, "", resourceHelper.gs(R.string.authorizationfailed), Runnable { finish() }) }) + ) } private fun setWakeLock() { @@ -197,6 +195,7 @@ class MainActivity : NoSplashAppCompatActivity() { } } main_pager.adapter = pageAdapter + main_pager.offscreenPageLimit = 8 // This may cause more memory consumption checkPluginPreferences(main_pager) // Tabs diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index d2057f3c19..ad5d314a14 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -280,7 +280,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang } for (plugin in pluginStore.plugins) { - pref?.let { pref-> pref.getKey()?.let { plugin.updatePreferenceSummary(pref) }} + pref?.let { it.key?.let { plugin.updatePreferenceSummary(pref) }} } val hmacPasswords = arrayOf( diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index d77d563e09..ea9e44e7fe 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -106,7 +106,7 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { } public String age(boolean useShortText, ResourceHelper resourceHelper) { - Map diff = computeDiff(date, System.currentTimeMillis()); + Map diff = DateUtil.computeDiff(date, System.currentTimeMillis()); String days = " " + resourceHelper.gs(R.string.days) + " "; String hours = " " + resourceHelper.gs(R.string.hours) + " "; @@ -135,23 +135,6 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { "}"; } - //Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0} - private static Map computeDiff(long date1, long date2) { - long diffInMillies = date2 - date1; - List units = new ArrayList<>(EnumSet.allOf(TimeUnit.class)); - Collections.reverse(units); - Map result = new LinkedHashMap<>(); - long milliesRest = diffInMillies; - for (TimeUnit unit : units) { - long diff = unit.convert(milliesRest, TimeUnit.MILLISECONDS); - long diffInMilliesForUnit = unit.toMillis(diff); - milliesRest = milliesRest - diffInMilliesForUnit; - result.put(unit, diff); - } - return result; - } - - public boolean isEvent5minBack(List list, long time) { for (int i = 0; i < list.size(); i++) { CareportalEvent event = list.get(i); diff --git a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java index aa902b0867..937022196c 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java @@ -510,9 +510,9 @@ public class TemporaryBasal implements Interval, DbObjectBase { } else { rate = absoluteRate; } - return DecimalFormatter.to2Decimal(rate) + "U/h "; + return DecimalFormatter.to2Decimal(rate) + "U/h"; } else { // percent - return percentRate + "% "; + return percentRate + "%"; } } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt index 93cac53038..2f6d515b0e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt @@ -6,6 +6,7 @@ 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.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity import info.nightscout.androidaps.plugins.pump.common.dialog.RileyLinkBLEScanActivity @@ -18,8 +19,6 @@ import info.nightscout.androidaps.plugins.pump.insight.activities.InsightAlertAc import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingActivity import info.nightscout.androidaps.plugins.pump.insight.activities.InsightPairingInformationActivity import info.nightscout.androidaps.plugins.pump.medtronic.dialog.MedtronicHistoryActivity -import info.nightscout.androidaps.plugins.pump.omnipod.dialogs.PodHistoryActivity -import info.nightscout.androidaps.plugins.pump.omnipod.dialogs.PodManagementActivity import info.nightscout.androidaps.setupwizard.SetupWizardActivity @Module @@ -50,5 +49,5 @@ abstract class ActivitiesModule { @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity @ContributesAndroidInjector abstract fun contributesTDDStatsActivity(): TDDStatsActivity - + @ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index 1bb1595863..a836da500e 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -69,7 +69,8 @@ import javax.inject.Singleton PreferencesModule::class, OverviewModule::class, DataClassesModule::class, - SMSModule::class + SMSModule::class, + UIModule::class ] ) interface AppComponent : AndroidInjector { diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 548402341b..944fe3615f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -23,7 +23,11 @@ import info.nightscout.androidaps.utils.storage.FileStorage import info.nightscout.androidaps.utils.storage.Storage import javax.inject.Singleton -@Module(includes = [AppModule.AppBindings::class, PluginsModule::class]) +@Module(includes = [ + AppModule.AppBindings::class, + PluginsModule::class, + SkinsModule::class +]) open class AppModule { @Provides diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OmnipodModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OmnipodModule.kt index d60f2d8bfb..ccb538dfbd 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OmnipodModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/OmnipodModule.kt @@ -4,6 +4,8 @@ import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationManager import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState +import info.nightscout.androidaps.plugins.pump.omnipod.dialogs.PodHistoryActivity +import info.nightscout.androidaps.plugins.pump.omnipod.dialogs.PodManagementActivity import info.nightscout.androidaps.plugins.pump.omnipod.dialogs.wizard.pages.InitPodRefreshAction import info.nightscout.androidaps.plugins.pump.omnipod.driver.comm.AapsOmnipodManager import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUITask @@ -12,10 +14,15 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUITask @Suppress("unused") abstract class OmnipodModule { + // Activities + @ContributesAndroidInjector abstract fun contributesPodManagementActivity(): PodManagementActivity + @ContributesAndroidInjector abstract fun contributesPodHistoryActivity(): PodHistoryActivity + @ContributesAndroidInjector abstract fun omnipodCommunicationManagerProvider(): OmnipodCommunicationManager @ContributesAndroidInjector abstract fun omnipodUITaskProvider(): OmnipodUITask @ContributesAndroidInjector abstract fun aapsOmnipodManagerProvider(): AapsOmnipodManager @ContributesAndroidInjector abstract fun initPodRefreshAction(): InitPodRefreshAction @ContributesAndroidInjector abstract fun podSessionState(): PodSessionState -} \ No newline at end of file + +} diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt index fa2df68ef0..39fb959c15 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt @@ -3,6 +3,7 @@ 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 import info.nightscout.androidaps.utils.CryptoUtil @@ -15,4 +16,5 @@ abstract class PreferencesModule { @ContributesAndroidInjector abstract fun importExportPrefsInjector(): ImportExportPrefs @ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat @ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat + @ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/SkinsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/SkinsModule.kt new file mode 100644 index 0000000000..ebd4691f38 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/SkinsModule.kt @@ -0,0 +1,30 @@ +package info.nightscout.androidaps.dependencyInjection + +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntKey +import dagger.multibindings.IntoMap +import info.nightscout.androidaps.skins.SkinButtonsOn +import info.nightscout.androidaps.skins.SkinClassic +import info.nightscout.androidaps.skins.SkinInterface +import javax.inject.Qualifier + +@Module +open class SkinsModule { + + @Provides + @Skin + @IntoMap + @IntKey(0) + fun bindsSkinClassic(skinClassic: SkinClassic): SkinInterface = skinClassic + + @Provides + @Skin + @IntoMap + @IntKey(10) + fun bindsSkinButtonsOn(skinButtonsOn: SkinButtonsOn): SkinInterface = skinButtonsOn + + @Qualifier + annotation class Skin +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/UIModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/UIModule.kt new file mode 100644 index 0000000000..50abc0b69a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/UIModule.kt @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.dependencyInjection + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.plugins.aps.loop.APSResult +import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA +import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS +import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB +import info.nightscout.androidaps.plugins.constraints.objectives.objectives.* +import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData +import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread +import info.nightscout.androidaps.skins.SkinListPreference + +@Module +@Suppress("unused") +abstract class UIModule { + @ContributesAndroidInjector abstract fun skinListPreferenceInjector(): SkinListPreference +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index d891a89dda..5fe20f2968 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -212,7 +212,7 @@ class ObjectivesFragment : DaggerFragment() { bundle.putInt("currentTask", taskPosition) dialog.arguments = bundle ObjectivesExamDialog.objective = objective - fragmentManager?.let { dialog.show(it, "ObjectivesFragment") } + dialog.show(childFragmentManager, "ObjectivesFragment") } } // horizontal line diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt index 749f7f795c..4ea2d78989 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/phoneChecker/PhoneCheckerPlugin.kt @@ -41,7 +41,7 @@ class PhoneCheckerPlugin @Inject constructor( override fun onStart() { super.onStart() - phoneRooted = RootBeer(context).isRootedWithoutBusyBoxCheck() + phoneRooted = RootBeer(context).isRooted() devMode = isDevModeEnabled() } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt index d1ce90fc72..636d623bd0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt @@ -116,6 +116,14 @@ class VersionCheckerUtils @Inject constructor( private fun String?.toNumberList() = this?.numericVersionPart().takeIf { !it.isNullOrBlank() }?.split(".")?.map { it.toInt() } + fun versionDigits(versionString: String?): IntArray { + val digits = mutableListOf() + versionString?.numericVersionPart().toNumberList()?.let { + digits.addAll(it.take(4)) + } + return digits.toIntArray() + } + fun findVersion(file: String?): String? { val regex = "(.*)version(.*)\"(((\\d+)\\.)+(\\d+))\"(.*)".toRegex() return file?.lines()?.filter { regex.matches(it) }?.mapNotNull { regex.matchEntire(it)?.groupValues?.getOrNull(3) }?.firstOrNull() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 9e80d9d331..6a324d74cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -25,14 +25,15 @@ import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.SingleClickButton +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.extensions.plusAssign -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.ui.UIRunnable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import kotlinx.android.synthetic.main.actions_fragment.* @@ -68,19 +69,19 @@ class ActionsFragment : DaggerFragment() { super.onViewCreated(view, savedInstanceState) actions_profileswitch.setOnClickListener { - fragmentManager?.let { ProfileSwitchDialog().show(it, "Actions") } + ProfileSwitchDialog().show(childFragmentManager, "Actions") } actions_temptarget.setOnClickListener { - fragmentManager?.let { TempTargetDialog().show(it, "Actions") } + TempTargetDialog().show(childFragmentManager, "Actions") } actions_extendedbolus.setOnClickListener { activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), resourceHelper.gs(R.string.ebstopsloop), Runnable { - fragmentManager?.let { ExtendedBolusDialog().show(it, "Actions") } + ExtendedBolusDialog().show(childFragmentManager, "Actions") }, null) - }) + })) } } actions_extendedbolus_cancel.setOnClickListener { @@ -101,7 +102,7 @@ class ActionsFragment : DaggerFragment() { } } actions_settempbasal.setOnClickListener { - fragmentManager?.let { TempBasalDialog().show(it, "Actions") } + TempBasalDialog().show(childFragmentManager, "Actions") } actions_canceltempbasal.setOnClickListener { if (activePlugin.activeTreatments.isTempBasalInProgress) { @@ -122,25 +123,25 @@ class ActionsFragment : DaggerFragment() { } actions_fill.setOnClickListener { activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { fragmentManager?.let { FillDialog().show(it, "FillDialog") } }) + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { FillDialog().show(childFragmentManager, "FillDialog") })) } } actions_historybrowser.setOnClickListener { startActivity(Intent(context, HistoryBrowseActivity::class.java)) } actions_tddstats.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) } actions_bgcheck.setOnClickListener { - fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.BGCHECK, R.string.careportal_bgcheck).show(it, "Actions") } + CareDialog().setOptions(CareDialog.EventType.BGCHECK, R.string.careportal_bgcheck).show(childFragmentManager, "Actions") } actions_cgmsensorinsert.setOnClickListener { - fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.SENSOR_INSERT, R.string.careportal_cgmsensorinsert).show(it, "Actions") } + CareDialog().setOptions(CareDialog.EventType.SENSOR_INSERT, R.string.careportal_cgmsensorinsert).show(childFragmentManager, "Actions") } actions_pumpbatterychange.setOnClickListener { - fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.BATTERY_CHANGE, R.string.careportal_pumpbatterychange).show(it, "Actions") } + CareDialog().setOptions(CareDialog.EventType.BATTERY_CHANGE, R.string.careportal_pumpbatterychange).show(childFragmentManager, "Actions") } actions_note.setOnClickListener { - fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.NOTE, R.string.careportal_note).show(it, "Actions") } + CareDialog().setOptions(CareDialog.EventType.NOTE, R.string.careportal_note).show(childFragmentManager, "Actions") } actions_exercise.setOnClickListener { - fragmentManager?.let { CareDialog().setOptions(CareDialog.EventType.EXERCISE, R.string.careportal_exercise).show(it, "Actions") } + CareDialog().setOptions(CareDialog.EventType.EXERCISE, R.string.careportal_exercise).show(childFragmentManager, "Actions") } sp.putBoolean(R.string.key_objectiveuseactions, true) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt index 4cf73af02f..3e472a9ce9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationFragment.kt @@ -72,7 +72,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { args.putString("event", AutomationEvent(mainApp).toJSON()) args.putInt("position", -1) // New event dialog.arguments = args - fragmentManager?.let { dialog.show(it, "EditEventDialog") } + dialog.show(childFragmentManager, "EditEventDialog") } val callback: ItemTouchHelper.Callback = SimpleItemTouchHelperCallback(eventListAdapter) @@ -182,7 +182,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener { args.putString("event", event.toJSON()) args.putInt("position", position) dialog.arguments = args - fragmentManager?.let { dialog.show(it, "EditEventDialog") } + dialog.show(childFragmentManager, "EditEventDialog") } // Start a drag whenever the handle view it touched holder.iconSort.setOnTouchListener { v: View, motionEvent: MotionEvent -> diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt index 21a62bc0a5..44929a9dee 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/automation/dialogs/EditEventDialog.kt @@ -66,15 +66,15 @@ class EditEventDialog : DialogFragmentWithDate() { args.putString("trigger", event.trigger.toJSON()) val dialog = EditTriggerDialog() dialog.arguments = args - fragmentManager?.let { dialog.show(it, "EditTriggerDialog") } + dialog.show(childFragmentManager, "EditTriggerDialog") } // setup action list view - fragmentManager?.let { actionListAdapter = ActionListAdapter() } + actionListAdapter = ActionListAdapter() automation_actionListView.layoutManager = LinearLayoutManager(context) automation_actionListView.adapter = actionListAdapter - automation_addAction.setOnClickListener { fragmentManager?.let { ChooseActionDialog().show(it, "ChooseActionDialog") } } + automation_addAction.setOnClickListener { ChooseActionDialog().show(childFragmentManager, "ChooseActionDialog") } showPreconditions() @@ -187,9 +187,7 @@ class EditEventDialog : DialogFragmentWithDate() { args.putString("action", action.toJSON()) val dialog = EditActionDialog() dialog.arguments = args - fragmentManager?.let { - dialog.show(it, "EditActionDialog") - } + dialog.show(childFragmentManager, "EditActionDialog") } } view.findViewById(R.id.automation_iconTrash).setOnClickListener { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.kt index 613dfeab32..4007e43b45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/careportal/CareportalFragment.kt @@ -125,9 +125,7 @@ class CareportalFragment : DaggerFragment(), View.OnClickListener { R.id.careportal_openapsoffline -> newDialog.setOptions(OPENAPSOFFLINE, R.string.careportal_openapsoffline) R.id.careportal_temporarytarget -> newDialog.setOptions(TEMPTARGET, R.string.careportal_temporarytarget) } - fragmentManager?.let { - NewNSTreatmentDialog().show(it, "CareportalFragment") - } + NewNSTreatmentDialog().show(childFragmentManager, "CareportalFragment") } private fun updateGUI() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt index 1d72212fb3..4d9021bb71 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt @@ -6,12 +6,12 @@ import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.os.Build -import android.os.Environment import android.provider.Settings +import androidx.activity.invoke import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import info.nightscout.androidaps.BuildConfig import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity @@ -22,9 +22,9 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.maintenance.formats.* import info.nightscout.androidaps.plugins.general.smsCommunicator.otp.OneTimePassword import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show -import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.PrefImportSummaryDialog import info.nightscout.androidaps.utils.alertDialogs.TwoMessagesAlertDialog import info.nightscout.androidaps.utils.alertDialogs.WarningDialog @@ -32,8 +32,6 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP -import org.joda.time.DateTime -import org.joda.time.Days import java.io.File import java.io.FileNotFoundException import java.io.IOException @@ -51,37 +49,25 @@ private val PERMISSIONS_STORAGE = arrayOf( Manifest.permission.WRITE_EXTERNAL_STORAGE ) -private const val IMPORT_AGE_NOT_YET_OLD_DAYS = 60 - @Singleton class ImportExportPrefs @Inject constructor( private var log: AAPSLogger, private val resourceHelper: ResourceHelper, private val sp: SP, private val buildHelper: BuildHelper, - private val otp: OneTimePassword, private val rxBus: RxBusWrapper, private val passwordCheck: PasswordCheck, private val classicPrefsFormat: ClassicPrefsFormat, - private val encryptedPrefsFormat: EncryptedPrefsFormat + private val encryptedPrefsFormat: EncryptedPrefsFormat, + private val prefFileList: PrefFileListProvider ) { val TAG = LTag.CORE - private val path = File(Environment.getExternalStorageDirectory().toString()) - - private val file = File(path, resourceHelper.gs(R.string.app_name) + "Preferences") - private val encFile = File(path, resourceHelper.gs(R.string.app_name) + "Preferences.json") - - fun prefsImportFile(): File { - return if (encFile.exists()) encFile else file - } - fun prefsFileExists(): Boolean { - return encFile.exists() || file.exists() + return prefFileList.listPreferenceFiles().size > 0 } - fun exportSharedPreferences(f: Fragment) { f.activity?.let { exportSharedPreferences(it) } } @@ -135,9 +121,6 @@ class ImportExportPrefs @Inject constructor( return name } - private fun getCurrentDeviceModelString() = - Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")" - private fun prefsEncryptionIsDisabled() = buildHelper.isEngineeringMode() && !sp.getBoolean(resourceHelper.gs(R.string.key_maintenance_encrypt_exported_prefs), true) @@ -173,36 +156,41 @@ class ImportExportPrefs @Inject constructor( return true } - private fun askToConfirmExport(activity: Activity, then: ((password: String) -> Unit)) { + private fun askToConfirmExport(activity: Activity, fileToExport: File, then: ((password: String) -> Unit)) { if (!prefsEncryptionIsDisabled() && !assureMasterPasswordSet(activity, R.string.nav_export)) return TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_export), - resourceHelper.gs(R.string.export_to) + " " + encFile + " ?", + resourceHelper.gs(R.string.export_to) + " " + fileToExport + " ?", resourceHelper.gs(R.string.password_preferences_encrypt_prompt), { askForMasterPassIfNeeded(activity, R.string.preferences_export_canceled, then) - }, null, R.drawable.ic_header_export) + }, null, R.drawable.ic_header_export) } - private fun askToConfirmImport(activity: Activity, fileToImport: File, then: ((password: String) -> Unit)) { + private fun askToConfirmImport(activity: Activity, fileToImport: PrefsFile, then: ((password: String) -> Unit)) { - if (encFile.exists()) { + if (fileToImport.handler == PrefsFormatsHandler.ENCRYPTED) { if (!assureMasterPasswordSet(activity, R.string.nav_import)) return TwoMessagesAlertDialog.showAlert(activity, resourceHelper.gs(R.string.nav_import), - resourceHelper.gs(R.string.import_from) + " " + fileToImport + " ?", + resourceHelper.gs(R.string.import_from) + " " + fileToImport.file + " ?", resourceHelper.gs(R.string.password_preferences_decrypt_prompt), { askForMasterPass(activity, R.string.preferences_import_canceled, then) }, null, R.drawable.ic_header_import) } else { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.nav_import), - resourceHelper.gs(R.string.import_from) + " " + fileToImport + " ?", + resourceHelper.gs(R.string.import_from) + " " + fileToImport.file + " ?", Runnable { then("") }) } } private fun exportSharedPreferences(activity: Activity) { - askToConfirmExport(activity) { password -> + + prefFileList.ensureExportDirExists() + val legacyFile = prefFileList.legacyFile() + val newFile = prefFileList.newExportFile() + + askToConfirmExport(activity, newFile) { password -> try { val entries: MutableMap = mutableMapOf() for ((key, value) in sp.getAll()) { @@ -211,12 +199,14 @@ class ImportExportPrefs @Inject constructor( val prefs = Prefs(entries, prepareMetadata(activity)) - classicPrefsFormat.savePreferences(file, prefs) - encryptedPrefsFormat.savePreferences(encFile, prefs, password) + if (BuildConfig.DEBUG && buildHelper.isEngineeringMode()) { + classicPrefsFormat.savePreferences(legacyFile, prefs) + } + encryptedPrefsFormat.savePreferences(newFile, prefs, password) ToastUtils.okToast(activity, resourceHelper.gs(R.string.exported)) } catch (e: FileNotFoundException) { - ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + encFile) + ToastUtils.errorToast(activity, resourceHelper.gs(R.string.filenotfound) + " " + newFile) log.error(TAG, "Unhandled exception", e) } catch (e: IOException) { ToastUtils.errorToast(activity, e.message) @@ -226,21 +216,38 @@ class ImportExportPrefs @Inject constructor( } fun importSharedPreferences(fragment: Fragment) { - fragment.activity?.let { importSharedPreferences(it) } + fragment.activity?.let { fragmentAct -> + val callForPrefFile = fragmentAct.registerForActivityResult(PrefsFileContract()) { + it?.let { + importSharedPreferences(fragmentAct, it) + } + } + callForPrefFile.invoke() + } } - fun importSharedPreferences(activity: Activity) { + fun importSharedPreferences(activity: FragmentActivity) { + val callForPrefFile = activity.registerForActivityResult(PrefsFileContract()) { + it?.let { + importSharedPreferences(activity, it) + } + } + callForPrefFile.invoke() + } - val importFile = prefsImportFile() + private fun importSharedPreferences(activity: Activity, importFile: PrefsFile) { askToConfirmImport(activity, importFile) { password -> - val format: PrefsFormat = if (encFile.exists()) encryptedPrefsFormat else classicPrefsFormat + val format: PrefsFormat = when (importFile.handler) { + PrefsFormatsHandler.CLASSIC -> classicPrefsFormat + PrefsFormatsHandler.ENCRYPTED -> encryptedPrefsFormat + } try { - val prefs = format.loadPreferences(importFile, password) - prefs.metadata = checkMetadata(prefs.metadata) + val prefs = format.loadPreferences(importFile.file, password) + prefs.metadata = prefFileList.checkMetadata(prefs.metadata) // import is OK when we do not have errors (warnings are allowed) val importOk = checkIfImportIsOk(prefs) @@ -276,45 +283,6 @@ class ImportExportPrefs @Inject constructor( } } - // check metadata for known issues, change their status and add info with explanations - private fun checkMetadata(metadata: Map): Map { - 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()).getDays() - - 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) - } - } - - return meta - } - private fun checkIfImportIsOk(prefs: Prefs): Boolean { var importOk = true diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/PrefFileListProvider.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/PrefFileListProvider.kt new file mode 100644 index 0000000000..52910dfff1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/PrefFileListProvider.kt @@ -0,0 +1,219 @@ +package info.nightscout.androidaps.plugins.general.maintenance + +import android.app.Activity +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 info.nightscout.androidaps.BuildConfig +import info.nightscout.androidaps.R +import info.nightscout.androidaps.logging.AAPSLogger +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.sharedPreferences.SP +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 file: File, + val baseDir: File, + val dirKind: PrefsImportDir, + val handler: PrefsFormatsHandler, + + // metadata here is used only for list display + val metadata: @RawValue Map +) : Parcelable + +class PrefsFileContract : ActivityResultContract() { + + companion object { + const val OUTPUT_PARAM = "prefs_file" + } + + override fun parseResult(resultCode: Int, intent: Intent?): PrefsFile? { + return when (resultCode) { + Activity.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 { + val prefFiles = mutableListOf() + + // 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, 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, 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 { 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): Map { + 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()).getDays() + + 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)) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/PrefImportListActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/PrefImportListActivity.kt new file mode 100644 index 0000000000..7703166d04 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/activities/PrefImportListActivity.kt @@ -0,0 +1,135 @@ +package info.nightscout.androidaps.plugins.general.maintenance.activities + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dagger.android.support.DaggerAppCompatActivity +import info.nightscout.androidaps.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.formats.PrefsMetadataKey +import info.nightscout.androidaps.plugins.general.maintenance.formats.PrefsStatus +import info.nightscout.androidaps.utils.LocaleHelper +import info.nightscout.androidaps.utils.resources.ResourceHelper +import kotlinx.android.synthetic.main.maintenance_importlist_activity.* +import javax.inject.Inject + +class PrefImportListActivity : DaggerAppCompatActivity() { + + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var prefFileListProvider: PrefFileListProvider + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setTheme(R.style.AppTheme) + setContentView(R.layout.maintenance_importlist_activity) + + title = resourceHelper.gs(R.string.preferences_import_list_title) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setDisplayShowTitleEnabled(true) + + importlist_recyclerview.layoutManager = LinearLayoutManager(this) + importlist_recyclerview.adapter = RecyclerViewAdapter(prefFileListProvider.listPreferenceFiles(loadMetadata = true)) + } + + inner class RecyclerViewAdapter internal constructor(private var prefFileList: List) : RecyclerView.Adapter() { + + 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) + var metaDeviceName: TextView = itemView.findViewById(R.id.meta_device_name) + var metaAppVersion: TextView = itemView.findViewById(R.id.meta_app_version) + var metaVariantFormat: TextView = itemView.findViewById(R.id.meta_variant_format) + + var metalineName: View = itemView.findViewById(R.id.metaline_name) + var metaDateTimeIcon: View = itemView.findViewById(R.id.meta_date_time_icon) + + init { + itemView.isClickable = true + itemView.setOnClickListener { + val prefFile = fileName.tag as PrefsFile + val i = Intent() + + i.putExtra(PrefsFileContract.OUTPUT_PARAM, prefFile) + setResult(Activity.RESULT_OK, i) + finish() + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PrefFileViewHolder { + val v = LayoutInflater.from(parent.context).inflate(R.layout.maintenance_importlist_item, parent, false) + return PrefFileViewHolder(v) + } + + override fun getItemCount(): Int { + return prefFileList.size + } + + override fun onBindViewHolder(holder: PrefFileViewHolder, position: Int) { + val prefFile = prefFileList[position] + holder.fileName.text = prefFile.file.name + holder.fileName.tag = prefFile + + holder.fileDir.text = resourceHelper.gs(R.string.in_directory, prefFile.file.parentFile.absolutePath) + + val visible = if (prefFile.handler == PrefsFormatsHandler.CLASSIC) View.GONE else View.VISIBLE + holder.metalineName.visibility = visible + holder.metaDateTimeIcon.visibility = visible + holder.metaAppVersion.visibility = visible + + if (prefFile.handler == PrefsFormatsHandler.CLASSIC) { + holder.metaVariantFormat.text = resourceHelper.gs(R.string.metadata_format_old) + holder.metaVariantFormat.setTextColor(resourceHelper.gc(R.color.metadataTextWarning)) + holder.metaDateTime.text = " " + } else { + + prefFile.metadata[PrefsMetadataKey.AAPS_FLAVOUR]?.let { + holder.metaVariantFormat.text = it.value + val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning + holder.metaVariantFormat.setTextColor(resourceHelper.gc(color)) + } + + prefFile.metadata[PrefsMetadataKey.CREATED_AT]?.let { + holder.metaDateTime.text = prefFileListProvider.formatExportedAgo(it.value) + } + + prefFile.metadata[PrefsMetadataKey.AAPS_VERSION]?.let { + holder.metaAppVersion.text = it.value + val color = if (it.status == PrefsStatus.OK) R.color.metadataOk else R.color.metadataTextWarning + holder.metaAppVersion.setTextColor(resourceHelper.gc(color)) + } + + prefFile.metadata[PrefsMetadataKey.DEVICE_NAME]?.let { + holder.metaDeviceName.text = it.value + } + + } + + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + return true + } + return false + } + + public override fun attachBaseContext(newBase: Context) { + super.attachBaseContext(LocaleHelper.wrap(newBase)) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt index 4689f549a6..40b1ceada4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/ClassicPrefsFormat.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.general.maintenance.formats +import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.storage.Storage @@ -19,6 +20,11 @@ class ClassicPrefsFormat @Inject constructor( val FORMAT_KEY = "aaps_old" } + override fun isPreferencesFile(file: File, preloadedContents: String?): Boolean { + val contents = preloadedContents ?: storage.getFileContents(file) + return contents.contains("units::" + Constants.MGDL) || contents.contains("units::" + Constants.MMOL) + } + override fun savePreferences(file: File, prefs: Prefs, masterPassword: String?) { try { val contents = prefs.values.entries.joinToString("\n") { entry -> @@ -35,7 +41,6 @@ class ClassicPrefsFormat @Inject constructor( override fun loadPreferences(file: File, masterPassword: String?): Prefs { var lineParts: Array val entries: MutableMap = mutableMapOf() - val metadata: MutableMap = mutableMapOf() try { val rawLines = storage.getFileContents(file).split("\n") @@ -46,9 +51,7 @@ class ClassicPrefsFormat @Inject constructor( } } - metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN, resourceHelper.gs(R.string.metadata_warning_outdated_format)) - - return Prefs(entries, metadata) + return Prefs(entries, loadMetadata()) } catch (e: FileNotFoundException) { throw PrefFileNotFoundError(file.absolutePath) @@ -57,4 +60,10 @@ class ClassicPrefsFormat @Inject constructor( } } + override fun loadMetadata(contents: String?): PrefMetadataMap { + val metadata: MutableMap = mutableMapOf() + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN, resourceHelper.gs(R.string.metadata_warning_outdated_format)) + return metadata + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt index 463b5a31cb..ec88c07bec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt @@ -27,6 +27,16 @@ class EncryptedPrefsFormat @Inject constructor( val FORMAT_KEY_NOENC = "aaps_structured" private val KEY_CONSCIENCE = "if you remove/change this, please make sure you know the consequences!" + private val FORMAT_TEST_REGEX = Regex("(\\\"format\\\"\\s*\\:\\s*\\\"aaps_[^\"]*\\\")") + } + + override fun isPreferencesFile(file: File, preloadedContents: String?): Boolean { + return if (file.absolutePath.endsWith(".json")) { + val contents = preloadedContents ?: storage.getFileContents(file) + FORMAT_TEST_REGEX.containsMatchIn(contents) + } else { + false + } } override fun savePreferences(file: File, prefs: Prefs, masterPassword: String?) { @@ -97,7 +107,6 @@ class EncryptedPrefsFormat @Inject constructor( override fun loadPreferences(file: File, masterPassword: String?): Prefs { val entries: MutableMap = mutableMapOf() - val metadata: MutableMap = mutableMapOf() val issues = LinkedList() try { @@ -105,25 +114,11 @@ class EncryptedPrefsFormat @Inject constructor( val fileContents = jsonBody.replace(Regex("(?is)(\\\"file_hash\\\"\\s*\\:\\s*\\\")([^\"]*)(\\\")"), "$1--to-be-calculated--$3") val calculatedFileHash = cryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) val container = JSONObject(jsonBody) + val metadata: MutableMap = loadMetadata(container) - if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { + if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content")) { val fileFormat = container.getString(PrefsMetadataKey.FILE_FORMAT.key) - - if ((fileFormat != FORMAT_KEY_ENC) && (fileFormat != FORMAT_KEY_NOENC)) { - throw PrefFormatError("Unsupported file format: " + fileFormat) - } - - val meta = container.getJSONObject("metadata") val security = container.getJSONObject("security") - - metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(fileFormat, PrefsStatus.OK) - for (key in meta.keys()) { - val metaKey = PrefsMetadataKey.fromKey(key) - if (metaKey != null) { - metadata[metaKey] = PrefMetadata(meta.getString(key), PrefsStatus.OK) - } - } - val encrypted = fileFormat == FORMAT_KEY_ENC var secure: PrefsStatus = PrefsStatus.OK var decryptedOk = false @@ -208,8 +203,6 @@ class EncryptedPrefsFormat @Inject constructor( } metadata[PrefsMetadataKey.ENCRYPTION] = PrefMetadata(encryptionDescStr, secure, issuesStr) - } else { - metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(resourceHelper.gs(R.string.prefdecrypt_wrong_json), PrefsStatus.ERROR) } return Prefs(entries, metadata) @@ -223,4 +216,35 @@ class EncryptedPrefsFormat @Inject constructor( } } + override fun loadMetadata(contents: String?): PrefMetadataMap { + contents?.let { + val container = JSONObject(contents) + return loadMetadata(container) + } + return mutableMapOf() + } + + private fun loadMetadata(container: JSONObject): MutableMap { + val metadata: MutableMap = mutableMapOf() + if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { + val fileFormat = container.getString(PrefsMetadataKey.FILE_FORMAT.key) + if ((fileFormat != FORMAT_KEY_ENC) && (fileFormat != FORMAT_KEY_NOENC)) { + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(resourceHelper.gs(R.string.metadata_format_other), PrefsStatus.ERROR) + } else { + val meta = container.getJSONObject("metadata") + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(fileFormat, PrefsStatus.OK) + for (key in meta.keys()) { + val metaKey = PrefsMetadataKey.fromKey(key) + if (metaKey != null) { + metadata[metaKey] = PrefMetadata(meta.getString(key), PrefsStatus.OK) + } + } + } + } else { + metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(resourceHelper.gs(R.string.prefdecrypt_wrong_json), PrefsStatus.ERROR) + } + + return metadata; + } + } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt index a47add0dd7..d0d3935023 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/PrefsFormat.kt @@ -1,12 +1,14 @@ package info.nightscout.androidaps.plugins.general.maintenance.formats import android.content.Context +import android.os.Parcelable import androidx.annotation.DrawableRes import androidx.annotation.StringRes import info.nightscout.androidaps.R +import kotlinx.android.parcel.Parcelize import java.io.File -enum class PrefsMetadataKey(val key: String, @DrawableRes val icon:Int, @StringRes val label:Int) { +enum class PrefsMetadataKey(val key: String, @DrawableRes val icon: Int, @StringRes val label: Int) { 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), @@ -33,16 +35,15 @@ enum class PrefsMetadataKey(val key: String, @DrawableRes val icon:Int, @StringR } } - } - fun formatForDisplay(context: Context, value:String): String { + fun formatForDisplay(context: Context, value: String): String { return when (this) { FILE_FORMAT -> when (value) { - ClassicPrefsFormat.FORMAT_KEY -> context.getString(R.string.metadata_format_old) - EncryptedPrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new) + ClassicPrefsFormat.FORMAT_KEY -> context.getString(R.string.metadata_format_old) + EncryptedPrefsFormat.FORMAT_KEY_ENC -> context.getString(R.string.metadata_format_new) EncryptedPrefsFormat.FORMAT_KEY_NOENC -> context.getString(R.string.metadata_format_debug) - else -> context.getString(R.string.metadata_format_other) + else -> context.getString(R.string.metadata_format_other) } CREATED_AT -> value.replace("T", " ").replace("Z", " (UTC)") else -> value @@ -51,16 +52,21 @@ enum class PrefsMetadataKey(val key: String, @DrawableRes val icon:Int, @StringR } -data class PrefMetadata(var value : String, var status : PrefsStatus, var info : String? = null) +@Parcelize +data class PrefMetadata(var value: String, var status: PrefsStatus, var info: String? = null) : Parcelable -data class Prefs(val values : Map, var metadata : Map) +typealias PrefMetadataMap = Map + +data class Prefs(val values: Map, var metadata: PrefMetadataMap) interface PrefsFormat { fun savePreferences(file: File, prefs: Prefs, masterPassword: String? = null) - fun loadPreferences(file: File, masterPassword: String? = null) : Prefs + fun loadPreferences(file: File, masterPassword: String? = null): Prefs + fun loadMetadata(contents: String? = null): PrefMetadataMap + fun isPreferencesFile(file: File, preloadedContents: String? = null): Boolean } -enum class PrefsStatus(@DrawableRes val icon:Int) { +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), @@ -68,6 +74,11 @@ 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) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index 962e443c66..4a6455e41c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -57,6 +57,7 @@ import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.CommandQueue +import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.buildHelper.BuildHelper @@ -64,41 +65,33 @@ import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.wizard.QuickWizard import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers -import kotlinx.android.synthetic.main.overview_fragment.* -import kotlinx.android.synthetic.main.overview_fragment.careportal_canulaage -import kotlinx.android.synthetic.main.overview_fragment.careportal_insulinage -import kotlinx.android.synthetic.main.overview_fragment.careportal_reservoirlevel -import kotlinx.android.synthetic.main.overview_fragment.careportal_sensorage -import kotlinx.android.synthetic.main.overview_fragment.careportal_pbage -import kotlinx.android.synthetic.main.overview_fragment.careportal_batterylevel -import kotlinx.android.synthetic.main.overview_fragment.overview_activeprofile -import kotlinx.android.synthetic.main.overview_fragment.overview_apsmode -import kotlinx.android.synthetic.main.overview_fragment.overview_arrow -import kotlinx.android.synthetic.main.overview_fragment.overview_basebasal -import kotlinx.android.synthetic.main.overview_fragment.overview_bg -import kotlinx.android.synthetic.main.overview_fragment.overview_bggraph -import kotlinx.android.synthetic.main.overview_fragment.overview_carbsbutton -import kotlinx.android.synthetic.main.overview_fragment.overview_chartMenuButton -import kotlinx.android.synthetic.main.overview_fragment.overview_cob -import kotlinx.android.synthetic.main.overview_fragment.overview_extendedbolus -import kotlinx.android.synthetic.main.overview_fragment.overview_insulinbutton -import kotlinx.android.synthetic.main.overview_fragment.overview_iob -import kotlinx.android.synthetic.main.overview_fragment.overview_iobcalculationprogess -import kotlinx.android.synthetic.main.overview_fragment.overview_iobgraph -import kotlinx.android.synthetic.main.overview_fragment.overview_looplayout +import kotlinx.android.synthetic.main.overview_buttons_layout.* +import kotlinx.android.synthetic.main.overview_buttons_layout.overview_carbsbutton +import kotlinx.android.synthetic.main.overview_buttons_layout.overview_insulinbutton +import kotlinx.android.synthetic.main.overview_buttons_layout.overview_quickwizardbutton +import kotlinx.android.synthetic.main.overview_buttons_layout.overview_treatmentbutton +import kotlinx.android.synthetic.main.overview_buttons_layout.overview_wizardbutton import kotlinx.android.synthetic.main.overview_fragment.overview_notifications -import kotlinx.android.synthetic.main.overview_fragment.overview_pumpstatus -import kotlinx.android.synthetic.main.overview_fragment.overview_pumpstatuslayout -import kotlinx.android.synthetic.main.overview_fragment.overview_quickwizardbutton -import kotlinx.android.synthetic.main.overview_fragment.overview_sensitivity -import kotlinx.android.synthetic.main.overview_fragment.overview_temptarget -import kotlinx.android.synthetic.main.overview_fragment.overview_treatmentbutton -import kotlinx.android.synthetic.main.overview_fragment.overview_wizardbutton import kotlinx.android.synthetic.main.overview_fragment_nsclient_tablet.* +import kotlinx.android.synthetic.main.overview_graphs_layout.overview_bggraph +import kotlinx.android.synthetic.main.overview_graphs_layout.overview_chartMenuButton +import kotlinx.android.synthetic.main.overview_graphs_layout.overview_iobcalculationprogess +import kotlinx.android.synthetic.main.overview_graphs_layout.overview_iobgraph +import kotlinx.android.synthetic.main.overview_info_layout.* +import kotlinx.android.synthetic.main.overview_info_layout.overview_arrow +import kotlinx.android.synthetic.main.overview_info_layout.overview_basebasal +import kotlinx.android.synthetic.main.overview_info_layout.overview_bg +import kotlinx.android.synthetic.main.overview_info_layout.overview_cob +import kotlinx.android.synthetic.main.overview_info_layout.overview_extendedbolus +import kotlinx.android.synthetic.main.overview_info_layout.overview_iob +import kotlinx.android.synthetic.main.overview_info_layout.overview_sensitivity +import kotlinx.android.synthetic.main.overview_loop_pumpstatus_layout.* +import kotlinx.android.synthetic.main.overview_statuslights_layout.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -140,6 +133,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList @Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var overviewMenus: OverviewMenus + @Inject lateinit var skinProvider: SkinProvider private val disposable = CompositeDisposable() @@ -170,19 +164,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList smallHeight = screenHeight <= Constants.SMALL_HEIGHT val landscape = screenHeight < screenWidth - return when { - resourceHelper.gb(R.bool.isTablet) && Config.NSCLIENT -> - inflater.inflate(R.layout.overview_fragment_nsclient_tablet, container, false) - - Config.NSCLIENT -> - inflater.inflate(R.layout.overview_fragment_nsclient, container, false) - - smallHeight || landscape -> - inflater.inflate(R.layout.overview_fragment_landscape, container, false) - - else -> - inflater.inflate(R.layout.overview_fragment, container, false) - } + return inflater.inflate(skinProvider.activeSkin().overviewLayout(landscape, resourceHelper.gb(R.bool.isTablet), smallHeight), container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -226,7 +208,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList super.onPause() disposable.clear() loopHandler.removeCallbacksAndMessages(null) - overview_apsmode?.let { unregisterForContextMenu(it) } + overview_apsmode_llayout?.let { unregisterForContextMenu(it) } overview_activeprofile?.let { unregisterForContextMenu(it) } overview_temptarget?.let { unregisterForContextMenu(it) } } @@ -300,7 +282,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } loopHandler.postDelayed(refreshLoop, 60 * 1000L) - overview_apsmode?.let { registerForContextMenu(overview_apsmode) } + overview_apsmode_llayout?.let { registerForContextMenu(overview_apsmode) } overview_activeprofile?.let { registerForContextMenu(it) } overview_temptarget?.let { registerForContextMenu(it) } updateGUI("onResume") @@ -312,26 +294,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } override fun onContextItemSelected(item: MenuItem): Boolean { - val manager = fragmentManager - return if (manager != null && overviewMenus.onContextItemSelected(item, manager)) true else super.onContextItemSelected(item) + return if (overviewMenus.onContextItemSelected(item, childFragmentManager)) true else super.onContextItemSelected(item) } override fun onClick(v: View) { - val manager = fragmentManager ?: return // try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days // https://stackoverflow.com/questions/14860239/checking-if-state-is-saved-before-committing-a-fragmenttransaction - if (manager.isStateSaved) return + if (childFragmentManager.isStateSaved) return activity?.let { activity -> when (v.id) { - R.id.overview_treatmentbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { TreatmentDialog().show(manager, "Overview") }) - R.id.overview_wizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { WizardDialog().show(manager, "Overview") }) - R.id.overview_insulinbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { InsulinDialog().show(manager, "Overview") }) - R.id.overview_quickwizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { onClickQuickWizard() }) - R.id.overview_carbsbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { CarbsDialog().show(manager, "Overview") }) - - R.id.overview_pumpstatus -> { - if (activePlugin.activePump.isSuspended || !activePlugin.activePump.isInitialized) commandQueue.readStatus("RefreshClicked", null) - } + R.id.overview_treatmentbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { TreatmentDialog().show(childFragmentManager, "Overview") })) + R.id.overview_wizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { WizardDialog().show(childFragmentManager, "Overview") })) + R.id.overview_insulinbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { InsulinDialog().show(childFragmentManager, "Overview") })) + R.id.overview_quickwizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { onClickQuickWizard() })) + R.id.overview_carbsbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable(Runnable { CarbsDialog().show(childFragmentManager, "Overview") })) R.id.overview_cgmbutton -> { if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) @@ -346,7 +322,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList R.id.overview_calibrationbutton -> { if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) { - CalibrationDialog().show(manager, "CalibrationDialog") + CalibrationDialog().show(childFragmentManager, "CalibrationDialog") } else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) { try { dexcomPlugin.findDexcomPackageName()?.let { @@ -570,9 +546,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList val glucoseStatus = GlucoseStatus(injector).glucoseStatusData if (glucoseStatus != null) { - overview_delta?.text = "Δ ${Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)} $units" + overview_delta?.text = "Δ ${Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)}" overview_deltashort?.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) - overview_avgdelta?.text = "øΔ15m: ${Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units)}\nøΔ40m: ${Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)}" + overview_avgdelta?.text = "Δ15m: ${Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units)}\nΔ40m: ${Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)}" } else { overview_delta?.text = "Δ " + resourceHelper.gs(R.string.notavailable) overview_deltashort?.text = "---" @@ -597,53 +573,72 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList if (Config.APS && pump.pumpDescription.isTempBasalCapable) { overview_apsmode?.visibility = View.VISIBLE when { - loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuperBolus -> { - overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopsuperbolusfor), loopPlugin.minutesToEndOfSuspend()) - overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + loopPlugin.isEnabled() && loopPlugin.isSuperBolus -> { + overview_apsmode.setImageResource(R.drawable.loop_superbolus) + overview_apsmode_text?.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) + //overview_apsmode_text?.text = String.format(resourceHelper.gs(R.string.loopsuperbolusfor), loopPlugin.minutesToEndOfSuspend()) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) } - loopPlugin.isDisconnected -> { - overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopdisconnectedfor), loopPlugin.minutesToEndOfSuspend()) - overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) + loopPlugin.isDisconnected -> { + overview_apsmode.setImageResource(R.drawable.loop_disconnected) + overview_apsmode_text?.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) +// overview_apsmode_text?.text = String.format(resourceHelper.gs(R.string.loopdisconnectedfor), loopPlugin.minutesToEndOfSuspend()) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) } - loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuspended -> { - overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) - overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + loopPlugin.isEnabled() && loopPlugin.isSuspended -> { + overview_apsmode.setImageResource(R.drawable.loop_paused) + overview_apsmode_text?.text = DateUtil.age(loopPlugin.minutesToEndOfSuspend() * 60000L, true, resourceHelper) +// overview_apsmode_text?.text = String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) } - pump.isSuspended -> { - overview_apsmode?.text = resourceHelper.gs(R.string.pumpsuspended) - overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + pump.isSuspended -> { + overview_apsmode.setImageResource(R.drawable.loop_paused) + overview_apsmode_text?.text = "" +// overview_apsmode_text?.text = resourceHelper.gs(R.string.pumpsuspended) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) } - loopPlugin.isEnabled(PluginType.LOOP) -> { - val isLGS = loopPlugin.isLGS - overview_apsmode?.text = - if (closedLoopEnabled.value()) - if (isLGS) - resourceHelper.gs(R.string.lgs) - else - resourceHelper.gs(R.string.closedloop) - else - resourceHelper.gs(R.string.openloop) - - overview_apsmode?.setBackgroundColor(if (isLGS) resourceHelper.gc(R.color.ribbonUnusual) else resourceHelper.gc(R.color.ribbonDefault)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + loopPlugin.isEnabled() && closedLoopEnabled.value() && loopPlugin.isLGS -> { + overview_apsmode.setImageResource(R.drawable.loop_lgs) + overview_apsmode_text?.text = "" +// overview_apsmode_text?.text = resourceHelper.gs(R.string.closedloop) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) } - else -> { - overview_apsmode?.text = resourceHelper.gs(R.string.disabledloop) - overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) - overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) + loopPlugin.isEnabled() && closedLoopEnabled.value() -> { + overview_apsmode.setImageResource(R.drawable.loop_closed) + overview_apsmode_text?.text = "" +// overview_apsmode_text?.text = resourceHelper.gs(R.string.closedloop) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + } + + loopPlugin.isEnabled() && !closedLoopEnabled.value() -> { + overview_apsmode.setImageResource(R.drawable.loop_open) + overview_apsmode_text?.text = "" +// overview_apsmode_text?.text = resourceHelper.gs(R.string.openloop) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + } + + else -> { + overview_apsmode.setImageResource(R.drawable.loop_disabled) + overview_apsmode_text?.text = "" +// overview_apsmode_text?.text = resourceHelper.gs(R.string.disabledloop) +// overview_apsmode_text?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) +// overview_apsmode_text?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) } } } else { - overview_apsmode?.visibility = View.GONE + overview_apsmode_text?.visibility = View.GONE } // temp target @@ -660,9 +655,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList // Basal, TBR val activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) - overview_basebasal?.text = activeTemp?.let { if (resourceHelper.shortTextMode()) "T: " + activeTemp.toStringVeryShort() else activeTemp.toStringFull() } + overview_basebasal?.text = activeTemp?.let { if (resourceHelper.shortTextMode()) "T:" + activeTemp.toStringVeryShort() else activeTemp.toStringFull() } ?: resourceHelper.gs(R.string.pump_basebasalrate, profile.basal) - overview_basebasal?.setOnClickListener { + overview_basal_llayout?.setOnClickListener { var fullText = "${resourceHelper.gs(R.string.pump_basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.basal)}" if (activeTemp != null) fullText += "\n" + resourceHelper.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull() @@ -672,6 +667,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList } overview_basebasal?.setTextColor(activeTemp?.let { resourceHelper.gc(R.color.basal) } ?: resourceHelper.gc(R.color.defaulttextcolor)) + overview_basebasal_icon.setImageResource(if (activeTemp != null) R.drawable.icon_cp_basal_start else R.drawable.icon_cp_basal_end) // Extended bolus val extendedBolus = treatmentsPlugin.getExtendedBolusFromHistory(System.currentTimeMillis()) @@ -684,7 +680,9 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), extendedBolus.toString()) } } + overview_extended_llayout?.visibility = (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() + // Active profile overview_activeprofile?.text = profileFunction.getProfileNameWithDuration() if (profile.percentage != 100 || profile.timeshift != 0) { overview_activeprofile?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) @@ -702,23 +700,15 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() overview_iob?.text = when { - resourceHelper.shortTextMode() -> { + resourceHelper.shortTextMode() -> resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) - } - resourceHelper.gb(R.bool.isTablet) -> { - resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + " (" + - resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + - resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + ")" - } - - else -> { + else -> resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + " (" + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "/" + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + ")" - } } - overview_iob?.setOnClickListener { + overview_iob_llayout?.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.iob), resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + @@ -736,7 +726,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList var cobText: String = resourceHelper.gs(R.string.value_unavailable_short) val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Overview COB") if (cobInfo.displayCob != null) { - cobText = DecimalFormatter.to0Decimal(cobInfo.displayCob) + cobText = resourceHelper.gs(R.string.format_carbs, cobInfo.displayCob.toInt()) if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")" } overview_cob?.text = cobText @@ -757,9 +747,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList overview_uploader?.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } } // Sensitivity - iobCobCalculatorPlugin.getLastAutosensData("Overview")?.let { autosensData -> - overview_sensitivity?.text = String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) - } + overview_sensitivity?.text = + iobCobCalculatorPlugin.getLastAutosensData("Overview")?.let { autosensData -> + String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) + } ?: "" // ****** GRAPH ******* GlobalScope.launch(Dispatchers.Main) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt index 3acf960195..f26bacf8ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -178,7 +178,7 @@ class LocalProfileFragment : DaggerFragment() { localprofile_profileswitch.setOnClickListener { // TODO: select in dialog localProfilePlugin.currentProfileIndex - fragmentManager?.let { ProfileSwitchDialog().show(it, "NewNSTreatmentDialog") } + ProfileSwitchDialog().show(childFragmentManager, "NewNSTreatmentDialog") } localprofile_reset.setOnClickListener { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt index 60420d6d70..9a8477ae2d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/danaR/DanaRFragment.kt @@ -71,21 +71,19 @@ class DanaRFragment : DaggerFragment() { danar_history.setOnClickListener { startActivity(Intent(context, DanaRHistoryActivity::class.java)) } danar_viewprofile.setOnClickListener { - fragmentManager?.let { fragmentManager -> - val profile = danaRPump.createConvertedProfile()?.getDefaultProfile() - ?: return@let - val profileName = danaRPump.createConvertedProfile()?.getDefaultProfileName() - ?: return@let - val args = Bundle() - args.putLong("time", DateUtil.now()) - args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) - args.putString("customProfile", profile.data.toString()) - args.putString("customProfileUnits", profile.units) - args.putString("customProfileName", profileName) - val pvd = ProfileViewerDialog() - pvd.arguments = args - pvd.show(fragmentManager, "ProfileViewDialog") - } + val profile = danaRPump.createConvertedProfile()?.getDefaultProfile() + ?: return@setOnClickListener + val profileName = danaRPump.createConvertedProfile()?.getDefaultProfileName() + ?: return@setOnClickListener + val args = Bundle() + args.putLong("time", DateUtil.now()) + args.putInt("mode", ProfileViewerDialog.Mode.CUSTOM_PROFILE.ordinal) + args.putString("customProfile", profile.data.toString()) + args.putString("customProfileUnits", profile.units) + args.putString("customProfileName", profileName) + val pvd = ProfileViewerDialog() + pvd.arguments = args + pvd.show(childFragmentManager, "ProfileViewDialog") } danar_stats.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) } danar_user_options.setOnClickListener { startActivity(Intent(context, DanaRUserOptionsActivity::class.java)) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodFragment.kt index fce29225d4..6dba697009 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodFragment.kt @@ -176,7 +176,7 @@ class OmnipodFragment : DaggerFragment() { disposable += rxBus .toObservable(EventPreferenceChange::class.java) .observeOn(Schedulers.io()) - .subscribe({ event -> + .subscribe({ setVisibilityOfPodDebugButton() }, { fabricPrivacy.logException(it) }) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java index dd6d7295b7..466a6be10f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/OmnipodPumpPlugin.java @@ -619,15 +619,7 @@ public class OmnipodPumpPlugin extends PumpPluginAbstract implements OmnipodPump if (omnipodUtil.getPodSessionState() != null) { podSessionState = omnipodUtil.getPodSessionState(); } else { - String podState = sp.getString(OmnipodConst.Prefs.PodState, ""); - - aapsLogger.info(LTag.PUMP, "PodSessionState-SP: loaded from SharedPreferences: " + podState); - - if (StringUtils.isNotEmpty(podState)) { - podSessionState = omnipodUtil.getGsonInstance().fromJson(podState, PodSessionState.class); - podSessionState.injectDaggerClass(injector); - omnipodUtil.setPodSessionState(podSessionState); - } + podSessionState = omnipodUtil.loadSessionState(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodUtil.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodUtil.java index a0d6203309..f002989e3c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/util/OmnipodUtil.java @@ -8,6 +8,7 @@ import com.google.gson.JsonDeserializer; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializer; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.ISODateTimeFormat; @@ -15,10 +16,12 @@ import org.joda.time.format.ISODateTimeFormat; import javax.inject.Inject; import javax.inject.Singleton; +import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.logging.AAPSLogger; +import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.pump.common.defs.PumpType; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil; @@ -46,6 +49,7 @@ public class OmnipodUtil { private final OmnipodPumpStatus omnipodPumpStatus; private final ActivePluginProvider activePlugins; private final SP sp; + private final HasAndroidInjector injector; private boolean lowLevelDebug = true; private OmnipodCommandType currentCommand; @@ -63,7 +67,8 @@ public class OmnipodUtil { RileyLinkUtil rileyLinkUtil, OmnipodPumpStatus omnipodPumpStatus, SP sp, - ActivePluginProvider activePlugins + ActivePluginProvider activePlugins, + HasAndroidInjector injector ) { this.aapsLogger = aapsLogger; this.rxBus = rxBus; @@ -71,6 +76,7 @@ public class OmnipodUtil { this.omnipodPumpStatus = omnipodPumpStatus; this.sp = sp; this.activePlugins = activePlugins; + this.injector = injector; } @@ -223,4 +229,20 @@ public class OmnipodUtil { public SP getSp() { return this.sp; } + + public PodSessionState loadSessionState() { + String podState = sp.getString(OmnipodConst.Prefs.PodState, ""); + + aapsLogger.info(LTag.PUMP, "PodSessionState-SP: loaded from SharedPreferences: " + podState); + + if (StringUtils.isNotEmpty(podState)) { + PodSessionState podSessionState = gsonInstance.fromJson(podState, PodSessionState.class); + podSessionState.injectDaggerClass(injector); + setPodSessionState(podSessionState); + + return podSessionState; + } + + return null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt index a6b1cdf296..7e130ceab9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsBolusFragment.kt @@ -145,12 +145,10 @@ class TreatmentsBolusFragment : DaggerFragment() { init { calculation.setOnClickListener { val treatment = it.tag as Treatment - fragmentManager?.let { fragmentManager -> - if (treatment.getBoluscalc() != null) { - val wizardDialog = WizardInfoDialog() - wizardDialog.setData(treatment.getBoluscalc()!!) - wizardDialog.show(fragmentManager, "WizardInfoDialog") - } + if (treatment.getBoluscalc() != null) { + val wizardDialog = WizardInfoDialog() + wizardDialog.setData(treatment.getBoluscalc()!!) + wizardDialog.show(childFragmentManager, "WizardInfoDialog") } } calculation.paintFlags = calculation.paintFlags or Paint.UNDERLINE_TEXT_FLAG diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt index 10bdf42de5..d45d547ef0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/fragments/TreatmentsProfileSwitchFragment.kt @@ -156,7 +156,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal) val pvd = ProfileViewerDialog() pvd.arguments = args - fragmentManager?.let { pvd.show(it, "ProfileViewDialog") } + pvd.show(childFragmentManager, "ProfileViewDialog") } } } diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt new file mode 100644 index 0000000000..3bc092dfc2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinButtonsOn.kt @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.skins + +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.R +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SkinButtonsOn @Inject constructor() : SkinInterface { + + override val description: Int get() = R.string.buttonson_desrciption + + override fun overviewLayout(isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean): Int = + when { + Config.NSCLIENT && isTablet -> R.layout.overview_fragment_nsclient_tablet + Config.NSCLIENT -> R.layout.overview_fragment_nsclient + else -> R.layout.overview_fragment + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt new file mode 100644 index 0000000000..15e69e9ad9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinClassic.kt @@ -0,0 +1,21 @@ +package info.nightscout.androidaps.skins + +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.R +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SkinClassic @Inject constructor(): SkinInterface { + + override val description: Int get() = R.string.classic_desrciption + + override fun overviewLayout(isLandscape: Boolean, isTablet: Boolean, isSmallHeight: Boolean): Int = + when { + Config.NSCLIENT && isTablet -> R.layout.overview_fragment_nsclient_tablet + Config.NSCLIENT -> R.layout.overview_fragment_nsclient + isSmallHeight || isLandscape -> R.layout.overview_fragment_landscape + else -> R.layout.overview_fragment + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinInterface.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinInterface.kt new file mode 100644 index 0000000000..6a9d055a13 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinInterface.kt @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.skins + +import androidx.annotation.LayoutRes +import androidx.annotation.StringRes + +interface SkinInterface { + @get:StringRes val description : Int + + @LayoutRes fun overviewLayout(isLandscape : Boolean, isTablet : Boolean, isSmallHeight : Boolean): Int +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinListPreference.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinListPreference.kt new file mode 100644 index 0000000000..977f19c268 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinListPreference.kt @@ -0,0 +1,29 @@ +package info.nightscout.androidaps.skins + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.ListPreference +import dagger.android.HasAndroidInjector +import java.util.* +import javax.inject.Inject + +class SkinListPreference(context: Context, attrs: AttributeSet?) + : ListPreference(context, attrs) { + + @Inject lateinit var skinProvider: SkinProvider + + constructor(context: Context) : this(context, null) + + init { + (context.applicationContext as HasAndroidInjector).androidInjector().inject(this) + val entries = Vector() + val values = Vector() + + for (skin in skinProvider.list) { + values.addElement(skin.javaClass.name) + entries.addElement(context.getString(skin.description)) + } + entryValues = values.toTypedArray() + setEntries(entries.toTypedArray()) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/skins/SkinProvider.kt b/app/src/main/java/info/nightscout/androidaps/skins/SkinProvider.kt new file mode 100644 index 0000000000..5ea6a97501 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/skins/SkinProvider.kt @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.skins + +import info.nightscout.androidaps.R +import info.nightscout.androidaps.dependencyInjection.SkinsModule +import info.nightscout.androidaps.utils.sharedPreferences.SP +import okhttp3.internal.toImmutableMap +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SkinProvider @Inject constructor( + val sp: SP, + @SkinsModule.Skin val allSkins: Map<@JvmSuppressWildcards Int, @JvmSuppressWildcards SkinInterface> +) { + + fun activeSkin(): SkinInterface = + list.firstOrNull { it.javaClass.name == sp.getString(R.string.key_skin, "") } + ?: list.first() + + val list: List + get() = allSkins.toImmutableMap().toList().sortedBy { it.first }.map { it.second } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java index 82fabc52b7..f058896f19 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/DateUtil.java @@ -11,11 +11,18 @@ import java.text.DateFormat; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; +import java.util.EnumSet; import java.util.GregorianCalendar; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -270,6 +277,42 @@ public class DateUtil { return TimeZone.getDefault().getOffset(timestamp) / 60000; } + //Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0} + public static Map computeDiff(long date1, long date2) { + long diffInMillies = date2 - date1; + List units = new ArrayList<>(EnumSet.allOf(TimeUnit.class)); + Collections.reverse(units); + Map result = new LinkedHashMap<>(); + long milliesRest = diffInMillies; + for (TimeUnit unit : units) { + long diff = unit.convert(milliesRest, TimeUnit.MILLISECONDS); + long diffInMilliesForUnit = unit.toMillis(diff); + milliesRest = milliesRest - diffInMilliesForUnit; + result.put(unit, diff); + } + return result; + } + + public static String age(long milliseconds, boolean useShortText, ResourceHelper resourceHelper) { + Map diff = computeDiff(0L, milliseconds); + + String days = " " + resourceHelper.gs(R.string.days) + " "; + String hours = " " + resourceHelper.gs(R.string.hours) + " "; + String minutes = " " + resourceHelper.gs(R.string.unit_minutes) + " "; + + if (useShortText) { + days = resourceHelper.gs(R.string.shortday); + hours = resourceHelper.gs(R.string.shorthour); + minutes = resourceHelper.gs(R.string.shortminute); + } + + String result = ""; + if (diff.get(TimeUnit.DAYS) > 0) result += diff.get(TimeUnit.DAYS) + days; + if (diff.get(TimeUnit.HOURS) > 0) result += diff.get(TimeUnit.HOURS) + hours; + if (diff.get(TimeUnit.DAYS) == 0) result += diff.get(TimeUnit.MINUTES) + minutes; + return result; + } + public static String niceTimeScalar(long t, ResourceHelper resourceHelper) { String unit = resourceHelper.gs(R.string.unit_second); t = t / 1000; diff --git a/app/src/main/res/drawable-hdpi/bad.png b/app/src/main/res/drawable-hdpi/bad.png new file mode 100644 index 0000000000..139bf1ddee Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bad.png differ diff --git a/app/src/main/res/drawable-hdpi/good.png b/app/src/main/res/drawable-hdpi/good.png new file mode 100644 index 0000000000..05723ff4ea Binary files /dev/null and b/app/src/main/res/drawable-hdpi/good.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_closed.png b/app/src/main/res/drawable-hdpi/loop_closed.png new file mode 100644 index 0000000000..066dd4c26a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_closed.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_disabled.png b/app/src/main/res/drawable-hdpi/loop_disabled.png new file mode 100644 index 0000000000..268a9bc76b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_disabled.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_disconnected.png b/app/src/main/res/drawable-hdpi/loop_disconnected.png new file mode 100644 index 0000000000..bfff44ea1e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_disconnected.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_lgs.png b/app/src/main/res/drawable-hdpi/loop_lgs.png new file mode 100644 index 0000000000..e45fda615b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_lgs.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_open.png b/app/src/main/res/drawable-hdpi/loop_open.png new file mode 100644 index 0000000000..cdefc55ce8 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_open.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_pause.png b/app/src/main/res/drawable-hdpi/loop_pause.png new file mode 100644 index 0000000000..bb97008a07 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_pause.png differ diff --git a/app/src/main/res/drawable-hdpi/loop_superbolus.png b/app/src/main/res/drawable-hdpi/loop_superbolus.png new file mode 100644 index 0000000000..d9cec8f047 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/loop_superbolus.png differ diff --git a/app/src/main/res/drawable-hdpi/soso.png b/app/src/main/res/drawable-hdpi/soso.png new file mode 100644 index 0000000000..f7a9be577e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/soso.png differ diff --git a/app/src/main/res/drawable-mdpi/bad.png b/app/src/main/res/drawable-mdpi/bad.png new file mode 100644 index 0000000000..9210154195 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/bad.png differ diff --git a/app/src/main/res/drawable-mdpi/good.png b/app/src/main/res/drawable-mdpi/good.png new file mode 100644 index 0000000000..a15f82c2b2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/good.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_closed.png b/app/src/main/res/drawable-mdpi/loop_closed.png new file mode 100644 index 0000000000..ba4283035b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_closed.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_disabled.png b/app/src/main/res/drawable-mdpi/loop_disabled.png new file mode 100644 index 0000000000..e4650ec0ac Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_disabled.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_disconnected.png b/app/src/main/res/drawable-mdpi/loop_disconnected.png new file mode 100644 index 0000000000..0bc9949349 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_disconnected.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_lgs.png b/app/src/main/res/drawable-mdpi/loop_lgs.png new file mode 100644 index 0000000000..a49229d4d8 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_lgs.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_open.png b/app/src/main/res/drawable-mdpi/loop_open.png new file mode 100644 index 0000000000..f82346f739 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_open.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_paused.png b/app/src/main/res/drawable-mdpi/loop_paused.png new file mode 100644 index 0000000000..7258615f66 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_paused.png differ diff --git a/app/src/main/res/drawable-mdpi/loop_superbolus.png b/app/src/main/res/drawable-mdpi/loop_superbolus.png new file mode 100644 index 0000000000..34a61c1bf3 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/loop_superbolus.png differ diff --git a/app/src/main/res/drawable-mdpi/soso.png b/app/src/main/res/drawable-mdpi/soso.png new file mode 100644 index 0000000000..91d9bd7ddf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/soso.png differ diff --git a/app/src/main/res/drawable-xhdpi/bad.png b/app/src/main/res/drawable-xhdpi/bad.png new file mode 100644 index 0000000000..46dca7155f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/bad.png differ diff --git a/app/src/main/res/drawable-xhdpi/good.png b/app/src/main/res/drawable-xhdpi/good.png new file mode 100644 index 0000000000..08946fed3e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/good.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_closed.png b/app/src/main/res/drawable-xhdpi/loop_closed.png new file mode 100644 index 0000000000..f7548add59 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_closed.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_disabled.png b/app/src/main/res/drawable-xhdpi/loop_disabled.png new file mode 100644 index 0000000000..0f7db5d681 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_disabled.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_disconnected.png b/app/src/main/res/drawable-xhdpi/loop_disconnected.png new file mode 100644 index 0000000000..7db5047b9f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_disconnected.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_lgs.png b/app/src/main/res/drawable-xhdpi/loop_lgs.png new file mode 100644 index 0000000000..baa073ac8c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_lgs.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_open.png b/app/src/main/res/drawable-xhdpi/loop_open.png new file mode 100644 index 0000000000..55726849d0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_open.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_paused.png b/app/src/main/res/drawable-xhdpi/loop_paused.png new file mode 100644 index 0000000000..399ec449bc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_paused.png differ diff --git a/app/src/main/res/drawable-xhdpi/loop_superbolus.png b/app/src/main/res/drawable-xhdpi/loop_superbolus.png new file mode 100644 index 0000000000..8bcf96248b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/loop_superbolus.png differ diff --git a/app/src/main/res/drawable-xhdpi/soso.png b/app/src/main/res/drawable-xhdpi/soso.png new file mode 100644 index 0000000000..8e19db274b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/soso.png differ diff --git a/app/src/main/res/drawable-xxhdpi/bad.png b/app/src/main/res/drawable-xxhdpi/bad.png new file mode 100644 index 0000000000..b7a3201d60 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bad.png differ diff --git a/app/src/main/res/drawable-xxhdpi/good.png b/app/src/main/res/drawable-xxhdpi/good.png new file mode 100644 index 0000000000..9f5c243b04 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/good.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_closed.png b/app/src/main/res/drawable-xxhdpi/loop_closed.png new file mode 100644 index 0000000000..5418ef1e7e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_closed.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_disabled.png b/app/src/main/res/drawable-xxhdpi/loop_disabled.png new file mode 100644 index 0000000000..580dc5a5f5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_disabled.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_disconnected.png b/app/src/main/res/drawable-xxhdpi/loop_disconnected.png new file mode 100644 index 0000000000..6e991f8ebf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_disconnected.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_lgs.png b/app/src/main/res/drawable-xxhdpi/loop_lgs.png new file mode 100644 index 0000000000..86b4910f33 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_lgs.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_open.png b/app/src/main/res/drawable-xxhdpi/loop_open.png new file mode 100644 index 0000000000..1d35a9c987 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_open.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_paused.png b/app/src/main/res/drawable-xxhdpi/loop_paused.png new file mode 100644 index 0000000000..6db561b858 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_paused.png differ diff --git a/app/src/main/res/drawable-xxhdpi/loop_superbolus.png b/app/src/main/res/drawable-xxhdpi/loop_superbolus.png new file mode 100644 index 0000000000..c5187cf95d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/loop_superbolus.png differ diff --git a/app/src/main/res/drawable-xxhdpi/soso.png b/app/src/main/res/drawable-xxhdpi/soso.png new file mode 100644 index 0000000000..6caac0cc05 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/soso.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/bad.png b/app/src/main/res/drawable-xxxhdpi/bad.png new file mode 100644 index 0000000000..484bd8fd0b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/bad.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/good.png b/app/src/main/res/drawable-xxxhdpi/good.png new file mode 100644 index 0000000000..eb81a2b978 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/good.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_closed.png b/app/src/main/res/drawable-xxxhdpi/loop_closed.png new file mode 100644 index 0000000000..bd78239462 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_closed.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_disabled.png b/app/src/main/res/drawable-xxxhdpi/loop_disabled.png new file mode 100644 index 0000000000..824d6b9db1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_disabled.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_disconnected.png b/app/src/main/res/drawable-xxxhdpi/loop_disconnected.png new file mode 100644 index 0000000000..9c451cca88 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_disconnected.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_lgs.png b/app/src/main/res/drawable-xxxhdpi/loop_lgs.png new file mode 100644 index 0000000000..80848ff60a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_lgs.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_open.png b/app/src/main/res/drawable-xxxhdpi/loop_open.png new file mode 100644 index 0000000000..f010399a53 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_open.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_paused.png b/app/src/main/res/drawable-xxxhdpi/loop_paused.png new file mode 100644 index 0000000000..36f9bcb5a5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_paused.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/loop_superbolus.png b/app/src/main/res/drawable-xxxhdpi/loop_superbolus.png new file mode 100644 index 0000000000..018bf70188 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/loop_superbolus.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/soso.png b/app/src/main/res/drawable-xxxhdpi/soso.png new file mode 100644 index 0000000000..50b8f3fa5c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/soso.png differ diff --git a/app/src/main/res/drawable/ic_swap_vert_black_48dp_green.xml b/app/src/main/res/drawable/ic_swap_vert_black_48dp_green.xml new file mode 100644 index 0000000000..f78eaede2c --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_vert_black_48dp_green.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/maintenance_importlist_activity.xml b/app/src/main/res/layout/maintenance_importlist_activity.xml new file mode 100644 index 0000000000..0bdd43b927 --- /dev/null +++ b/app/src/main/res/layout/maintenance_importlist_activity.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/maintenance_importlist_item.xml b/app/src/main/res/layout/maintenance_importlist_item.xml new file mode 100644 index 0000000000..8c2cfc0557 --- /dev/null +++ b/app/src/main/res/layout/maintenance_importlist_item.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/overview_buttons_layout.xml b/app/src/main/res/layout/overview_buttons_layout.xml new file mode 100644 index 0000000000..5c97104159 --- /dev/null +++ b/app/src/main/res/layout/overview_buttons_layout.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index f122376c2f..747f53798e 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -1,628 +1,39 @@ - + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="0dp"> - - - - - + android:layout_height="wrap_content" /> - + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/overview_fragment_landscape.xml b/app/src/main/res/layout/overview_fragment_landscape.xml index 471634ea8a..2d6250384d 100644 --- a/app/src/main/res/layout/overview_fragment_landscape.xml +++ b/app/src/main/res/layout/overview_fragment_landscape.xml @@ -1,628 +1,40 @@ - + android:layout_weight="1" + tools:ignore="UselessParent"> - - - - - + android:layout_height="wrap_content" /> - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + diff --git a/app/src/main/res/layout/overview_fragment_nsclient.xml b/app/src/main/res/layout/overview_fragment_nsclient.xml index 504e88c789..c4ce063e29 100644 --- a/app/src/main/res/layout/overview_fragment_nsclient.xml +++ b/app/src/main/res/layout/overview_fragment_nsclient.xml @@ -1,466 +1,32 @@ - + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="0dp"> - - - - - + android:layout_height="wrap_content" /> - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + android:textSize="16sp" /> + android:textSize="16sp" /> + android:textSize="16sp" /> - + - - - - - - - - + - + - - - - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml b/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml index 585a72159a..5c4340eea6 100644 --- a/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml +++ b/app/src/main/res/layout/overview_fragment_nsclient_tablet.xml @@ -27,82 +27,7 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/overview_info_layout.xml b/app/src/main/res/layout/overview_info_layout.xml new file mode 100644 index 0000000000..9f2cdd8c2f --- /dev/null +++ b/app/src/main/res/layout/overview_info_layout.xml @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/overview_loop_pumpstatus_layout.xml b/app/src/main/res/layout/overview_loop_pumpstatus_layout.xml new file mode 100644 index 0000000000..98c0d0bd62 --- /dev/null +++ b/app/src/main/res/layout/overview_loop_pumpstatus_layout.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/overview_notification_item.xml b/app/src/main/res/layout/overview_notification_item.xml index 0c0d36ad25..03b5688fc3 100644 --- a/app/src/main/res/layout/overview_notification_item.xml +++ b/app/src/main/res/layout/overview_notification_item.xml @@ -1,7 +1,6 @@ - + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + +