Merge branch 'dev' into biometric

This commit is contained in:
Milos Kozak 2019-12-08 16:43:07 +01:00
commit 9d787505cf
434 changed files with 13034 additions and 4385 deletions

48
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,48 @@
This document speciffy hints and good practices for source code contributions.
AndroidAPS is community effort and all contributions are welcome! If you wish help us improving AndroidAPS - please read and try to adhere to
this guidelines, to make the development and process of change aproval as smooth as possible :)
General rules
=============
* There are plenty of ways you can help, some of them are listed on wiki:
https://androidaps.readthedocs.io/en/latest/EN/Getting-Started/How-can-I-help.html
* If you wish to help with documentation or translating:
https://androidaps.readthedocs.io/en/latest/EN/translations.html
Development guidelines
======================
Coding convetions
-----------------
1. Use Android Studio with default indents (4 chars, use spaces)
2. Use autoformat feature CTRL-ALT-L in every changed file before commit
Commiting Changes / Pull Requests
---------------------------------
1. Make fork of repository on github
2. Create separate branch for each feature, branch from most recent dev
3. Commit all changes to your fork
4. When ready, rebase on top of dev and make pull request to main repo
Naming Conventions for Pull Requests / Branches
-----------------------------------------------
TODO
Translations
------------
* If possible, always use Android translation mechanism (with strings.xml and @strings/id) instead of hardcoded texts
* Provide only English strings - all other languages will be crowd translated via Crowdn https://translations.androidaps.org/
Hints
-----
* Start small, it is easier to review smaller changes that affect fewer parts of code
* Take a look into Issues list (https://github.com/MilosKozak/AndroidAPS/issues) - maybe there is somthing you can fix or implement
* For new features, make sure there is Issue to track progress and have on-topic discussion
* Reach out to community, discuss idea on Gitter (https://gitter.im/MilosKozak/AndroidAPS)
* Speak with other developers to minimise merge conflicts. Find out who worked, working or plan to work on speciffic issue or part of app

View file

@ -109,7 +109,7 @@ android {
targetSdkVersion 28
multiDexEnabled true
versionCode 1500
version "2.5.0-dev"
version "2.6-dev"
buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -225,8 +225,8 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.android.gms:play-services-wearable:17.0.0'
implementation 'com.google.firebase:firebase-core:17.2.0'
implementation("com.crashlytics.sdk.android:crashlytics:2.9.9@aar") {
implementation 'com.google.firebase:firebase-core:17.2.1'
implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') {
transitive = true;
}
@ -234,12 +234,12 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.biometric:biometric:1.0.0-rc02'
implementation 'androidx.biometric:biometric:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.percentlayout:percentlayout:1.0.0'
implementation "com.wdullaer:materialdatetimepicker:2.3.0"
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
@ -248,11 +248,11 @@ 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.7"
implementation "org.slf4j:slf4j-api:1.7.21"
implementation "org.apache.commons:commons-lang3:3.9"
implementation "org.slf4j:slf4j-api:1.7.29"
// Graphview cannot be upgraded
implementation "com.jjoe64:graphview:4.0.1"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.1.1"
implementation "com.joanzapata.iconify:android-iconify-fontawesome:2.2.2"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation(name: "android-edittext-validator-v1.3.4-mod", ext: "aar")
implementation 'com.madgag.spongycastle:core:1.58.0.0'
@ -264,24 +264,23 @@ dependencies {
// excluding org.json which is provided by Android
exclude group: "org.json", module: "json"
}
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.google.code.gson:gson:2.8.6"
implementation "com.google.guava:guava:24.1-jre"
implementation "net.danlew:android.joda:2.9.9.1"
implementation "uk.com.robust-it:cloning:1.9.9"
implementation "net.danlew:android.joda:2.10.3"
implementation 'org.mozilla:rhino:1.7.7.2'
implementation 'org.mozilla:rhino:1.7.11'
implementation 'com.github.DavidProdinger:weekdays-selector:1.1.0'
testImplementation "junit:junit:4.12"
testImplementation "org.json:json:20140107"
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.9.9"
testImplementation "joda-time:joda-time:2.10.5"
testImplementation("com.google.truth:truth:0.39") {
exclude group: "com.google.guava", module: "guava"
}
@ -301,11 +300,11 @@ dependencies {
// new for tidepool
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation "com.squareup.retrofit2:retrofit:2.4.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
implementation "com.squareup.retrofit2:converter-gson:2.4.0"
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation "com.squareup.retrofit2:retrofit:2.6.2"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.6.2"
implementation "com.squareup.retrofit2:converter-gson:2.6.2"
}

View file

@ -37,12 +37,10 @@ import com.joanzapata.iconify.fonts.FontAwesomeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.activities.AgreementActivity;
import info.nightscout.androidaps.activities.HistoryBrowseActivity;
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity;
import info.nightscout.androidaps.activities.PreferencesActivity;
import info.nightscout.androidaps.activities.SingleFragmentActivity;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRebuildTabs;
@ -51,7 +49,6 @@ import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtilsKt;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus;
import info.nightscout.androidaps.setupwizard.SetupWizardActivity;
@ -59,7 +56,6 @@ import info.nightscout.androidaps.tabs.TabPageAdapter;
import info.nightscout.androidaps.utils.AndroidPermission;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.LocaleHelper;
import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.protection.ProtectionCheck;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -141,11 +137,9 @@ public class MainActivity extends NoSplashAppCompatActivity {
.subscribe(this::processPreferenceChange, FabricPrivacy::logException)
);
if (!SP.getBoolean(R.string.key_setupwizard_processed, false)) {
if (!SP.getBoolean(R.string.key_setupwizard_processed, false) || !SP.contains(R.string.key_units)) {
Intent intent = new Intent(this, SetupWizardActivity.class);
startActivity(intent);
} else {
checkEula();
}
AndroidPermission.notifyForStoragePermission(this);
@ -237,20 +231,8 @@ public class MainActivity extends NoSplashAppCompatActivity {
}
}
private void checkEula() {
//SP.removeBoolean(R.string.key_i_understand);
boolean IUnderstand = SP.getBoolean(R.string.key_i_understand, false);
if (!IUnderstand) {
Intent intent = new Intent(getApplicationContext(), AgreementActivity.class);
startActivity(intent);
finish();
}
}
private void doMigrations() {
checkUpgradeToProfileTarget();
// guarantee that the unreachable threshold is at least 30 and of type String
// Added in 1.57 at 21.01.2018
int unreachable_threshold = SP.getInt(R.string.key_pump_unreachable_threshold, 30);
@ -260,26 +242,6 @@ public class MainActivity extends NoSplashAppCompatActivity {
}
private void checkUpgradeToProfileTarget() { // TODO: can be removed in the future
boolean oldKeyExists = SP.contains("openapsma_min_bg");
if (oldKeyExists) {
Profile profile = ProfileFunctions.getInstance().getProfile();
String oldRange = SP.getDouble("openapsma_min_bg", 0d) + " - " + SP.getDouble("openapsma_max_bg", 0d);
String newRange = "";
if (profile != null) {
newRange = profile.getTargetLow() + " - " + profile.getTargetHigh();
}
String message = "Target range is changed in current version.\n\nIt's not taken from preferences but from profile.\n\n!!! REVIEW YOUR SETTINGS !!!";
message += "\n\nOld settings: " + oldRange;
message += "\nProfile settings: " + newRange;
OKDialog.show(this, "Target range change", message, () -> {
SP.remove("openapsma_min_bg");
SP.remove("openapsma_max_bg");
SP.remove("openapsma_target_bg");
});
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

View file

@ -59,7 +59,6 @@ import info.nightscout.androidaps.plugins.insulin.InsulinOrefUltraRapidActingPlu
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin;
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin;
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin;
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
@ -129,7 +128,14 @@ public class MainApp extends Application {
sConstraintsChecker = new ConstraintChecker();
sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class);
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> log.error("Uncaught exception crashing app", ex));
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
if (ex instanceof InternalError) {
// usually the app trying to spawn a thread while being killed
return;
}
log.error("Uncaught exception crashing app", ex);
});
try {
if (FabricPrivacy.fabricEnabled()) {
@ -188,8 +194,7 @@ public class MainApp extends Application {
if (Config.APS) pluginsList.add(OpenAPSAMAPlugin.getPlugin());
if (Config.APS) pluginsList.add(OpenAPSSMBPlugin.getPlugin());
pluginsList.add(NSProfilePlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(SimpleProfilePlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(LocalProfilePlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(LocalProfilePlugin.INSTANCE);
pluginsList.add(TreatmentsPlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(SafetyPlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(VersionCheckerPlugin.INSTANCE);
@ -204,7 +209,7 @@ public class MainApp extends Application {
pluginsList.add(SourcePoctechPlugin.getPlugin());
pluginsList.add(SourceTomatoPlugin.getPlugin());
pluginsList.add(SourceEversensePlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.getPlugin());
if (!Config.NSCLIENT) pluginsList.add(SmsCommunicatorPlugin.INSTANCE);
pluginsList.add(FoodPlugin.getPlugin());
pluginsList.add(WearPlugin.initPlugin(this));

View file

@ -1,44 +0,0 @@
package info.nightscout.androidaps.activities;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import info.nightscout.androidaps.MainActivity;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.SP;
public class AgreementActivity extends NoSplashActivity {
boolean IUnderstand;
CheckBox agreeCheckBox;
Button saveButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_agreement);
IUnderstand = SP.getBoolean(R.string.key_i_understand, false);
setContentView(R.layout.activity_agreement);
agreeCheckBox = (CheckBox)findViewById(R.id.agreementCheckBox);
agreeCheckBox.setChecked(IUnderstand);
saveButton = (Button)findViewById(R.id.agreementSaveButton);
addListenerOnButton();
}
public void addListenerOnButton() {
saveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SP.putBoolean(R.string.key_i_understand, agreeCheckBox.isChecked());
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
});
}
}

View file

@ -45,7 +45,7 @@ import info.nightscout.androidaps.utils.T;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
public class HistoryBrowseActivity extends NoSplashActivity {
public class HistoryBrowseActivity extends NoSplashAppCompatActivity {
private static Logger log = LoggerFactory.getLogger(HistoryBrowseActivity.class);
private CompositeDisposable disposable = new CompositeDisposable();
@ -150,7 +150,7 @@ public class HistoryBrowseActivity extends NoSplashActivity {
);
dpd.setThemeDark(true);
dpd.dismissOnPause(true);
dpd.show(getFragmentManager(), "Datepickerdialog");
dpd.show(getSupportFragmentManager(), "Datepickerdialog");
});
bgGraph.getGridLabelRenderer().setGridColor(MainApp.gc(R.color.graphgrid));
@ -231,9 +231,8 @@ public class HistoryBrowseActivity extends NoSplashActivity {
noProfile.setVisibility(View.GONE);
}
final String units = profile.getUnits();
final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units);
final double highLine = OverviewPlugin.INSTANCE.determineHighLine(units);
final double lowLine = OverviewPlugin.INSTANCE.determineLowLine();
final double highLine = OverviewPlugin.INSTANCE.determineHighLine();
buttonDate.setText(DateUtil.dateAndTimeString(start));
buttonZoom.setText(String.valueOf(rangeToDisplay));

View file

@ -1,13 +0,0 @@
package info.nightscout.androidaps.activities
import android.app.Activity
import android.os.Bundle
import info.nightscout.androidaps.R
open class NoSplashActivity : Activity() {
public override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme_NoActionBar)
super.onCreate(savedInstanceState)
}
}

View file

@ -1,12 +1,18 @@
package info.nightscout.androidaps.activities
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.LocaleHelper
open class NoSplashAppCompatActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme_NoActionBar)
super.onCreate(savedInstanceState)
}
public override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(LocaleHelper.wrap(newBase))
}
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.activities;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.EditTextPreference;
@ -9,12 +10,13 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import java.util.Arrays;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRebuildTabs;
import info.nightscout.androidaps.interfaces.PluginBase;
@ -30,7 +32,6 @@ import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin;
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin;
import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader;
import info.nightscout.androidaps.plugins.general.wear.WearPlugin;
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatuslinePlugin;
import info.nightscout.androidaps.plugins.insulin.InsulinOrefFreePeakPlugin;
@ -47,9 +48,11 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
import info.nightscout.androidaps.utils.LocaleHelper;
import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.protection.ProtectionCheck;
import info.nightscout.androidaps.utils.SafeParse;
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
MyPreferenceFragment myPreferenceFragment;
@ -66,21 +69,47 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
}
@Override
public void attachBaseContext(Context newBase) {
super.attachBaseContext(LocaleHelper.INSTANCE.wrap(newBase));
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
RxBus.INSTANCE.send(new EventPreferenceChange(key));
if (key.equals("language")) {
if (key.equals(MainApp.gs(R.string.key_language))) {
RxBus.INSTANCE.send(new EventRebuildTabs(true));
//recreate() does not update language so better close settings
finish();
}
if (key.equals("short_tabtitles")) {
if (key.equals(MainApp.gs(R.string.key_short_tabtitles))) {
RxBus.INSTANCE.send(new EventRebuildTabs());
}
if (key.equals(MainApp.gs(R.string.key_units))) {
recreate();
return;
}
if (key.equals(MainApp.gs(R.string.key_openapsama_useautosens)) && SP.getBoolean(R.string.key_openapsama_useautosens, false)) {
OKDialog.show(this, MainApp.gs(R.string.configbuilder_sensitivity), MainApp.gs(R.string.sensitivity_warning), null);
}
updatePrefSummary(myPreferenceFragment.getPreference(key));
updatePrefSummary(myPreferenceFragment.findPreference(key));
}
private static void adjustUnitDependentPrefs(Preference pref) {
// convert preferences values to current units
String[] unitDependent = new String[]{
MainApp.gs(R.string.key_hypo_target),
MainApp.gs(R.string.key_activity_target),
MainApp.gs(R.string.key_eatingsoon_target),
MainApp.gs(R.string.key_high_mark),
MainApp.gs(R.string.key_low_mark)
};
if (Arrays.asList(unitDependent).contains(pref.getKey())) {
EditTextPreference editTextPref = (EditTextPreference) pref;
String converted = Profile.toCurrentUnitsString(SafeParse.stringToDouble(editTextPref.getText()));
editTextPref.setSummary(converted);
editTextPref.setText(converted);
}
}
private static void updatePrefSummary(Preference pref) {
@ -116,15 +145,17 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
EditTextPreference editTextPref = (EditTextPreference) pref;
if (pref.getKey().contains("password") || pref.getKey().contains("secret")) {
pref.setSummary("******");
} else if (pref.getKey().equals(MainApp.gs(R.string.key_danars_name))) {
pref.setSummary(SP.getString(R.string.key_danars_name, ""));
} else if (editTextPref.getText() != null) {
((EditTextPreference) pref).setDialogMessage(editTextPref.getDialogMessage());
pref.setSummary(editTextPref.getText());
} else if (pref.getKey().contains(SP.getString(R.string.key_smscommunicator_allowednumbers, "")) && (editTextPref.getText() == null || TextUtils.isEmpty(editTextPref.getText().trim()))) {
pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary));
} else {
for (PluginBase plugin : MainApp.getPluginsList()) {
plugin.updatePreferenceSummary(pref);
}
}
}
if (pref != null)
adjustUnitDependentPrefs(pref);
}
public static void initSummary(Preference p) {
@ -167,8 +198,8 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
if (!Config.NSCLIENT) {
addPreferencesFromResource(R.xml.pref_password);
}
addPreferencesFromResource(R.xml.pref_general);
addPreferencesFromResource(R.xml.pref_age);
addPreferencesFromResource(R.xml.pref_language);
addPreferencesFromResource(R.xml.pref_overview);
@ -212,7 +243,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
addPreferencesFromResource(R.xml.pref_others);
@ -222,26 +253,11 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
addPreferencesFromResourceIfEnabled(StatuslinePlugin.getPlugin(), PluginType.GENERAL);
}
if (Config.NSCLIENT) {
PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) findPreference(getString(R.string.key_advancedsettings));
if (scrnAdvancedSettings != null) {
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_warning)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_res_critical)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_warning)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_statuslights_bat_critical)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights)));
scrnAdvancedSettings.removePreference(getPreference(getString(R.string.key_show_statuslights_extended)));
}
}
initSummary(getPreferenceScreen());
final Preference tidepoolTestLogin = findPreference(MainApp.gs(R.string.key_tidepool_test_login));
if (tidepoolTestLogin != null)
tidepoolTestLogin.setOnPreferenceClickListener(preference -> {
TidepoolUploader.INSTANCE.testLogin(getActivity());
return false;
});
for (PluginBase plugin : MainApp.getPluginsList()) {
plugin.preprocessPreferences(this);
}
}
@Override
@ -249,9 +265,5 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
super.onSaveInstanceState(outState);
outState.putInt("id", id);
}
public Preference getPreference(String key) {
return findPreference(key);
}
}
}

View file

@ -54,7 +54,7 @@ import info.nightscout.androidaps.utils.SafeParse;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
public class TDDStatsActivity extends NoSplashActivity {
public class TDDStatsActivity extends NoSplashAppCompatActivity {
private static Logger log = LoggerFactory.getLogger(TDDStatsActivity.class);
private CompositeDisposable disposable = new CompositeDisposable();

View file

@ -1,7 +1,5 @@
package info.nightscout.androidaps.data;
import com.rits.cloning.Cloner;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
@ -37,8 +35,19 @@ public class IobTotal implements DataPointWithLabelInterface {
public IobTotal copy() {
Cloner cloner = new Cloner();
return cloner.deepClone(this);
IobTotal i = new IobTotal(time);
i.iob = iob;
i.activity = activity;
i.bolussnooze = bolussnooze;
i.basaliob = basaliob;
i.netbasalinsulin = netbasalinsulin;
i.hightempinsulin = hightempinsulin;
i.lastBolusTime = lastBolusTime;
if (iobWithZeroTemp != null) i.iobWithZeroTemp = iobWithZeroTemp.copy();
i.netInsulin = netInsulin;
i.netRatio = netRatio;
i.extendedBolusInsulin = extendedBolusInsulin;
return i;
}
public IobTotal(long time) {
@ -137,7 +146,7 @@ public class IobTotal implements DataPointWithLabelInterface {
// DataPoint interface
int color;
private int color;
@Override
public double getX() {

View file

@ -19,6 +19,7 @@ import info.nightscout.androidaps.interfaces.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.utils.DateUtil;
@ -75,6 +76,11 @@ public class Profile {
}
}
// Constructor from profileStore JSON
public Profile(JSONObject json) {
init(json, 100, 0);
}
public Profile(JSONObject json, int percentage, int timeshift) {
init(json, percentage, timeshift);
}
@ -100,8 +106,6 @@ public class Profile {
units = json.getString("units").toLowerCase();
if (json.has("dia"))
dia = json.getDouble("dia");
if (json.has("dia"))
dia = json.getDouble("dia");
if (json.has("timezone"))
timeZone = TimeZone.getTimeZone(json.getString("timezone"));
isf = json.getJSONArray("sens");
@ -149,7 +153,7 @@ public class Profile {
return units;
}
public TimeZone getTimeZone() {
TimeZone getTimeZone() {
return timeZone;
}
@ -162,7 +166,7 @@ public class Profile {
double multiplier = getMultiplier(array);
LongSparseArray<Double> sparse = new LongSparseArray<>();
for (Integer index = 0; index < array.length(); index++) {
for (int index = 0; index < array.length(); index++) {
try {
final JSONObject o = array.getJSONObject(index);
long tas = 0;
@ -385,12 +389,12 @@ public class Profile {
return retValue;
}
public double getIsf() {
return getIsfTimeFromMidnight(secondsFromMidnight());
public double getIsfMgdl() {
return toMgdl(getIsfTimeFromMidnight(secondsFromMidnight()), units);
}
public double getIsf(long time) {
return getIsfTimeFromMidnight(secondsFromMidnight(time));
public double getIsfMgdl(long time) {
return toMgdl(getIsfTimeFromMidnight(secondsFromMidnight(time)), units);
}
double getIsfTimeFromMidnight(int timeAsSeconds) {
@ -405,15 +409,15 @@ public class Profile {
return getValuesList(isf_v, null, new DecimalFormat("0.0"), getUnits() + MainApp.gs(R.string.profile_per_unit));
}
public ProfileValue[] getIsfs() {
public ProfileValue[] getIsfsMgdl() {
if (isf_v == null)
isf_v = convertToSparseArray(ic);
ProfileValue[] ret = new ProfileValue[isf_v.size()];
for (Integer index = 0; index < isf_v.size(); index++) {
Integer tas = (int) isf_v.keyAt(index);
for (int index = 0; index < isf_v.size(); index++) {
int tas = (int) isf_v.keyAt(index);
double value = isf_v.valueAt(index);
ret[index] = new ProfileValue(tas, value);
ret[index] = new ProfileValue(tas, toMgdl(value, units));
}
return ret;
}
@ -495,44 +499,44 @@ public class Profile {
return ret;
}
public double getTarget() {
return getTarget(secondsFromMidnight());
public double getTargetMgdl() {
return getTargetMgdl(secondsFromMidnight());
}
protected double getTarget(int timeAsSeconds) {
return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2;
public double getTargetMgdl(int timeAsSeconds) {
return toMgdl((getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2, units);
}
public double getTargetLow() {
return getTargetLowTimeFromMidnight(secondsFromMidnight());
public double getTargetLowMgdl() {
return toMgdl(getTargetLowTimeFromMidnight(secondsFromMidnight()), units);
}
public double getTargetLow(long time) {
return getTargetLowTimeFromMidnight(secondsFromMidnight(time));
public double getTargetLowMgdl(long time) {
return toMgdl(getTargetLowTimeFromMidnight(secondsFromMidnight(time)), units);
}
public double getTargetLowTimeFromMidnight(int timeAsSeconds) {
double getTargetLowTimeFromMidnight(int timeAsSeconds) {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
return getValueToTime(targetLow_v, timeAsSeconds);
}
public double getTargetHigh() {
return getTargetHighTimeFromMidnight(secondsFromMidnight());
public double getTargetHighMgdl() {
return toMgdl(getTargetHighTimeFromMidnight(secondsFromMidnight()), units);
}
public double getTargetHigh(long time) {
return getTargetHighTimeFromMidnight(secondsFromMidnight(time));
public double getTargetHighMgdl(long time) {
return toMgdl(getTargetHighTimeFromMidnight(secondsFromMidnight(time)), units);
}
public double getTargetHighTimeFromMidnight(int timeAsSeconds) {
double getTargetHighTimeFromMidnight(int timeAsSeconds) {
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
return getValueToTime(targetHigh_v, timeAsSeconds);
}
public class TargetValue {
public TargetValue(int timeAsSeconds, double low, double high) {
TargetValue(int timeAsSeconds, double low, double high) {
this.timeAsSeconds = timeAsSeconds;
this.low = low;
this.high = high;
@ -559,17 +563,17 @@ public class Profile {
return ret;
}
public ProfileValue[] getSingleTargets() {
public ProfileValue[] getSingleTargetsMgdl() {
if (targetLow_v == null)
targetLow_v = convertToSparseArray(targetLow);
if (targetHigh_v == null)
targetHigh_v = convertToSparseArray(targetHigh);
ProfileValue[] ret = new ProfileValue[targetLow_v.size()];
for (Integer index = 0; index < targetLow_v.size(); index++) {
Integer tas = (int) targetLow_v.keyAt(index);
for (int index = 0; index < targetLow_v.size(); index++) {
int tas = (int) targetLow_v.keyAt(index);
double target = (targetLow_v.valueAt(index) + targetHigh_v.valueAt(index)) / 2;
ret[index] = new ProfileValue(tas, target);
ret[index] = new ProfileValue(tas, toMgdl(target, units));
}
return ret;
}
@ -617,22 +621,39 @@ public class Profile {
else return value * Constants.MGDL_TO_MMOLL;
}
public static double toUnits(Double valueInMgdl, Double valueInMmol, String units) {
public static double fromMmolToUnits(double value, String units) {
if (units.equals(Constants.MMOL)) return value;
else return value * Constants.MMOLL_TO_MGDL;
}
public static double toUnits(double valueInMgdl, double valueInMmol, String units) {
if (units.equals(Constants.MGDL)) return valueInMgdl;
else return valueInMmol;
}
public static String toUnitsString(Double valueInMgdl, Double valueInMmol, String units) {
public static String toUnitsString(double valueInMgdl, double valueInMmol, String units) {
if (units.equals(Constants.MGDL)) return DecimalFormatter.to0Decimal(valueInMgdl);
else return DecimalFormatter.to1Decimal(valueInMmol);
}
public static String toSignedUnitsString(Double valueInMgdl, Double valueInMmol, String units) {
public static String toSignedUnitsString(double valueInMgdl, double valueInMmol, String units) {
if (units.equals(Constants.MGDL))
return (valueInMgdl > 0 ? "+" : "") + DecimalFormatter.to0Decimal(valueInMgdl);
else return (valueInMmol > 0 ? "+" : "") + DecimalFormatter.to1Decimal(valueInMmol);
}
public static double toCurrentUnits(double anyBg) {
if (anyBg < 32) return fromMmolToUnits(anyBg, ProfileFunctions.getSystemUnits());
else return fromMgdlToUnits(anyBg, ProfileFunctions.getSystemUnits());
}
public static String toCurrentUnitsString(double anyBg) {
if (anyBg < 32)
return toUnitsString(anyBg * Constants.MMOLL_TO_MGDL, anyBg, ProfileFunctions.getSystemUnits());
else
return toUnitsString(anyBg, anyBg * Constants.MGDL_TO_MMOLL, ProfileFunctions.getSystemUnits());
}
// targets are stored in mg/dl but profile vary
public static String toTargetRangeString(double low, double high, String sourceUnits, String units) {
double lowMgdl = toMgdl(low, sourceUnits);
@ -670,4 +691,112 @@ public class Profile {
public int getTimeshift() {
return timeshift;
}
public Profile convertToNonCustomizedProfile() {
JSONObject o = new JSONObject();
try {
o.put("units", units);
o.put("dia", dia);
o.put("timezone", timeZone.getID());
// SENS
JSONArray sens = new JSONArray();
double lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getIsfTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
sens.put(item);
}
}
o.put("sens", sens);
// CARBRATIO
JSONArray carbratio = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getIcTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
carbratio.put(item);
}
}
o.put("carbratio", carbratio);
// BASAL
JSONArray basal = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getBasalTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
basal.put(item);
}
}
o.put("basal", basal);
// TARGET_LOW
JSONArray target_low = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getTargetLowTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
target_low.put(item);
}
}
o.put("target_low", target_low);
// TARGET_HIGH
JSONArray target_high = new JSONArray();
lastValue = -1d;
for (int i = 0; i < 24; i++) {
int timeAsSeconds = i * 60 * 60;
double value = getTargetHighTimeFromMidnight(timeAsSeconds);
if (value != lastValue) {
JSONObject item = new JSONObject();
String time;
DecimalFormat df = new DecimalFormat("00");
time = df.format(i) + ":00";
item.put("time", time);
item.put("timeAsSeconds", timeAsSeconds);
item.put("value", value);
lastValue = value;
target_high.put(item);
}
}
o.put("target_high", target_high);
} catch (JSONException e) {
log.error("Unhandled exception" + e);
}
return new Profile(o);
}
}

View file

@ -1,118 +0,0 @@
package info.nightscout.androidaps.data;
import androidx.annotation.Nullable;
import androidx.collection.ArrayMap;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Iterator;
import info.nightscout.androidaps.Constants;
/**
* Created by mike on 01.06.2017.
*/
public class ProfileStore {
private static Logger log = LoggerFactory.getLogger(ProfileStore.class);
private JSONObject json = null;
private String units = Constants.MGDL;
ArrayMap<String, Profile> cachedObjects = new ArrayMap<>();
public ProfileStore(JSONObject json) {
this.json = json;
getDefaultProfile(); // initialize units
}
public JSONObject getData() {
return json;
}
@Nullable
public Profile getDefaultProfile() {
Profile profile = null;
try {
String defaultProfileName = json.getString("defaultProfile");
JSONObject store = json.getJSONObject("store");
if (store.has(defaultProfileName)) {
profile = cachedObjects.get(defaultProfileName);
if (profile == null) {
if (store.has("units"))
units = store.getString("units");
profile = new Profile(store.getJSONObject(defaultProfileName), units);
units = profile.getUnits();
cachedObjects.put(defaultProfileName, profile);
}
}
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return profile;
}
@Nullable
public String getDefaultProfileName() {
String defaultProfileName = null;
try {
defaultProfileName = json.getString("defaultProfile");
JSONObject store = json.getJSONObject("store");
if (store.has(defaultProfileName)) {
return defaultProfileName;
}
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return defaultProfileName;
}
public String getUnits() {
return units;
}
@Nullable
public Profile getSpecificProfile(String profileName) {
Profile profile = null;
try {
JSONObject store = json.getJSONObject("store");
if (store.has(profileName)) {
profile = cachedObjects.get(profileName);
if (profile == null) {
if (store.has("units"))
units = store.getString("units");
profile = new Profile(store.getJSONObject(profileName), units);
units = profile.getUnits();
cachedObjects.put(profileName, profile);
}
}
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return profile;
}
public ArrayList<CharSequence> getProfileList() {
ArrayList<CharSequence> ret = new ArrayList<CharSequence>();
JSONObject store;
try {
store = json.getJSONObject("store");
Iterator<?> keys = store.keys();
while (keys.hasNext()) {
String profileName = (String) keys.next();
ret.add(profileName);
}
} catch (JSONException e) {
log.error("Unhandled exception", e);
}
return ret;
}
}

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.data
import androidx.collection.ArrayMap
import org.json.JSONException
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
class ProfileStore(val data: JSONObject) {
private val log = LoggerFactory.getLogger(ProfileStore::class.java)
private val cachedObjects = ArrayMap<String, Profile>()
private fun getStore(): JSONObject? {
try {
if (data.has("store")) return data.getJSONObject("store")
} catch (e: JSONException) {
log.error("Unhandled exception", e)
}
return null
}
fun getDefaultProfile(): Profile? = getDefaultProfileName()?.let { getSpecificProfile(it) }
fun getDefaultProfileName(): String? {
val defaultProfileName = data.getString("defaultProfile")
return getStore()?.has(defaultProfileName)?.let { defaultProfileName }
}
fun getProfileList(): ArrayList<CharSequence> {
val ret = ArrayList<CharSequence>()
getStore()?.keys()?.let { keys ->
while (keys.hasNext()) {
val profileName = keys.next() as String
ret.add(profileName)
}
}
return ret
}
fun getSpecificProfile(profileName: String): Profile? {
var profile: Profile? = null
try {
getStore()?.let { store ->
if (store.has(profileName)) {
profile = cachedObjects[profileName]
if (profile == null) {
val profileObject = store.getJSONObject(profileName)
if (profileObject != null && profileObject.has("units")) {
profile = Profile(profileObject, profileObject.getString("units"))
cachedObjects[profileName] = profile
}
}
}
}
} catch (e: JSONException) {
log.error("Unhandled exception", e)
}
return profile
}
}

View file

@ -11,6 +11,7 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
@ -74,7 +75,7 @@ public class QuickWizardEntry {
//BG
double bg = 0;
if (lastBG != null && useBG() == YES) {
bg = lastBG.valueToUnits(profile.getUnits());
bg = lastBG.valueToUnits(ProfileFunctions.getSystemUnits());
}
// COB

View file

@ -7,6 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import info.nightscout.androidaps.Constants;
@ -19,10 +20,11 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.T;
@DatabaseTable(tableName = DatabaseHelper.DATABASE_BGREADINGS)
public class BgReading implements DataPointWithLabelInterface {
private static Logger log = LoggerFactory.getLogger(L.DATABASE);
private static Logger log = LoggerFactory.getLogger(L.GLUCOSE);
@DatabaseField(id = true)
public long date;
@ -73,9 +75,10 @@ public class BgReading implements DataPointWithLabelInterface {
public String directionToSymbol() {
String symbol = "";
if (direction == null) {
symbol = "??";
} else if (direction.compareTo("DoubleDown") == 0) {
if (direction == null)
direction = calculateDirection();
if (direction.compareTo("DoubleDown") == 0) {
symbol = "\u21ca";
} else if (direction.compareTo("SingleDown") == 0) {
symbol = "\u2193";
@ -95,18 +98,13 @@ public class BgReading implements DataPointWithLabelInterface {
return symbol;
}
public static boolean isSlopeNameInvalid(String direction) {
if (direction.compareTo("NOT_COMPUTABLE") == 0 ||
private static boolean isSlopeNameInvalid(String direction) {
return direction.compareTo("NOT_COMPUTABLE") == 0 ||
direction.compareTo("NOT COMPUTABLE") == 0 ||
direction.compareTo("OUT_OF_RANGE") == 0 ||
direction.compareTo("OUT OF RANGE") == 0 ||
direction.compareTo("NONE") == 0 ||
direction.compareTo("NotComputable") == 0
) {
return true;
} else {
return false;
}
direction.compareTo("NotComputable") == 0;
}
@ -123,7 +121,8 @@ public class BgReading implements DataPointWithLabelInterface {
public boolean isDataChanging(BgReading other) {
if (date != other.date) {
log.error("Comparing different");
if (L.isEnabled(L.GLUCOSE))
log.error("Comparing different");
return false;
}
if (value != other.value)
@ -133,7 +132,8 @@ public class BgReading implements DataPointWithLabelInterface {
public boolean isEqual(BgReading other) {
if (date != other.date) {
log.error("Comparing different");
if (L.isEnabled(L.GLUCOSE))
log.error("Comparing different");
return false;
}
if (value != other.value)
@ -149,7 +149,8 @@ public class BgReading implements DataPointWithLabelInterface {
public void copyFrom(BgReading other) {
if (date != other.date) {
log.error("Copying different");
if (L.isEnabled(L.GLUCOSE))
log.error("Copying different");
return;
}
value = other.value;
@ -181,8 +182,7 @@ public class BgReading implements DataPointWithLabelInterface {
@Override
public double getY() {
String units = ProfileFunctions.getInstance().getProfileUnits();
return valueToUnits(units);
return valueToUnits(ProfileFunctions.getSystemUnits());
}
@Override
@ -215,9 +215,9 @@ public class BgReading implements DataPointWithLabelInterface {
@Override
public int getColor() {
String units = ProfileFunctions.getInstance().getProfileUnits();
Double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units);
Double highLine = OverviewPlugin.INSTANCE.determineHighLine(units);
String units = ProfileFunctions.getSystemUnits();
Double lowLine = OverviewPlugin.INSTANCE.determineLowLine();
Double highLine = OverviewPlugin.INSTANCE.determineHighLine();
int color = MainApp.gc(R.color.inrange);
if (isPrediction())
return getPredectionColor();
@ -246,4 +246,53 @@ public class BgReading implements DataPointWithLabelInterface {
return isaCOBPrediction || isCOBPrediction || isIOBPrediction || isUAMPrediction || isZTPrediction;
}
// Copied from xDrip+
String calculateDirection() {
// Rework to get bgreaings from internal DB and calculate on that base
List<BgReading> bgReadingsList = MainApp.getDbHelper().getAllBgreadingsDataFromTime(this.date - T.mins(10).msecs(), false);
if (bgReadingsList == null || bgReadingsList.size() < 2)
return "NONE";
BgReading current = bgReadingsList.get(1);
BgReading previous = bgReadingsList.get(0);
if (bgReadingsList.get(1).date < bgReadingsList.get(0).date) {
current = bgReadingsList.get(0);
previous = bgReadingsList.get(1);
}
double slope;
// Avoid division by 0
if (current.date == previous.date)
slope = 0;
else
slope = (previous.value - current.value) / (previous.date - current.date);
if (L.isEnabled(L.GLUCOSE))
log.debug("Slope is :" + slope + " delta " + (previous.value - current.value) + " date difference " + (current.date - previous.date));
double slope_by_minute = slope * 60000;
String arrow = "NONE";
if (slope_by_minute <= (-3.5)) {
arrow = "DoubleDown";
} else if (slope_by_minute <= (-2)) {
arrow = "SingleDown";
} else if (slope_by_minute <= (-1)) {
arrow = "FortyFiveDown";
} else if (slope_by_minute <= (1)) {
arrow = "Flat";
} else if (slope_by_minute <= (2)) {
arrow = "FortyFiveUp";
} else if (slope_by_minute <= (3.5)) {
arrow = "SingleUp";
} else if (slope_by_minute <= (40)) {
arrow = "DoubleUp";
}
if (L.isEnabled(L.GLUCOSE))
log.debug("Direction set to: " + arrow);
return arrow;
}
}

View file

@ -100,8 +100,8 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval {
String hours = " " + MainApp.gs(R.string.hours) + " ";
if (useShortText) {
days = "d";
hours = "h";
days = MainApp.gs(R.string.shortday);
hours = MainApp.gs(R.string.shorthour);
}
return diff.get(TimeUnit.DAYS) + days + diff.get(TimeUnit.HOURS) + hours;
@ -167,7 +167,7 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval {
@Override
public double getY() {
String units = ProfileFunctions.getInstance().getProfileUnits();
String units = ProfileFunctions.getSystemUnits();
if (eventType.equals(MBG)) {
double mbg = 0d;
try {

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.db;
import android.content.Context;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.Nullable;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
@ -772,8 +773,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
TempTarget tempTarget = new TempTarget()
.date(trJson.getLong("mills"))
.duration(JsonHelper.safeGetInt(trJson, "duration"))
.low(Profile.toMgdl(trJson.getDouble("targetBottom"), units))
.high(Profile.toMgdl(trJson.getDouble("targetTop"), units))
.low(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetBottom"), units))
.high(Profile.toMgdl(JsonHelper.safeGetDouble(trJson, "targetTop"), units))
.reason(JsonHelper.safeGetString(trJson, "reason", ""))
._id(trJson.getString("_id"))
.source(Source.NIGHTSCOUT);

View file

@ -259,11 +259,11 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface {
double sensitivityRatio = lastAutosensResult.ratio;
double normalTarget = 100;
if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) {
if (exercise_mode && isTempTarget && profile.getTargetMgdl() >= normalTarget + 5) {
// w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44
// e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6
double c = half_basal_exercise_target - normalTarget;
sensitivityRatio = c / (c + profile.getTarget() - normalTarget);
sensitivityRatio = c / (c + profile.getTargetMgdl() - normalTarget);
}
if (realDuration > 0) {

View file

@ -299,11 +299,11 @@ public class TemporaryBasal implements Interval, DbObjectBase {
double sensitivityRatio = lastAutosensResult.ratio;
double normalTarget = 100;
if (exercise_mode && isTempTarget && profile.getTarget() >= normalTarget + 5) {
if (exercise_mode && isTempTarget && profile.getTargetMgdl() >= normalTarget + 5) {
// w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44
// e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6
double c = half_basal_exercise_target - normalTarget;
sensitivityRatio = c / (c + profile.getTarget() - normalTarget);
sensitivityRatio = c / (c + profile.getTargetMgdl() - normalTarget);
}
if (realDuration > 0) {

View file

@ -1,10 +1,13 @@
package info.nightscout.androidaps.interfaces;
import android.os.SystemClock;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -215,4 +218,10 @@ public abstract class PluginBase {
protected void onStateChange(PluginType type, State oldState, State newState) {
}
public void preprocessPreferences(@NotNull final PreferenceFragment preferenceFragment) {
}
public void updatePreferenceSummary(@NotNull final Preference pref) {
}
}

View file

@ -10,6 +10,5 @@ import info.nightscout.androidaps.data.ProfileStore;
public interface ProfileInterface {
@Nullable
ProfileStore getProfile();
String getUnits();
String getProfileName();
}

View file

@ -247,7 +247,7 @@ public class APSResult {
}
}
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return array;
}
@ -280,7 +280,7 @@ public class APSResult {
}
}
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return latest;

View file

@ -23,6 +23,7 @@ import javax.annotation.Nullable;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
@ -194,8 +195,6 @@ public class DetermineBasalAdapterAMAJS {
double autosensDataRatio,
boolean tempTargetSet) throws JSONException {
String units = profile.getUnits();
mProfile = new JSONObject();
mProfile.put("max_iob", maxIob);
mProfile.put("dia", Math.min(profile.getDia(), 3d));
@ -206,7 +205,7 @@ public class DetermineBasalAdapterAMAJS {
mProfile.put("max_bg", maxBg);
mProfile.put("target_bg", targetBg);
mProfile.put("carb_ratio", profile.getIc());
mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units));
mProfile.put("sens", profile.getIsfMgdl());
mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
mProfile.put("skip_neutral_temps", true);
@ -220,7 +219,7 @@ public class DetermineBasalAdapterAMAJS {
mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
}
if (units.equals(Constants.MMOL)) {
if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) {
mProfile.put("out_units", "mmol/L");
}

View file

@ -107,6 +107,13 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface {
return;
}
if (pump == null) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected)));
if (L.isEnabled(L.APS))
log.debug(MainApp.gs(R.string.nopumpselected));
return;
}
if (!isEnabled(PluginType.APS)) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled)));
if (L.isEnabled(L.APS))
@ -121,12 +128,10 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface {
return;
}
String units = profile.getUnits();
double maxBasal = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
double minBg = Profile.toMgdl(profile.getTargetLow(), units);
double maxBg = Profile.toMgdl(profile.getTargetHigh(), units);
double targetBg = Profile.toMgdl(profile.getTarget(), units);
double minBg = profile.getTargetLowMgdl();
double maxBg = profile.getTargetHighMgdl();
double targetBg = profile.getTargetMgdl();
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
@ -162,9 +167,9 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface {
return;
if (!HardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC))
return;
if (!HardLimits.checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF))
if (!HardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF))
return;
if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal()))
if (!HardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal()))
return;
if (!HardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal()))
return;

View file

@ -20,6 +20,7 @@ import javax.annotation.Nullable;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
@ -160,8 +161,6 @@ public class DetermineBasalAdapterMAJS {
GlucoseStatus glucoseStatus,
MealData mealData) throws JSONException {
String units = profile.getUnits();
mProfile = new JSONObject();
mProfile.put("max_iob", maxIob);
mProfile.put("dia", Math.min(profile.getDia(), 3d));
@ -172,11 +171,11 @@ public class DetermineBasalAdapterMAJS {
mProfile.put("max_bg", maxBg);
mProfile.put("target_bg", targetBg);
mProfile.put("carb_ratio", profile.getIc());
mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units));
mProfile.put("sens", profile.getIsfMgdl());
mProfile.put("current_basal", basalRate);
if (units.equals(Constants.MMOL)) {
if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) {
mProfile.put("out_units", "mmol/L");
}

View file

@ -106,6 +106,13 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface {
return;
}
if (pump == null) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected)));
if (L.isEnabled(L.APS))
log.debug(MainApp.gs(R.string.nopumpselected));
return;
}
if (!isEnabled(PluginType.APS)) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled)));
if (L.isEnabled(L.APS))
@ -120,13 +127,11 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface {
return;
}
String units = profile.getUnits();
double maxBasal = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
double minBg = Profile.toMgdl(profile.getTargetLow(), units);
double maxBg = Profile.toMgdl(profile.getTargetHigh(), units);
double targetBg = Profile.toMgdl(profile.getTarget(), units);
double minBg = profile.getTargetLowMgdl();
double maxBg = profile.getTargetHighMgdl();
double targetBg = profile.getTargetMgdl();
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
@ -160,9 +165,9 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface {
return;
if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC))
return;
if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF))
if (!checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF))
return;
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal()))
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal()))
return;
if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal()))
return;

View file

@ -23,6 +23,7 @@ import javax.annotation.Nullable;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.MealData;
@ -219,8 +220,6 @@ public class DetermineBasalAdapterSMBJS {
boolean advancedFiltering
) throws JSONException {
String units = profile.getUnits();
mProfile = new JSONObject();
mProfile.put("max_iob", maxIob);
@ -232,7 +231,7 @@ public class DetermineBasalAdapterSMBJS {
mProfile.put("max_bg", maxBg);
mProfile.put("target_bg", targetBg);
mProfile.put("carb_ratio", profile.getIc());
mProfile.put("sens", Profile.toMgdl(profile.getIsf(), units));
mProfile.put("sens", profile.getIsfMgdl());
mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3));
mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d));
@ -273,7 +272,7 @@ public class DetermineBasalAdapterSMBJS {
mProfile.put("temptargetSet", tempTargetSet);
mProfile.put("autosens_max", SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2")));
if (units.equals(Constants.MMOL)) {
if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) {
mProfile.put("out_units", "mmol/L");
}

View file

@ -111,6 +111,13 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr
return;
}
if (pump == null) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.nopumpselected)));
if (L.isEnabled(L.APS))
log.debug(MainApp.gs(R.string.nopumpselected));
return;
}
if (!isEnabled(PluginType.APS)) {
RxBus.INSTANCE.send(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openapsma_disabled)));
if (L.isEnabled(L.APS))
@ -125,16 +132,14 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr
return;
}
String units = profile.getUnits();
Constraint<Double> inputConstraints = new Constraint<>(0d); // fake. only for collecting all results
Constraint<Double> maxBasalConstraint = MainApp.getConstraintChecker().getMaxBasalAllowed(profile);
inputConstraints.copyReasons(maxBasalConstraint);
double maxBasal = maxBasalConstraint.value();
double minBg = Profile.toMgdl(profile.getTargetLow(), units);
double maxBg = Profile.toMgdl(profile.getTargetHigh(), units);
double targetBg = Profile.toMgdl(profile.getTarget(), units);
double minBg = profile.getTargetLowMgdl();
double maxBg = profile.getTargetHighMgdl();
double targetBg = profile.getTargetMgdl();
minBg = Round.roundTo(minBg, 0.1d);
maxBg = Round.roundTo(maxBg, 0.1d);
@ -168,9 +173,9 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, Constr
return;
if (!checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", HardLimits.MINIC, HardLimits.MAXIC))
return;
if (!checkOnlyHardLimits(Profile.toMgdl(profile.getIsf(), units), "sens", HardLimits.MINISF, HardLimits.MAXISF))
if (!checkOnlyHardLimits(profile.getIsfMgdl(), "sens", HardLimits.MINISF, HardLimits.MAXISF))
return;
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.05, HardLimits.maxBasal()))
if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, HardLimits.maxBasal()))
return;
if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, HardLimits.maxBasal()))
return;

View file

@ -27,6 +27,7 @@ import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.overview.dialogs.ErrorHelperActivity;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.SP;
import io.reactivex.disposables.CompositeDisposable;
@ -75,35 +76,42 @@ public class ProfileFunctions {
}
public String getProfileName() {
return getProfileName(System.currentTimeMillis());
return getProfileName(System.currentTimeMillis(), true, false);
}
public String getProfileName(boolean customized) {
return getProfileName(System.currentTimeMillis(), customized);
return getProfileName(System.currentTimeMillis(), customized, false);
}
public String getProfileName(long time) {
return getProfileName(time, true);
public String getProfileNameWithDuration() {
return getProfileName(System.currentTimeMillis(), true, true);
}
public String getProfileName(long time, boolean customized) {
public String getProfileName(long time, boolean customized, boolean showRemainingTime) {
String profileName = MainApp.gs(R.string.noprofileselected);
TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin();
ProfileInterface activeProfile = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface();
ProfileSwitch profileSwitch = activeTreatments.getProfileSwitchFromHistory(time);
if (profileSwitch != null) {
if (profileSwitch.profileJson != null) {
return customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName;
profileName = customized ? profileSwitch.getCustomizedName() : profileSwitch.profileName;
} else {
ProfileStore profileStore = activeProfile.getProfile();
if (profileStore != null) {
Profile profile = profileStore.getSpecificProfile(profileSwitch.profileName);
if (profile != null)
return profileSwitch.profileName;
profileName = profileSwitch.profileName;
}
}
if (showRemainingTime && profileSwitch.durationInMinutes != 0) {
profileName += DateUtil.untilString(profileSwitch.originalEnd());
}
return profileName;
}
return MainApp.gs(R.string.noprofileselected);
return profileName;
}
public boolean isProfileValid(String from) {
@ -116,9 +124,8 @@ public class ProfileFunctions {
return getProfile(System.currentTimeMillis());
}
public String getProfileUnits() {
Profile profile = getProfile();
return profile != null ? profile.getUnits() : Constants.MGDL;
public static String getSystemUnits() {
return SP.getString(R.string.key_units, Constants.MGDL);
}
@Nullable
@ -176,7 +183,7 @@ public class ProfileFunctions {
profileSwitch = new ProfileSwitch();
profileSwitch.date = System.currentTimeMillis();
profileSwitch.source = Source.USER;
profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false);
profileSwitch.profileName = getInstance().getProfileName(System.currentTimeMillis(), false, false);
profileSwitch.profileJson = getInstance().getProfile().getData().toString();
profileSwitch.profilePlugin = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface().getClass().getName();
profileSwitch.durationInMinutes = duration;

View file

@ -22,7 +22,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific
public class DstHelperPlugin extends PluginBase implements ConstraintsInterface {
public static final int DISABLE_TIMEFRAME_HOURS = -3;
public static final int WARN_PRIOR_TIMEFRAME_HOURS = 24;
public static final int WARN_PRIOR_TIMEFRAME_HOURS = 12;
private static Logger log = LoggerFactory.getLogger(L.CONSTRAINTS);
static DstHelperPlugin plugin = null;

View file

@ -4,6 +4,7 @@ import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@ -12,6 +13,7 @@ import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
@ -21,6 +23,8 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.constraints.objectives.dialogs.NtpProgressDialog
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
@ -101,21 +105,20 @@ class ObjectivesFragment : Fragment() {
}
private fun scrollToCurrentObjective() {
for (i in 0 until ObjectivesPlugin.objectives.size) {
val objective = ObjectivesPlugin.objectives[i]
if (!objective.isStarted || !objective.isAccomplished) {
val smoothScroller = object : LinearSmoothScroller(context!!) {
override fun getVerticalSnapPreference(): Int {
return SNAP_TO_START
}
override fun calculateTimeForScrolling(dx: Int): Int {
return super.calculateTimeForScrolling(dx) * 4
activity?.runOnUiThread {
for (i in 0 until ObjectivesPlugin.objectives.size) {
val objective = ObjectivesPlugin.objectives[i]
if (!objective.isStarted || !objective.isAccomplished) {
context?.let {
val smoothScroller = object : LinearSmoothScroller(it) {
override fun getVerticalSnapPreference(): Int = SNAP_TO_START
override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4
}
smoothScroller.targetPosition = i
objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller)
}
break
}
smoothScroller.targetPosition = i
objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller)
break
}
}
}
@ -129,7 +132,6 @@ class ObjectivesFragment : Fragment() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val objective = ObjectivesPlugin.objectives[position]
holder.title.text = MainApp.gs(R.string.nth_objective, position + 1)
holder.revert.visibility = View.GONE
if (objective.objective != 0) {
holder.objective.visibility = View.VISIBLE
holder.objective.text = MainApp.gs(objective.objective)
@ -145,6 +147,8 @@ class ObjectivesFragment : Fragment() {
holder.verify.visibility = View.GONE
holder.progress.visibility = View.GONE
holder.accomplished.visibility = View.GONE
holder.unFinish.visibility = View.GONE
holder.unStart.visibility = View.GONE
if (position == 0 || ObjectivesPlugin.objectives[position - 1].isAccomplished)
holder.start.visibility = View.VISIBLE
else
@ -155,15 +159,16 @@ class ObjectivesFragment : Fragment() {
holder.progress.visibility = View.GONE
holder.start.visibility = View.GONE
holder.accomplished.visibility = View.VISIBLE
holder.unFinish.visibility = View.VISIBLE
holder.unStart.visibility = View.GONE
} else if (objective.isStarted) {
holder.gate.setTextColor(-0x1)
holder.verify.visibility = View.VISIBLE
holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked
holder.start.visibility = View.GONE
holder.accomplished.visibility = View.GONE
if (objective.isRevertable) {
holder.revert.visibility = View.VISIBLE
}
holder.unFinish.visibility = View.GONE
holder.unStart.visibility = View.VISIBLE
holder.progress.visibility = View.VISIBLE
holder.progress.removeAllViews()
for (task in objective.tasks) {
@ -206,76 +211,87 @@ class ObjectivesFragment : Fragment() {
holder.accomplished.text = MainApp.gs(R.string.accomplished, DateUtil.dateAndTimeString(objective.accomplishedOn))
holder.accomplished.setTextColor(-0x3e3e3f)
holder.verify.setOnClickListener {
holder.verify.visibility = View.INVISIBLE
NetworkChangeReceiver.fetch()
NetworkChangeReceiver.grabNetworkStatus(context)
if (objectives_fake.isChecked) {
objective.accomplishedOn = DateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventObjectivesUpdateGui())
} else
SntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
activity?.runOnUiThread {
holder.verify.visibility = View.VISIBLE
} else {
// move out of UI thread
Thread {
NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck")
RxBus.send(EventNtpStatus(MainApp.gs(R.string.timedetection), 0))
SntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
log.debug("NTP time: $time System time: ${DateUtil.now()}")
SystemClock.sleep(300)
if (!networkConnected) {
ToastUtils.showToastInUiThread(context, R.string.notconnected)
RxBus.send(EventNtpStatus(MainApp.gs(R.string.notconnected), 99))
} else if (success) {
if (objective.isCompleted(time)) {
objective.accomplishedOn = time
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100))
SystemClock.sleep(1000)
RxBus.send(EventObjectivesUpdateGui())
SystemClock.sleep(100)
scrollToCurrentObjective()
} else {
ToastUtils.showToastInUiThread(context, R.string.requirementnotmet)
RxBus.send(EventNtpStatus(MainApp.gs(R.string.requirementnotmet), 99))
}
} else {
ToastUtils.showToastInUiThread(context, R.string.failedretrievetime)
RxBus.send(EventNtpStatus(MainApp.gs(R.string.failedretrievetime), 99))
}
}
}
}, NetworkChangeReceiver.isConnected())
}, NetworkChangeReceiver.isConnected())
}.start()
}
}
holder.start.setOnClickListener {
holder.start.visibility = View.INVISIBLE
NetworkChangeReceiver.fetch()
NetworkChangeReceiver.grabNetworkStatus(context)
if (objectives_fake.isChecked) {
objective.startedOn = DateUtil.now()
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventObjectivesUpdateGui())
} else
SntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
activity?.runOnUiThread {
holder.start.visibility = View.VISIBLE
// move out of UI thread
Thread {
NtpProgressDialog().show((context as AppCompatActivity).supportFragmentManager, "NtpCheck")
RxBus.send(EventNtpStatus(MainApp.gs(R.string.timedetection), 0))
SntpClient.ntpTime(object : SntpClient.Callback() {
override fun run() {
log.debug("NTP time: $time System time: ${DateUtil.now()}")
SystemClock.sleep(300)
if (!networkConnected) {
ToastUtils.showToastInUiThread(context, R.string.notconnected)
RxBus.send(EventNtpStatus(MainApp.gs(R.string.notconnected), 99))
} else if (success) {
objective.startedOn = time
scrollToCurrentObjective()
startUpdateTimer()
RxBus.send(EventNtpStatus(MainApp.gs(R.string.success), 100))
SystemClock.sleep(1000)
RxBus.send(EventObjectivesUpdateGui())
SystemClock.sleep(100)
scrollToCurrentObjective()
} else {
ToastUtils.showToastInUiThread(context, R.string.failedretrievetime)
RxBus.send(EventNtpStatus(MainApp.gs(R.string.failedretrievetime), 99))
}
}
}
}, NetworkChangeReceiver.isConnected())
}, NetworkChangeReceiver.isConnected())
}.start()
}
holder.revert.setOnClickListener {
objective.accomplishedOn = 0
objective.startedOn = 0
if (position > 0) {
val prevObj = ObjectivesPlugin.objectives[position - 1]
prevObj.accomplishedOn = 0
holder.unStart.setOnClickListener {
OKDialog.showConfirmation(activity, MainApp.gs(R.string.doyouwantresetstart)) {
objective.startedOn = 0
scrollToCurrentObjective()
RxBus.send(EventObjectivesUpdateGui())
}
}
holder.unFinish.setOnClickListener {
objective.accomplishedOn = 0
scrollToCurrentObjective()
RxBus.send(EventObjectivesUpdateGui())
}
if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted) {
if (objective.hasSpecialInput && !objective.isAccomplished && objective.isStarted && objective.specialActionEnabled()) {
// generate random request code if none exists
val request = SP.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt()))
SP.putString(R.string.key_objectives_request_code, request)
@ -310,7 +326,8 @@ class ObjectivesFragment : Fragment() {
val progress: LinearLayout = itemView.findViewById(R.id.objective_progress)
val verify: Button = itemView.findViewById(R.id.objective_verify)
val start: Button = itemView.findViewById(R.id.objective_start)
val revert: Button = itemView.findViewById(R.id.objective_back)
val unFinish: Button = itemView.findViewById(R.id.objective_unfinish)
val unStart: Button = itemView.findViewById(R.id.objective_unstart)
val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint)
val input: EditText = itemView.findViewById(R.id.objective_input)
val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton)

View file

@ -104,8 +104,8 @@ object ObjectivesPlugin : PluginBase(PluginDescription()
fun completeObjectives(activity: Activity, request: String) {
val requestCode = SP.getString(R.string.key_objectives_request_code, "")
var url = SP.getString(R.string.key_nsclientinternal_url, "").toLowerCase()
if (!url.endsWith("\"")) url = "$url/"
val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString()
if (!url.endsWith("/")) url = "$url/"
@Suppress("DEPRECATION") val hashNS = Hashing.sha1().hashString(url + BuildConfig.APPLICATION_ID + "/" + requestCode, Charsets.UTF_8).toString()
if (request.equals(hashNS.substring(0, 10), ignoreCase = true)) {
SP.putLong("Objectives_" + "openloop" + "_started", DateUtil.now())
SP.putLong("Objectives_" + "openloop" + "_accomplished", DateUtil.now())

View file

@ -0,0 +1,84 @@
package info.nightscout.androidaps.plugins.constraints.objectives.dialogs
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.bus.RxBus.toObservable
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventNtpStatus
import info.nightscout.androidaps.utils.FabricPrivacy
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.overview_bolusprogress_dialog.*
import org.slf4j.LoggerFactory
class NtpProgressDialog : DialogFragment() {
private val log = LoggerFactory.getLogger(L.UI)
private val disposable = CompositeDisposable()
private val DEFAULT_STATE = MainApp.gs(R.string.timedetection)
private var state: String = DEFAULT_STATE
private var percent = 0
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
dialog?.setTitle(String.format(MainApp.gs(R.string.objectives)))
isCancelable = false
state = savedInstanceState?.getString("state", DEFAULT_STATE) ?: DEFAULT_STATE
percent = savedInstanceState?.getInt("percent", 0) ?: 0
return inflater.inflate(R.layout.overview_bolusprogress_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
overview_bolusprogress_stop.setOnClickListener { dismiss() }
overview_bolusprogress_status.setText(state)
overview_bolusprogress_progressbar.setMax(100)
overview_bolusprogress_progressbar.setProgress(percent)
overview_bolusprogress_stop.text = MainApp.gs(R.string.close)
}
override fun onResume() {
super.onResume()
if (L.isEnabled(L.UI)) log.debug("onResume")
if (percent == 100) {
dismiss()
return
} else
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
disposable.add(toObservable(EventNtpStatus::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ event: EventNtpStatus ->
if (L.isEnabled(L.UI)) log.debug("Status: " + event.status + " Percent: " + event.percent)
overview_bolusprogress_status?.text = event.status
overview_bolusprogress_progressbar?.progress = event.percent
if (event.percent == 100) {
SystemClock.sleep(100)
dismiss()
}
state = event.status
percent = event.percent
}) { FabricPrivacy.logException(it) }
)
}
override fun onPause() {
if (L.isEnabled(L.UI)) log.debug("onPause")
super.onPause()
disposable.clear()
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString("state", state)
outState.putInt("percent", percent)
super.onSaveInstanceState(outState)
}
}

View file

@ -0,0 +1,5 @@
package info.nightscout.androidaps.plugins.constraints.objectives.events
import info.nightscout.androidaps.events.Event
class EventNtpStatus(val status: String, val percent: Int) : Event()

View file

@ -61,10 +61,6 @@ public abstract class Objective {
return true;
}
public boolean isRevertable() {
return false;
}
public boolean isAccomplished() {
return accomplishedOn != 0 && accomplishedOn < DateUtil.now();
}
@ -107,6 +103,8 @@ public abstract class Objective {
return tasks;
}
public boolean specialActionEnabled() { return true; }
public void specialAction(Activity activity, String input) {}
public abstract class Task {

View file

@ -7,6 +7,7 @@ import java.util.List;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.T;
@ -38,6 +39,11 @@ public class Objective3 extends Objective {
});
}
@Override
public boolean specialActionEnabled() {
return NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth;
}
@Override
public void specialAction(Activity activity, String input) {
ObjectivesPlugin.INSTANCE.completeObjectives(activity, input);

View file

@ -25,9 +25,4 @@ public class Objective5 extends Objective {
}
});
}
@Override
public boolean isRevertable() {
return true;
}
}

View file

@ -160,11 +160,12 @@ public class SignatureVerifierPlugin extends PluginBase implements ConstraintsIn
return sb.toString();
}
private String singleCharUnMap(String shortHash) {
public String singleCharUnMap(String shortHash) {
byte[] array = new byte[shortHash.length()];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < array.length; i++) {
sb.append(String.format("%02x",(int) map.charAt(map.indexOf(shortHash.charAt(i)))));
if (i != 0) sb.append(":");
sb.append(String.format("%02X", 0xFF & map.charAt(map.indexOf(shortHash.charAt(i)))));
}
return sb.toString();
}

View file

@ -1,13 +1,19 @@
package info.nightscout.androidaps.plugins.constraints.versionChecker
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ConstraintsInterface
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.utils.SP
import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt
/**
* Usually we would have a class here.
@ -16,16 +22,23 @@ import java.util.concurrent.TimeUnit
* */
object VersionCheckerPlugin : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.versionChecker)), ConstraintsInterface {
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.versionChecker)), ConstraintsInterface {
private val gracePeriod: GracePeriod
get() = if ((BuildConfig.VERSION_NAME.contains("RC", ignoreCase = true))) {
GracePeriod.RC
} else {
GracePeriod.RELEASE
}
override fun isClosedLoopAllowed(value: Constraint<Boolean>): Constraint<Boolean> {
checkWarning()
triggerCheckVersion()
return if (isOldVersion(GRACE_PERIOD_VERY_OLD))
return if (isOldVersion(gracePeriod.veryOld.daysToMillis()))
value.set(false, MainApp.gs(R.string.very_old_version), this)
else
value
@ -33,41 +46,49 @@ object VersionCheckerPlugin : PluginBase(PluginDescription()
private fun checkWarning() {
val now = System.currentTimeMillis()
if (!SP.contains(R.string.key_last_versionchecker_plugin_warning)) {
SP.putLong(R.string.key_last_versionchecker_plugin_warning, now)
return
}
if (isOldVersion(GRACE_PERIOD_WARNING) && shouldWarnAgain(now)) {
if (isOldVersion(gracePeriod.warning.daysToMillis()) && shouldWarnAgain(now)) {
// store last notification time
SP.putLong(R.string.key_last_versionchecker_plugin_warning, now)
//notify
val message = MainApp.gs(R.string.new_version_warning, Math.round((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / TimeUnit.DAYS.toMillis(1).toDouble()))
val message = MainApp.gs(R.string.new_version_warning,
((now - SP.getLong(R.string.key_last_time_this_version_detected, now)) / 1L.daysToMillis().toDouble()).roundToInt(),
gracePeriod.old,
gracePeriod.veryOld
)
val notification = Notification(Notification.OLDVERSION, message, Notification.NORMAL)
RxBus.send(EventNewNotification(notification))
}
}
private fun shouldWarnAgain(now: Long) =
now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY
now > SP.getLong(R.string.key_last_versionchecker_plugin_warning, 0) + WARN_EVERY
override fun applyMaxIOBConstraints(maxIob: Constraint<Double>): Constraint<Double> =
if (isOldVersion(GRACE_PERIOD_OLD))
maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this)
else
maxIob
if (isOldVersion(gracePeriod.old.daysToMillis()))
maxIob.set(0.toDouble(), MainApp.gs(R.string.old_version), this)
else
maxIob
private fun isOldVersion(gracePeriod: Long): Boolean {
val now = System.currentTimeMillis()
return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod
return now > SP.getLong(R.string.key_last_time_this_version_detected, 0) + gracePeriod
}
val WARN_EVERY = TimeUnit.DAYS.toMillis(1)
val GRACE_PERIOD_WARNING = TimeUnit.DAYS.toMillis(30)
val GRACE_PERIOD_OLD = TimeUnit.DAYS.toMillis(60)
val GRACE_PERIOD_VERY_OLD = TimeUnit.DAYS.toMillis(90)
private val WARN_EVERY = TimeUnit.DAYS.toMillis(1)
}
enum class GracePeriod(val warning: Long, val old: Long, val veryOld: Long) {
RELEASE(30, 60, 90),
RC(0, 7, 14)
}
private fun Long.daysToMillis() = TimeUnit.DAYS.toMillis(this)

View file

@ -15,21 +15,14 @@ import java.io.IOException
import java.net.URL
import java.util.concurrent.TimeUnit
// check network connection
fun isConnected(): Boolean {
val connMgr = MainApp.instance().applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return connMgr.activeNetworkInfo?.isConnected ?: false
}
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()
}
private val log = LoggerFactory.getLogger(L.CORE)
fun triggerCheckVersion() {
if (!SP.contains(R.string.key_last_time_this_version_detected)) {
@ -56,13 +49,34 @@ private fun checkVersion() = if (isConnected()) {
log.debug("Github master version no checked. No connectivity")
fun compareWithCurrentVersion(newVersion: String?, currentVersion: String) {
val comparison: Int? = newVersion?.versionStrip()?.compareTo(currentVersion.versionStrip())
when {
comparison == null -> onVersionNotDetectable()
comparison == 0 -> onSameVersionDetected()
comparison > 0 -> onNewVersionDetected(currentVersion = currentVersion, newVersion = newVersion)
else -> onOlderVersionDetected()
val newVersionElements = newVersion.toNumberList()
val currentVersionElements = currentVersion.toNumberList()
if (newVersionElements == null || newVersionElements.isEmpty()) {
onVersionNotDetectable()
return
}
if (currentVersionElements == null || currentVersionElements.isEmpty()) {
// current version scrambled?!
onNewVersionDetected(currentVersion, newVersion)
return
}
newVersionElements.take(3).forEachIndexed { i, newElem ->
val currElem: Int = currentVersionElements.getOrNull(i)
?: return onNewVersionDetected(currentVersion, newVersion)
(newElem - currElem).let {
when {
it > 0 -> return onNewVersionDetected(currentVersion, newVersion)
it < 0 -> return onOlderVersionDetected()
it == 0 -> Unit
}
}
}
onSameVersionDetected()
}
private fun onOlderVersionDetected() {
@ -75,7 +89,7 @@ fun onSameVersionDetected() {
}
fun onVersionNotDetectable() {
log.debug("fetch failed, ignore and smartcast to non-null")
log.debug("fetch failed")
}
fun onNewVersionDetected(currentVersion: String, newVersion: String?) {
@ -88,14 +102,25 @@ fun onNewVersionDetected(currentVersion: String, newVersion: String?) {
}
}
@Deprecated(replaceWith = ReplaceWith("numericVersionPart()"), message = "Will not work if RCs have another index number in it.")
fun String.versionStrip() = this.mapNotNull {
when (it) {
in '0'..'9' -> it
'.' -> it
else -> null
'.' -> it
else -> null
}
}.joinToString(separator = "")
fun String.numericVersionPart(): String =
"(((\\d+)\\.)+(\\d+))(\\D(.*))?".toRegex().matchEntire(this)?.groupValues?.getOrNull(1) ?: ""
fun String?.toNumberList() =
this?.numericVersionPart().takeIf { !it.isNullOrBlank() }?.split(".")?.map { it.toInt() }
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()
}
val CHECK_EVERY = TimeUnit.DAYS.toMillis(1)
val WARN_EVERY = TimeUnit.DAYS.toMillis(1)

View file

@ -10,8 +10,11 @@ import java.util.List;
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AutomationEvent {
private static final Logger log = LoggerFactory.getLogger(AutomationEvent.class);
private Trigger trigger = new TriggerConnector();
private List<Action> actions = new ArrayList<>();
@ -74,7 +77,7 @@ public class AutomationEvent {
}
o.put("actions", array);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -91,7 +94,7 @@ public class AutomationEvent {
actions.add(Action.instantiate(new JSONObject(array.getString(i))));
}
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -5,10 +5,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.OnStartDragListener
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.SimpleItemTouchHelperCallback
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy
@ -17,11 +21,14 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.automation_fragment.*
class AutomationFragment : Fragment() {
class AutomationFragment : Fragment(), OnStartDragListener {
private var disposable: CompositeDisposable = CompositeDisposable()
private var eventListAdapter: EventListAdapter? = null
private var itemTouchHelper: ItemTouchHelper? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.automation_fragment, container, false)
}
@ -29,7 +36,7 @@ class AutomationFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager, activity)
eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager, activity, this)
automation_eventListView.layoutManager = LinearLayoutManager(context)
automation_eventListView.adapter = eventListAdapter
@ -42,6 +49,10 @@ class AutomationFragment : Fragment() {
fragmentManager?.let { dialog.show(it, "EditEventDialog") }
}
val callback: ItemTouchHelper.Callback = SimpleItemTouchHelperCallback(eventListAdapter!!)
itemTouchHelper = ItemTouchHelper(callback)
itemTouchHelper?.attachToRecyclerView(automation_eventListView)
}
@Synchronized
@ -81,4 +92,8 @@ class AutomationFragment : Fragment() {
automation_logView?.text = sb.toString()
}
override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {
itemTouchHelper?.startDrag(viewHolder);
}
}

View file

@ -161,7 +161,7 @@ object AutomationPlugin : PluginBase(PluginDescription()
private fun processActions() {
if (!isEnabled(PluginType.GENERAL))
return
if (LoopPlugin.getPlugin().isSuspended) {
if (LoopPlugin.getPlugin().isSuspended || !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
if (L.isEnabled(L.AUTOMATION))
log.debug("Loop deactivated")
return

View file

@ -1,9 +1,12 @@
package info.nightscout.androidaps.plugins.general.automation;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
@ -17,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -25,19 +29,26 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperAdapter;
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.ItemTouchHelperViewHolder;
import info.nightscout.androidaps.plugins.general.automation.dragHelpers.OnStartDragListener;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged;
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui;
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
import info.nightscout.androidaps.utils.OKDialog;
class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> implements ItemTouchHelperAdapter {
private final List<AutomationEvent> eventList;
private final FragmentManager fragmentManager;
private final Activity activity;
EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager, Activity activity) {
private final OnStartDragListener mDragStartListener;
EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager, Activity activity, OnStartDragListener dragStartListener) {
this.eventList = events;
this.fragmentManager = fragmentManager;
this.activity = activity;
mDragStartListener = dragStartListener;
}
@NonNull
@ -54,6 +65,7 @@ class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder>
layout.addView(iv);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
final AutomationEvent event = eventList.get(position);
@ -91,16 +103,10 @@ class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder>
RxBus.INSTANCE.send(new EventAutomationDataChanged());
});
// remove event
holder.iconTrash.setOnClickListener(v ->
OKDialog.showConfirmation(activity, MainApp.gs(R.string.removerecord) + " " + event.getTitle(), () -> {
eventList.remove(event);
RxBus.INSTANCE.send(new EventAutomationDataChanged());
})
);
// edit event
holder.rootLayout.setOnClickListener(v -> {
holder.rootLayout.setOnClickListener(v ->
{
//EditEventDialog dialog = EditEventDialog.Companion.newInstance(event, false);
EditEventDialog dialog = new EditEventDialog();
Bundle args = new Bundle();
@ -110,6 +116,17 @@ class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder>
if (fragmentManager != null)
dialog.show(fragmentManager, "EditEventDialog");
});
// Start a drag whenever the handle view it touched
holder.iconSort.setOnTouchListener((v, motionEvent) ->
{
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
return true;
}
return v.onTouchEvent(motionEvent);
});
}
@Override
@ -117,12 +134,33 @@ class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder>
return eventList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(eventList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
RxBus.INSTANCE.send(new EventAutomationDataChanged());
return true;
}
@Override
public void onItemDismiss(int position) {
OKDialog.showConfirmation(activity, MainApp.gs(R.string.removerecord) + " " + eventList.get(position).getTitle(),
() -> {
eventList.remove(position);
notifyItemRemoved(position);
RxBus.INSTANCE.send(new EventAutomationDataChanged());
RxBus.INSTANCE.send(new EventAutomationUpdateGui());
}, () -> {
RxBus.INSTANCE.send(new EventAutomationUpdateGui());
});
}
static class ViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
final RelativeLayout rootLayout;
final LinearLayout iconLayout;
final TextView eventTitle;
final Context context;
final ImageView iconTrash;
final ImageView iconSort;
final CheckBox enabled;
ViewHolder(View view, Context context) {
@ -131,8 +169,18 @@ class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder>
eventTitle = view.findViewById(R.id.viewEventTitle);
rootLayout = view.findViewById(R.id.rootLayout);
iconLayout = view.findViewById(R.id.iconLayout);
iconTrash = view.findViewById(R.id.iconTrash);
iconSort = view.findViewById(R.id.iconSort);
enabled = view.findViewById(R.id.automation_enabled);
}
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault));
}
}
}

View file

@ -11,6 +11,8 @@ import javax.annotation.Nullable;
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
import info.nightscout.androidaps.queue.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
Action ideas:
@ -44,6 +46,7 @@ import info.nightscout.androidaps.queue.Callback;
public abstract class Action {
private static final Logger log = LoggerFactory.getLogger(Action.class);
public Trigger precondition = null;
@ -65,7 +68,7 @@ public abstract class Action {
try {
o.put("type", this.getClass().getName());
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -84,7 +87,7 @@ public abstract class Action {
Class clazz = Class.forName(type);
return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : "");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return null;
}
@ -98,7 +101,7 @@ public abstract class Action {
fromJSON(data.toString());
}
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
}
}

View file

@ -18,8 +18,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithE
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActionLoopSuspend extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionLoopSuspend.class);
public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
@Override
@ -59,7 +63,7 @@ public class ActionLoopSuspend extends Action {
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -70,7 +74,7 @@ public class ActionLoopSuspend extends Action {
JSONObject o = new JSONObject(data);
minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes"));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -20,8 +20,12 @@ import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotifi
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActionNotification extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionNotification.class);
public InputString text = new InputString();
@Override
@ -59,7 +63,7 @@ public class ActionNotification extends Action {
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -70,7 +74,7 @@ public class ActionNotification extends Action {
JSONObject o = new JSONObject(data);
text.setValue(JsonHelper.safeGetString(o, "text"));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -92,7 +92,7 @@ public class ActionProfileSwitch extends Action {
data.put("profileToSwitchTo", inputProfileName.getValue());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -105,7 +105,7 @@ public class ActionProfileSwitch extends Action {
profileName = JsonHelper.safeGetString(d, "profileToSwitchTo");
inputProfileName.setValue(profileName);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -19,8 +19,12 @@ import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuil
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActionProfileSwitchPercent extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionProfileSwitchPercent.class);
InputPercent pct = new InputPercent();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
@ -71,7 +75,7 @@ public class ActionProfileSwitchPercent extends Action {
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -83,7 +87,7 @@ public class ActionProfileSwitchPercent extends Action {
pct.setValue(JsonHelper.safeGetInt(d, "percentage"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -21,6 +21,7 @@ import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.JsonHelper;
public class ActionSendSMS extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionSendSMS.class);
public InputString text = new InputString();
@ -36,7 +37,7 @@ public class ActionSendSMS extends Action {
@Override
public void doAction(Callback callback) {
boolean result = SmsCommunicatorPlugin.getPlugin().sendNotificationToAllNumbers(text.getValue());
boolean result = SmsCommunicatorPlugin.INSTANCE.sendNotificationToAllNumbers(text.getValue());
if (callback != null)
callback.result(new PumpEnactResult().success(result).comment(result ? R.string.ok : R.string.danar_error)).run();
@ -56,7 +57,7 @@ public class ActionSendSMS extends Action {
o.put("type", this.getClass().getName());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -67,7 +68,7 @@ public class ActionSendSMS extends Action {
JSONObject o = new JSONObject(data);
text.setValue(JsonHelper.safeGetString(o, "text"));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -24,8 +24,12 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActionStartTempTarget extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionStartTempTarget.class);
String reason = "";
InputTempTarget value = new InputTempTarget();
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
@ -93,7 +97,7 @@ public class ActionStartTempTarget extends Action {
data.put("durationInMinutes", duration.getMinutes());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -107,7 +111,7 @@ public class ActionStartTempTarget extends Action {
value.setValue(JsonHelper.safeGetDouble(d, "value"));
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -14,8 +14,12 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.JsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ActionStopTempTarget extends Action {
private static final Logger log = LoggerFactory.getLogger(ActionStopTempTarget.class);
String reason = "";
private TempTarget tempTarget;
@ -54,7 +58,7 @@ public class ActionStopTempTarget extends Action {
data.put("reason", reason);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -65,7 +69,7 @@ public class ActionStopTempTarget extends Action {
JSONObject d = new JSONObject(data);
reason = JsonHelper.safeGetString(d, "reason");
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -0,0 +1,33 @@
package info.nightscout.androidaps.plugins.general.automation.dragHelpers
interface ItemTouchHelperAdapter {
/**
* Called when an item has been dragged far enough to trigger a move. This is called every time
* an item is shifted, and **not** at the end of a "drop" event.<br></br>
* <br></br>
* Implementations should call [RecyclerView.Adapter.notifyItemMoved] after
* adjusting the underlying data to reflect this move.
*
* @param fromPosition The start position of the moved item.
* @param toPosition Then resolved position of the moved item.
* @return True if the item was moved to the new adapter position.
*
* @see RecyclerView.getAdapterPositionFor
* @see RecyclerView.ViewHolder.getAdapterPosition
*/
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
/**
* Called when an item has been dismissed by a swipe.<br></br>
* <br></br>
* Implementations should call [RecyclerView.Adapter.notifyItemRemoved] after
* adjusting the underlying data to reflect this removal.
*
* @param position The position of the item dismissed.
*
* @see RecyclerView.getAdapterPositionFor
* @see RecyclerView.ViewHolder.getAdapterPosition
*/
fun onItemDismiss(position: Int)
}

View file

@ -0,0 +1,20 @@
package info.nightscout.androidaps.plugins.general.automation.dragHelpers
/**
* Interface to notify an item ViewHolder of relevant callbacks from [ ].
*
* @author Paul Burke (ipaulpro)
*/
interface ItemTouchHelperViewHolder {
/**
* Called when the [ItemTouchHelper] first registers an item as being moved or swiped.
* Implementations should update the item view to indicate it's active state.
*/
fun onItemSelected()
/**
* Called when the [ItemTouchHelper] has completed the move or swipe, and the active item
* state should be cleared.
*/
fun onItemClear()
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.general.automation.dragHelpers
import androidx.recyclerview.widget.RecyclerView
interface OnStartDragListener {
/**
* Called when a view is requesting a start of a drag.
*
* @param viewHolder The holder of the view to drag.
*/
fun onStartDrag(viewHolder: RecyclerView.ViewHolder)
}

View file

@ -0,0 +1,85 @@
package info.nightscout.androidaps.plugins.general.automation.dragHelpers
import android.graphics.Canvas
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs
/**
* An implementation of [ItemTouchHelper.Callback] that enables basic drag & drop and
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br></br>
*
* Expects the `RecyclerView.Adapter` to listen for [ ] callbacks and the `RecyclerView.ViewHolder` to implement
* [ItemTouchHelperViewHolder].
*
*/
class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() {
override fun isLongPressDragEnabled(): Boolean {
return true
}
override fun isItemViewSwipeEnabled(): Boolean {
return true
}
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int { // Set movement flags based on the layout manager
return if (recyclerView.layoutManager is GridLayoutManager) {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
val swipeFlags = 0
makeMovementFlags(dragFlags, swipeFlags)
} else {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END
makeMovementFlags(dragFlags, swipeFlags)
}
}
override fun onMove(recyclerView: RecyclerView, source: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
if (source.itemViewType != target.itemViewType) {
return false
}
// Notify the adapter of the move
mAdapter.onItemMove(source.adapterPosition, target.adapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) { // Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.adapterPosition)
}
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Fade out the view as it is swiped out of the parent's bounds
val alpha = ALPHA_FULL - abs(dX) / viewHolder.itemView.width.toFloat()
viewHolder.itemView.alpha = alpha
viewHolder.itemView.translationX = dX
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { // We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder is ItemTouchHelperViewHolder) { // Let the view holder know that this item is being moved or dragged
val itemViewHolder: ItemTouchHelperViewHolder = viewHolder
itemViewHolder.onItemSelected()
}
}
super.onSelectedChanged(viewHolder, actionState)
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = ALPHA_FULL
if (viewHolder is ItemTouchHelperViewHolder) { // Tell the view holder it's time to restore the idle state
val itemViewHolder: ItemTouchHelperViewHolder = viewHolder
itemViewHolder.onItemClear()
}
}
companion object {
const val ALPHA_FULL = 1.0f
}
}

View file

@ -25,7 +25,7 @@ public class InputBg extends Element {
public InputBg() {
super();
setUnits(ProfileFunctions.getInstance().getProfileUnits());
setUnits(ProfileFunctions.getSystemUnits());
if (getUnits().equals(Constants.MMOL))
value = MMOL_MIN;
else

View file

@ -37,7 +37,7 @@ public class InputTempTarget extends Element {
public InputTempTarget() {
super();
setUnits(ProfileFunctions.getInstance().getProfileUnits());
setUnits(ProfileFunctions.getSystemUnits());
if (getUnits().equals(Constants.MMOL))
value = 6;
else

View file

@ -1,22 +1,25 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
public abstract class Trigger {
private static final Logger log = LoggerFactory.getLogger(Trigger.class);
TriggerConnector connector = null;
long lastRun;
@ -56,7 +59,7 @@ public abstract class Trigger {
try {
return instantiate(new JSONObject(json));
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return null;
}
@ -69,7 +72,7 @@ public abstract class Trigger {
Class clazz = Class.forName(type);
return ((Trigger) clazz.newInstance()).fromJSON(data.toString());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return null;
}
@ -81,11 +84,11 @@ public abstract class Trigger {
}
@Nullable
Activity scanForActivity(Context cont) {
AppCompatActivity scanForActivity(Context cont) {
if (cont == null)
return null;
else if (cont instanceof Activity)
return (Activity) cont;
else if (cont instanceof AppCompatActivity)
return (AppCompatActivity) cont;
else if (cont instanceof ContextWrapper)
return scanForActivity(((ContextWrapper) cont).getBaseContext());

View file

@ -89,7 +89,7 @@ public class TriggerAutosensValue extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -102,7 +102,7 @@ public class TriggerAutosensValue extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -104,7 +104,7 @@ public class TriggerBg extends Trigger {
data.put("units", bg.getUnits());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -118,7 +118,7 @@ public class TriggerBg extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -86,7 +86,7 @@ public class TriggerBolusAgo extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -99,7 +99,7 @@ public class TriggerBolusAgo extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -87,7 +87,7 @@ public class TriggerCOB extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -100,7 +100,7 @@ public class TriggerCOB extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -162,7 +162,7 @@ public class TriggerConnector extends Trigger {
data.put("triggerList", array);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -179,7 +179,7 @@ public class TriggerConnector extends Trigger {
add(newItem);
}
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -45,7 +45,7 @@ public class TriggerDelta extends Trigger {
public TriggerDelta() {
super();
this.units = ProfileFunctions.getInstance().getProfileUnits();
this.units = ProfileFunctions.getSystemUnits();
initializer();
}
@ -129,7 +129,7 @@ public class TriggerDelta extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -144,7 +144,7 @@ public class TriggerDelta extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -82,7 +82,7 @@ public class TriggerIob extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -95,7 +95,7 @@ public class TriggerIob extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -93,7 +93,7 @@ public class TriggerLocation extends Trigger {
data.put("lastRun", lastRun);
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -108,7 +108,7 @@ public class TriggerLocation extends Trigger {
name.setValue(JsonHelper.safeGetString(d, "name"));
lastRun = JsonHelper.safeGetLong(d, "lastRun");
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -88,7 +88,7 @@ public class TriggerProfilePercent extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -101,7 +101,7 @@ public class TriggerProfilePercent extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -82,7 +82,7 @@ public class TriggerPumpLastConnection extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -95,7 +95,7 @@ public class TriggerPumpLastConnection extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.view.ViewGroup;
@ -9,6 +8,7 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import com.dpro.widgets.WeekdaysPicker;
@ -163,7 +163,7 @@ public class TriggerRecurringTime extends Trigger {
object.put("type", TriggerRecurringTime.class.getName());
object.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return object.toString();
}
@ -181,7 +181,7 @@ public class TriggerRecurringTime extends Trigger {
minute = JsonHelper.safeGetInt(o, "minute");
validTo = JsonHelper.safeGetLong(o, "validTo");
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}
@ -269,6 +269,8 @@ public class TriggerRecurringTime extends Trigger {
weekdaysPicker.setSelectedDays(getSelectedDays());
weekdaysPicker.setOnWeekdaysChangeListener((view, i, list) -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)));
weekdaysPicker.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
weekdaysPicker.setSundayFirstDay(Calendar.getInstance().getFirstDayOfWeek() == Calendar.SUNDAY);
weekdaysPicker.redrawDays();
root.addView(weekdaysPicker);
@ -294,9 +296,9 @@ public class TriggerRecurringTime extends Trigger {
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
AppCompatActivity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
tpd.show(a.getSupportFragmentManager(), "TimePickerDialog");
});
int px = MainApp.dpToPx(10);

View file

@ -74,7 +74,7 @@ public class TriggerTempTarget extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -86,7 +86,7 @@ public class TriggerTempTarget extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -1,12 +1,12 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
@ -65,7 +65,7 @@ public class TriggerTime extends Trigger {
object.put("type", TriggerTime.class.getName());
object.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return object.toString();
}
@ -78,7 +78,7 @@ public class TriggerTime extends Trigger {
lastRun = JsonHelper.safeGetLong(o, "lastRun");
runAt = JsonHelper.safeGetLong(o, "runAt");
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}
@ -142,9 +142,9 @@ public class TriggerTime extends Trigger {
);
dpd.setThemeDark(true);
dpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
AppCompatActivity a = scanForActivity(root.getContext());
if (a != null)
dpd.show(a.getFragmentManager(), "DatePickerDialog");
dpd.show(a.getSupportFragmentManager(), "DatePickerDialog");
});
timeButton.setOnClickListener(view -> {
GregorianCalendar calendar = new GregorianCalendar();
@ -162,9 +162,9 @@ public class TriggerTime extends Trigger {
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
AppCompatActivity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
tpd.show(a.getSupportFragmentManager(), "TimePickerDialog");
});
int px = MainApp.dpToPx(10);

View file

@ -1,12 +1,12 @@
package info.nightscout.androidaps.plugins.general.automation.triggers;
import android.app.Activity;
import android.graphics.Typeface;
import android.text.format.DateFormat;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentManager;
import com.google.common.base.Optional;
@ -18,7 +18,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import info.nightscout.androidaps.MainApp;
@ -33,14 +32,14 @@ import info.nightscout.androidaps.utils.T;
public class TriggerTimeRange extends Trigger {
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
// in minutes since midnight 60 means 1AM
private int start;
private int end;
long timeZoneOffset = DateUtil.getTimeZoneOffsetMs();
public TriggerTimeRange() {
start = getMinSinceMidnight(DateUtil.now());
end = getMinSinceMidnight(DateUtil.now());
}
@ -60,11 +59,11 @@ public class TriggerTimeRange extends Trigger {
return false;
boolean doRun = false;
if ( start < end && start < currentMinSinceMidnight && currentMinSinceMidnight < end)
if (start < end && start < currentMinSinceMidnight && currentMinSinceMidnight < end)
doRun = true;
// handle cases like 10PM to 6AM
else if ( start > end && (start < currentMinSinceMidnight || currentMinSinceMidnight < end))
// handle cases like 10PM to 6AM
else if (start > end && (start < currentMinSinceMidnight || currentMinSinceMidnight < end))
doRun = true;
if (doRun) {
@ -93,7 +92,7 @@ public class TriggerTimeRange extends Trigger {
object.put("type", TriggerTimeRange.class.getName());
object.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
log.debug(object.toString());
return object.toString();
@ -108,7 +107,7 @@ public class TriggerTimeRange extends Trigger {
start = JsonHelper.safeGetInt(o, "start");
end = JsonHelper.safeGetInt(o, "end");
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}
@ -129,8 +128,8 @@ public class TriggerTimeRange extends Trigger {
}
TriggerTimeRange period(int start, int end) {
this.start = getMinSinceMidnight(start*60000);
this.end = getMinSinceMidnight(end*60000);
this.start = getMinSinceMidnight(start * 60000);
this.end = getMinSinceMidnight(end * 60000);
return this;
}
@ -145,7 +144,7 @@ public class TriggerTimeRange extends Trigger {
}
long toMilis(long minutesSinceMidnight) {
return minutesSinceMidnight*60*1000;
return minutesSinceMidnight * 60 * 1000;
}
public int getMinSinceMidnight(long time) {
@ -157,11 +156,11 @@ public class TriggerTimeRange extends Trigger {
return (calendar.get(Calendar.HOUR_OF_DAY) * 60) + calendar.get(Calendar.MINUTE);
}
int getStart(){
int getStart() {
return start;
}
int getEnd(){
int getEnd() {
return end;
}
@ -170,8 +169,8 @@ public class TriggerTimeRange extends Trigger {
TextView label = new TextView(root.getContext());
TextView startButton = new TextView(root.getContext());
TextView endButton = new TextView(root.getContext());
log.debug("Start is: " + start );
log.debug("End is: " + end );
log.debug("Start is: " + start);
log.debug("End is: " + end);
startButton.setText(DateUtil.timeString(toMilis(start) - timeZoneOffset));
endButton.setText(MainApp.gs(R.string.and) + " " + DateUtil.timeString(toMilis(end) - timeZoneOffset));
@ -193,9 +192,9 @@ public class TriggerTimeRange extends Trigger {
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
AppCompatActivity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
tpd.show(a.getSupportFragmentManager(), "TimePickerDialog");
});
endButton.setOnClickListener(view -> {
GregorianCalendar calendar = new GregorianCalendar();
@ -213,9 +212,9 @@ public class TriggerTimeRange extends Trigger {
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
Activity a = scanForActivity(root.getContext());
AppCompatActivity a = scanForActivity(root.getContext());
if (a != null)
tpd.show(a.getFragmentManager(), "TimePickerDialog");
tpd.show(a.getSupportFragmentManager(), "TimePickerDialog");
});
int px = MainApp.dpToPx(10);

View file

@ -85,7 +85,7 @@ public class TriggerWifiSsid extends Trigger {
data.put("comparator", comparator.getValue().toString());
o.put("data", data);
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return o.toString();
}
@ -98,7 +98,7 @@ public class TriggerWifiSsid extends Trigger {
lastRun = JsonHelper.safeGetLong(d, "lastRun");
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
} catch (Exception e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
return this;
}

View file

@ -1,10 +1,8 @@
package info.nightscout.androidaps.plugins.general.careportal.Dialogs;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.format.DateFormat;
@ -20,9 +18,12 @@ import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.fragment.app.DialogFragment;
import com.google.common.collect.Lists;
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
import com.wdullaer.materialdatetimepicker.time.RadialPickerLayout;
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
import org.json.JSONException;
@ -39,7 +40,6 @@ import java.util.List;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.ProfileStore;
import info.nightscout.androidaps.db.BgReading;
@ -51,6 +51,7 @@ import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.careportal.OptionsToShow;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DefaultValueHelper;
@ -61,17 +62,14 @@ import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.SafeParse;
import info.nightscout.androidaps.utils.Translator;
public class NewNSTreatmentDialog extends DialogFragment implements View.OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {
public class NewNSTreatmentDialog extends AppCompatDialogFragment implements View.OnClickListener, DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener {
private static Logger log = LoggerFactory.getLogger(NewNSTreatmentDialog.class);
private Activity context;
private static OptionsToShow options;
private static String event;
Profile profile;
public ProfileStore profileStore;
String units = Constants.MGDL;
TextView eventTypeText;
LinearLayout layoutPercent;
@ -120,18 +118,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
}
@Override
public void onAttach(Activity activity) {
context = activity;
super.onAttach(activity);
}
@Override
public void onDetach() {
super.onDetach();
this.context = null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@ -179,7 +165,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
} else {
ArrayList<CharSequence> profileList;
units = profile != null ? profile.getUnits() : Constants.MGDL;
profileList = profileStore.getProfileList();
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<>(getContext(),
R.layout.spinner_centered, profileList);
@ -190,7 +175,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
profileSpinner.setSelection(p);
}
}
final Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units);
final Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, ProfileFunctions.getSystemUnits());
// temp target
final List<String> reasonList = Lists.newArrayList(
@ -205,8 +190,8 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
double defaultDuration;
double defaultTarget = 0;
if (profile != null && editTemptarget.getValue() == bg) {
double defaultTarget;
if (profile != null && editTemptarget.getValue().equals(bg)) {
defaultTarget = bg;
} else {
//prevent changes on screen rotate
@ -214,17 +199,17 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
boolean erase = false;
String units = ProfileFunctions.getInstance().getProfileUnits();
String units = ProfileFunctions.getSystemUnits();
DefaultValueHelper helper = new DefaultValueHelper();
if (MainApp.gs(R.string.eatingsoon).equals(reasonList.get(position))) {
defaultDuration = helper.determineEatingSoonTTDuration();
defaultTarget = helper.determineEatingSoonTT(units);
defaultTarget = helper.determineEatingSoonTT();
} else if (MainApp.gs(R.string.activity).equals(reasonList.get(position))) {
defaultDuration = helper.determineActivityTTDuration();
defaultTarget = helper.determineActivityTT(units);
defaultTarget = helper.determineActivityTT();
} else if (MainApp.gs(R.string.hypo).equals(reasonList.get(position))) {
defaultDuration = helper.determineHypoTTDuration();
defaultTarget = helper.determineHypoTT(units);
defaultTarget = helper.determineHypoTT();
} else if (editDuration.getValue() != 0) {
defaultDuration = editDuration.getValue();
} else {
@ -249,7 +234,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
});
// bg
bgUnitsView.setText(units);
bgUnitsView.setText(ProfileFunctions.getSystemUnits());
TextWatcher bgTextWatcher = new TextWatcher() {
@ -268,7 +253,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
if (profile == null) {
editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), bgTextWatcher);
editTemptarget.setParams(Constants.MIN_TT_MGDL, Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok));
} else if (units.equals(Constants.MMOL)) {
} else if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) {
editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher);
editTemptarget.setParams(Constants.MIN_TT_MMOL, Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok));
} else {
@ -277,7 +262,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
Double bg1 = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units);
Double bg1 = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, ProfileFunctions.getSystemUnits());
if (savedInstanceState != null && savedInstanceState.getDouble("editBg") != bg1) {
editBg.setValue(savedInstanceState.getDouble("editBg"));
} else {
@ -286,16 +271,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
});
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
editCarbs = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbsinput);
editCarbs = view.findViewById(R.id.careportal_newnstreatment_carbsinput);
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
editInsulin = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_insulininput);
editInsulin = view.findViewById(R.id.careportal_newnstreatment_insulininput);
editInsulin.setParams(0d, 0d, maxInsulin, 0.05d, new DecimalFormat("0.00"), false, view.findViewById(R.id.ok));
editSplit = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_splitinput);
editSplit = view.findViewById(R.id.careportal_newnstreatment_splitinput);
editSplit.setParams(100d, 0d, 100d, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok));
editDuration = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_durationinput);
editDuration = view.findViewById(R.id.careportal_newnstreatment_durationinput);
editDuration.setParams(0d, 0d, Constants.MAX_PROFILE_SWITCH_DURATION, 10d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
TextWatcher percentTextWatcher = new TextWatcher() {
@ -319,7 +304,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
Integer maxPercent = 200;
if (profile != null)
maxPercent = MainApp.getConstraintChecker().getMaxBasalPercentAllowed(profile).value();
editPercent = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentinput);
editPercent = view.findViewById(R.id.careportal_newnstreatment_percentinput);
editPercent.setParams(0d, -100d, (double) maxPercent, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok), percentTextWatcher);
TextWatcher absoluteTextWatcher = new TextWatcher() {
@ -343,16 +328,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
Double maxAbsolute = HardLimits.maxBasal();
if (profile != null)
maxAbsolute = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
editAbsolute = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_absoluteinput);
editAbsolute = view.findViewById(R.id.careportal_newnstreatment_absoluteinput);
editAbsolute.setParams(0d, 0d, maxAbsolute, 0.05d, new DecimalFormat("0.00"), true, view.findViewById(R.id.ok), absoluteTextWatcher);
editCarbTime = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbtimeinput);
editCarbTime = view.findViewById(R.id.careportal_newnstreatment_carbtimeinput);
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
editPercentage = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentage);
editPercentage = view.findViewById(R.id.careportal_newnstreatment_percentage);
editPercentage.setParams(100d, (double) Constants.CPP_MIN_PERCENTAGE, (double) Constants.CPP_MAX_PERCENTAGE, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
editTimeshift = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_timeshift);
editTimeshift = view.findViewById(R.id.careportal_newnstreatment_timeshift);
editTimeshift.setParams(0d, (double) Constants.CPP_MIN_TIMESHIFT, (double) Constants.CPP_MAX_TIMESHIFT, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
ProfileSwitch ps = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now());
@ -369,21 +354,21 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
options.duration = false;
}
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_eventtime_layout), options.date);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_bg_layout), options.bg);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_bgsource_layout), options.bg);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_insulin_layout), options.insulin);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_carbs_layout), options.carbs);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_split_layout), options.split);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_duration_layout), options.duration);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_eventtime_layout), options.date);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_bg_layout), options.bg);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_bgsource_layout), options.bg);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_insulin_layout), options.insulin);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_carbs_layout), options.carbs);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_split_layout), options.split);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_duration_layout), options.duration);
showOrHide(layoutPercent, options.percent);
showOrHide(layoutAbsolute, options.absolute);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_carbtime_layout), options.prebolus);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_profile_layout), options.profile);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_percentage_layout), options.profile);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_timeshift_layout), options.profile);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_reuse_layout), options.profile && ps != null && ps.isCPP);
showOrHide((ViewGroup) view.findViewById(R.id.careportal_newnstreatment_temptarget_layout), options.tempTarget);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_carbtime_layout), options.prebolus);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_profile_layout), options.profile);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_percentage_layout), options.profile);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_timeshift_layout), options.profile);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_reuse_layout), options.profile && ps != null && ps.isCPP);
showOrHide(view.findViewById(R.id.careportal_newnstreatment_temptarget_layout), options.tempTarget);
setCancelable(true);
getDialog().setCanceledOnTouchOutside(false);
@ -423,18 +408,18 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
);
dpd.setThemeDark(true);
dpd.dismissOnPause(true);
dpd.show(context.getFragmentManager(), "Datepickerdialog");
dpd.show(getActivity().getSupportFragmentManager(), "Datepickerdialog");
break;
case R.id.careportal_newnstreatment_eventtime:
TimePickerDialog tpd = TimePickerDialog.newInstance(
this,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
DateFormat.is24HourFormat(context)
DateFormat.is24HourFormat(getContext())
);
tpd.setThemeDark(true);
tpd.dismissOnPause(true);
tpd.show(context.getFragmentManager(), "Timepickerdialog");
tpd.show(getActivity().getSupportFragmentManager(), "Timepickerdialog");
break;
case R.id.ok:
confirmNSTreatmentCreation();
@ -457,7 +442,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
if ((data.size() > 0) &&
(data.get(0).date > millis - 7 * 60 * 1000L) &&
(data.get(0).date < millis + 7 * 60 * 1000L)) {
editBg.setValue(Profile.fromMgdlToUnits(data.get(0).value, units));
editBg.setValue(Profile.fromMgdlToUnits(data.get(0).value, ProfileFunctions.getSystemUnits()));
}
}
@ -471,7 +456,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
@Override
public void onTimeSet(RadialPickerLayout view, int hourOfDay, int minute, int second) {
public void onTimeSet(TimePickerDialog view, int hourOfDay, int minute, int second) {
eventTime.setHours(hourOfDay);
eventTime.setMinutes(minute);
eventTime.setSeconds(this.seconds++); // randomize seconds to prevent creating record of the same time, if user choose time manually
@ -479,7 +464,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
updateBGforDateTime();
}
JSONObject gatherData() {
String enteredBy = SP.getString("careportal_enteredby", "");
JSONObject data = new JSONObject();
@ -585,7 +569,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
data.put("preBolus", SafeParse.stringToDouble(editCarbTime.getText()));
if (!notesEdit.getText().toString().equals(""))
data.put("notes", notesEdit.getText().toString());
data.put("units", units);
data.put("units", ProfileFunctions.getSystemUnits());
if (!enteredBy.equals("")) data.put("enteredBy", enteredBy);
if (options.eventType == R.id.careportal_combobolus) {
Double enteredInsulin = SafeParse.stringToDouble(editInsulin.getText());
@ -611,7 +595,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
ret += MainApp.gs(R.string.treatments_wizard_bg_label);
ret += ": ";
ret += JsonHelper.safeGetObject(data, "glucose", "");
ret += " " + units + "\n";
ret += " " + ProfileFunctions.getSystemUnits() + "\n";
}
if (data.has("glucoseType")) {
ret += MainApp.gs(R.string.careportal_newnstreatment_glucosetype);
@ -704,6 +688,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
void confirmNSTreatmentCreation() {
Context context = getContext();
if (context != null) {
final JSONObject data = gatherData();
final String confirmText = buildConfirmText(data);
@ -717,7 +702,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
}
public void createNSTreatment(JSONObject data) {
void createNSTreatment(JSONObject data) {
if (options.executeProfileSwitch) {
if (data.has("profile")) {
ProfileFunctions.doProfileSwitch(profileStore, JsonHelper.safeGetString(data, "profile"), JsonHelper.safeGetInt(data, "duration"), JsonHelper.safeGetInt(data, "percentage"), JsonHelper.safeGetInt(data, "timeshift"));
@ -734,8 +719,8 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
.reason(reason)
.source(Source.USER);
if (tempTarget.durationInMinutes != 0) {
tempTarget.low(Profile.toMgdl(targetBottom, units))
.high(Profile.toMgdl(targetTop, units));
tempTarget.low(Profile.toMgdl(targetBottom, ProfileFunctions.getSystemUnits()))
.high(Profile.toMgdl(targetTop, ProfileFunctions.getSystemUnits()));
} else {
tempTarget.low(0).high(0);
}

View file

@ -30,6 +30,7 @@ import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.ToastUtils;
/**
@ -113,27 +114,24 @@ public class ImportExportPrefs {
.setMessage(MainApp.gs(R.string.import_from) + " " + file + " ?")
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
String line;
String[] lineParts;
try {
editor.clear();
editor.commit();
SP.clear();
BufferedReader reader = new BufferedReader(new FileReader(file));
while ((line = reader.readLine()) != null) {
lineParts = line.split("::");
if (lineParts.length == 2) {
if (lineParts[1].equals("true") || lineParts[1].equals("false")) {
editor.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1]));
SP.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1]));
} else {
editor.putString(lineParts[0], lineParts[1]);
SP.putString(lineParts[0], lineParts[1]);
}
}
}
reader.close();
editor.commit();
SP.putBoolean(R.string.key_setupwizard_processed, true);
OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> {
log.debug("Exiting");
MainApp.instance().stopKeepAliveService();

View file

@ -6,11 +6,11 @@ import android.widget.CheckBox
import android.widget.LinearLayout
import android.widget.TextView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashActivity
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.logging.L
import kotlinx.android.synthetic.main.activity_logsetting.*
class LogSettingActivity : NoSplashActivity() {
class LogSettingActivity : NoSplashAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -7,9 +7,12 @@ import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.text.Html;
import android.text.Spanned;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -89,7 +92,7 @@ public class NSClientPlugin extends PluginBase {
}
nsClientReceiverDelegate =
new NsClientReceiverDelegate(MainApp.instance().getApplicationContext());
new NsClientReceiverDelegate();
}
public boolean isAllowed() {
@ -104,7 +107,7 @@ public class NSClientPlugin extends PluginBase {
context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
super.onStart();
nsClientReceiverDelegate.registerReceivers();
nsClientReceiverDelegate.grabReceiversState();
disposable.add(RxBus.INSTANCE
.toObservable(EventNSClientStatus.class)
.observeOn(Schedulers.io())
@ -129,7 +132,6 @@ public class NSClientPlugin extends PluginBase {
.subscribe(event -> {
if (nsClientService != null) {
MainApp.instance().getApplicationContext().unbindService(mConnection);
nsClientReceiverDelegate.unregisterReceivers();
}
}, FabricPrivacy::logException)
);
@ -152,11 +154,27 @@ public class NSClientPlugin extends PluginBase {
@Override
protected void onStop() {
MainApp.instance().getApplicationContext().unbindService(mConnection);
nsClientReceiverDelegate.unregisterReceivers();
disposable.clear();
super.onStop();
}
@Override
public void preprocessPreferences(@NotNull PreferenceFragment preferenceFragment) {
super.preprocessPreferences(preferenceFragment);
if (Config.NSCLIENT) {
PreferenceScreen scrnAdvancedSettings = (PreferenceScreen) preferenceFragment.findPreference(MainApp.gs(R.string.key_advancedsettings));
if (scrnAdvancedSettings != null) {
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_warning)));
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_res_critical)));
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_warning)));
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_statuslights_bat_critical)));
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights)));
scrnAdvancedSettings.removePreference(preferenceFragment.findPreference(MainApp.gs(R.string.key_show_statuslights_extended)));
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {

View file

@ -312,21 +312,16 @@ public class NSUpload {
public static void uploadTempTarget(TempTarget tempTarget) {
try {
Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null) {
log.error("Profile is null. Skipping upload");
return;
}
JSONObject data = new JSONObject();
data.put("eventType", CareportalEvent.TEMPORARYTARGET);
data.put("duration", tempTarget.durationInMinutes);
data.put("reason", tempTarget.reason);
data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()));
data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits()));
if (tempTarget.low > 0) {
data.put("reason", tempTarget.reason);
data.put("targetBottom", Profile.fromMgdlToUnits(tempTarget.low, ProfileFunctions.getSystemUnits()));
data.put("targetTop", Profile.fromMgdlToUnits(tempTarget.high, ProfileFunctions.getSystemUnits()));
data.put("units", ProfileFunctions.getSystemUnits());
}
data.put("created_at", DateUtil.toISOString(tempTarget.date));
data.put("units", profile.getUnits());
data.put("enteredBy", MainApp.gs(R.string.app_name));
uploadCareportalEntryToNS(data);
} catch (JSONException e) {
@ -517,6 +512,21 @@ public class NSUpload {
}
}
public static void uploadProfileStore(JSONObject profileStore) {
if (SP.getBoolean(R.string.key_ns_uploadlocalprofile, false)) {
Context context = MainApp.instance().getApplicationContext();
Bundle bundle = new Bundle();
bundle.putString("action", "dbAdd");
bundle.putString("collection", "profile");
bundle.putString("data", String.valueOf(profileStore));
Intent intent = new Intent(Intents.ACTION_DATABASE);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
DbLogger.dbAdd(intent, String.valueOf(profileStore));
}
}
public static void uploadEvent(String careportalEvent, long time, @Nullable String notes) {
Context context = MainApp.instance().getApplicationContext();
Bundle bundle = new Bundle();
@ -594,7 +604,7 @@ public class NSUpload {
} catch (JSONException e) {
e.printStackTrace();
log.error("Unhandled exception", e);
}
}

View file

@ -18,44 +18,19 @@ import info.nightscout.androidaps.utils.SP;
class NsClientReceiverDelegate {
private final Context context;
private NetworkChangeReceiver networkChangeReceiver = NetworkChangeReceiver.instance;
private ChargingStateReceiver chargingStateReceiver = new ChargingStateReceiver();
private boolean allowedChargingState = true;
private boolean allowedNetworkState = true;
boolean allowed = true;
NsClientReceiverDelegate(Context context) {
this.context = context;
}
void registerReceivers() {
void grabReceiversState() {
Context context = MainApp.instance().getApplicationContext();
// register NetworkChangeReceiver --> https://developer.android.com/training/monitoring-device-state/connectivity-monitoring.html
// Nougat is not providing Connectivity-Action anymore ;-(
context.registerReceiver(networkChangeReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
context.registerReceiver(networkChangeReceiver,
new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(context);
if (event != null)
RxBus.INSTANCE.send(event);
EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(context);
if (event != null) RxBus.INSTANCE.send(event);
context.registerReceiver(chargingStateReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
EventChargingState eventChargingState = ChargingStateReceiver.grabChargingState(context);
if (eventChargingState != null) RxBus.INSTANCE.send(eventChargingState);
EventChargingState eventChargingState = chargingStateReceiver.grabChargingState(context);
if (eventChargingState != null)
RxBus.INSTANCE.send(eventChargingState);
}
void unregisterReceivers() {
context.unregisterReceiver(networkChangeReceiver);
context.unregisterReceiver(chargingStateReceiver);
}
void onStatusEvent(EventPreferenceChange ev) {
@ -63,11 +38,11 @@ class NsClientReceiverDelegate {
ev.isChanged(R.string.key_ns_wifi_ssids) ||
ev.isChanged(R.string.key_ns_allowroaming)
) {
EventNetworkChange event = networkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext());
EventNetworkChange event = NetworkChangeReceiver.grabNetworkStatus(MainApp.instance().getApplicationContext());
if (event != null)
RxBus.INSTANCE.send(event);
} else if (ev.isChanged(R.string.key_ns_chargingonly)) {
EventChargingState event = chargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext());
EventChargingState event = ChargingStateReceiver.grabChargingState(MainApp.instance().getApplicationContext());
if (event != null)
RxBus.INSTANCE.send(event);
}
@ -91,7 +66,7 @@ class NsClientReceiverDelegate {
}
}
void processStateChange() {
private void processStateChange() {
boolean newAllowedState = allowedChargingState && allowedNetworkState;
if (newAllowedState != allowed) {
allowed = newAllowedState;
@ -101,7 +76,6 @@ class NsClientReceiverDelegate {
boolean calculateStatus(final EventChargingState ev) {
boolean chargingOnly = SP.getBoolean(R.string.key_ns_chargingonly, false);
boolean newAllowedState = true;
if (!ev.isCharging() && chargingOnly) {
@ -129,8 +103,6 @@ class NsClientReceiverDelegate {
}
}
return newAllowedState;
}
}

View file

@ -760,36 +760,36 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
pvd.show(manager, "ProfileViewDialog");
} else if (item.getTitle().equals(MainApp.gs(R.string.eatingsoon))) {
DefaultValueHelper defHelper = new DefaultValueHelper();
double target = defHelper.determineEatingSoonTT(profile.getUnits());
double target = Profile.toMgdl(defHelper.determineEatingSoonTT(), ProfileFunctions.getSystemUnits());
TempTarget tempTarget = new TempTarget()
.date(System.currentTimeMillis())
.duration(defHelper.determineEatingSoonTTDuration())
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(target, profile.getUnits()))
.high(Profile.toMgdl(target, profile.getUnits()));
.low(target)
.high(target);
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
} else if (item.getTitle().equals(MainApp.gs(R.string.activity))) {
DefaultValueHelper defHelper = new DefaultValueHelper();
double target = defHelper.determineActivityTT(profile.getUnits());
double target = Profile.toMgdl(defHelper.determineActivityTT(), ProfileFunctions.getSystemUnits());
TempTarget tempTarget = new TempTarget()
.date(now())
.duration(defHelper.determineActivityTTDuration())
.reason(MainApp.gs(R.string.activity))
.source(Source.USER)
.low(Profile.toMgdl(target, profile.getUnits()))
.high(Profile.toMgdl(target, profile.getUnits()));
.low(target)
.high(target);
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
} else if (item.getTitle().equals(MainApp.gs(R.string.hypo))) {
DefaultValueHelper defHelper = new DefaultValueHelper();
double target = defHelper.determineHypoTT(profile.getUnits());
double target = Profile.toMgdl(defHelper.determineHypoTT(), ProfileFunctions.getSystemUnits());
TempTarget tempTarget = new TempTarget()
.date(now())
.duration(defHelper.determineHypoTTDuration())
.reason(MainApp.gs(R.string.hypo))
.source(Source.USER)
.low(Profile.toMgdl(target, profile.getUnits()))
.high(Profile.toMgdl(target, profile.getUnits()));
.low(target)
.high(target);
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
} else if (item.getTitle().equals(MainApp.gs(R.string.custom))) {
NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog();
@ -814,7 +814,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
public void onClick(View v) {
boolean xdrip = SourceXdripPlugin.getPlugin().isEnabled(PluginType.BGSOURCE);
boolean dexcom = SourceDexcomPlugin.INSTANCE.isEnabled(PluginType.BGSOURCE);
String units = ProfileFunctions.getInstance().getProfileUnits();
FragmentManager manager = getFragmentManager();
// try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days
@ -1041,9 +1040,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
final Profile profile = ProfileFunctions.getInstance().getProfile();
final String profileName = ProfileFunctions.getInstance().getProfileName();
final String units = profile.getUnits();
final double lowLine = OverviewPlugin.INSTANCE.determineLowLine(units);
final double highLine = OverviewPlugin.INSTANCE.determineHighLine(units);
final String units = ProfileFunctions.getSystemUnits();
final double lowLine = OverviewPlugin.INSTANCE.determineLowLine();
final double highLine = OverviewPlugin.INSTANCE.determineHighLine();
//Start with updating the BG as it is unaffected by loop.
// **** BG value ****
@ -1126,7 +1125,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
} else {
tempTargetView.setTextColor(MainApp.gc(R.color.ribbonTextDefault));
tempTargetView.setBackgroundColor(MainApp.gc(R.color.ribbonDefault));
tempTargetView.setText(Profile.toTargetRangeString(profile.getTargetLow(), profile.getTargetHigh(), units, units));
tempTargetView.setText(Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), Constants.MGDL, units));
tempTargetView.setVisibility(View.VISIBLE);
}
@ -1223,7 +1222,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
extendedBolusView.setVisibility(View.VISIBLE);
}
activeProfileView.setText(ProfileFunctions.getInstance().getProfileName());
activeProfileView.setText(ProfileFunctions.getInstance().getProfileNameWithDuration());
if (profile.getPercentage() != 100 || profile.getTimeshift() != 0) {
activeProfileView.setBackgroundColor(MainApp.gc(R.color.ribbonWarning));
activeProfileView.setTextColor(MainApp.gc(R.color.ribbonTextWarning));

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.general.overview
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.events.EventRefreshOverview
@ -9,7 +8,6 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
@ -65,22 +63,17 @@ object OverviewPlugin : PluginBase(PluginDescription()
super.onStop()
}
fun determineHighLine(units: String): Double {
var highLineSetting = SP.getDouble("high_mark", Profile.fromMgdlToUnits(bgTargetHigh, units))!!
if (highLineSetting < 1)
highLineSetting = Profile.fromMgdlToUnits(180.0, units)
fun determineHighLine(): Double {
var highLineSetting = SP.getDouble(R.string.key_high_mark, bgTargetHigh)
if (highLineSetting < 1) highLineSetting = 180.0
highLineSetting = Profile.toCurrentUnits(highLineSetting)
return highLineSetting
}
fun determineLowLine(): Double {
val profile = ProfileFunctions.getInstance().profile ?: return bgTargetLow
return determineLowLine(profile.units)
}
fun determineLowLine(units: String): Double {
var lowLineSetting = SP.getDouble("low_mark", Profile.fromMgdlToUnits(bgTargetLow, units))!!
if (lowLineSetting < 1)
lowLineSetting = Profile.fromMgdlToUnits(76.0, units)
var lowLineSetting = SP.getDouble(R.string.key_low_mark, bgTargetLow)
if (lowLineSetting < 1) lowLineSetting = 76.0
lowLineSetting = Profile.toCurrentUnits(lowLineSetting)
return lowLineSetting
}
}

View file

@ -12,6 +12,7 @@ import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.general.careportal.CareportalFragment;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.SetWarnColor;
@ -34,10 +35,14 @@ class StatuslightHandler {
applyStatuslight("sage", CareportalEvent.SENSORCHANGE, sageView, "SEN", 164, 166);
double batteryLevel = pump.isInitialized() ? pump.getBatteryLevel() : -1;
applyStatuslightLevel(R.string.key_statuslights_bat_critical, 5.0,
R.string.key_statuslights_bat_warning, 22.0,
batteryView, "BAT", batteryLevel);
if (pump.model() != PumpType.AccuChekCombo) {
double batteryLevel = pump.isInitialized() ? pump.getBatteryLevel() : -1;
applyStatuslightLevel(R.string.key_statuslights_bat_critical, 5.0,
R.string.key_statuslights_bat_warning, 22.0,
batteryView, "BAT", batteryLevel);
} else {
applyStatuslight("bage", CareportalEvent.PUMPBATTERYCHANGE, batteryView, "BAT", 504, 240);
}
}
@ -105,9 +110,14 @@ class StatuslightHandler {
handleAge("sage", CareportalEvent.SENSORCHANGE, sageView, "SEN ",
164, 166);
handleLevel(R.string.key_statuslights_bat_critical, 26.0,
R.string.key_statuslights_bat_warning, 51.0,
batteryView, "BAT ", pump.getBatteryLevel());
if (pump.model() != PumpType.AccuChekCombo) {
handleLevel(R.string.key_statuslights_bat_critical, 26.0,
R.string.key_statuslights_bat_warning, 51.0,
batteryView, "BAT ", pump.getBatteryLevel());
} else {
handleAge("bage", CareportalEvent.PUMPBATTERYCHANGE, batteryView, "BAT ",
336, 240);
}
}
void handleAge(String nsSettingPlugin, String eventName, TextView view, String text,

View file

@ -60,7 +60,7 @@ public class CalibrationDialog extends DialogFragment implements View.OnClickLis
view.findViewById(R.id.ok).setOnClickListener(this);
view.findViewById(R.id.cancel).setOnClickListener(this);
String units = ProfileFunctions.getInstance().getProfileUnits();
String units = ProfileFunctions.getSystemUnits();
Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units);
bgNumber = (NumberPicker) view.findViewById(R.id.overview_calibration_bg);

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.dialogs;
import android.os.Bundle;
import android.os.HandlerThread;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
@ -316,36 +315,36 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
int carbs = editCarbs.getValue().intValue();
Integer carbsAfterConstraints = MainApp.getConstraintChecker().applyCarbsConstraints(new Constraint<>(carbs)).value();
final String units = currentProfile.getUnits();
final String units = ProfileFunctions.getSystemUnits();
DefaultValueHelper helper = new DefaultValueHelper();
int activityTTDuration = helper.determineActivityTTDuration();
double activityTT = helper.determineActivityTT(units);
double activityTT = helper.determineActivityTT();
int eatingSoonTTDuration = helper.determineEatingSoonTTDuration();
double eatingSoonTT = helper.determineEatingSoonTT(units);
double eatingSoonTT = helper.determineEatingSoonTT();
int hypoTTDuration = helper.determineHypoTTDuration();
double hypoTT = helper.determineHypoTT(units);
double hypoTT = helper.determineHypoTT();
List<String> actions = new LinkedList<>();
if (startActivityTTCheckbox.isChecked()) {
String unitLabel = "mg/dl";
if (currentProfile.getUnits().equals(Constants.MMOL)) {
if (units.equals(Constants.MMOL)) {
unitLabel = "mmol/l";
}
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " min)</font>");
}
if (startEatingSoonTTCheckbox.isChecked()) {
if (currentProfile.getUnits().equals(Constants.MMOL)) {
if (units.equals(Constants.MMOL)) {
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)</font>");
} else {
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)</font>");
}
}
if (startHypoTTCheckbox.isChecked()) {
if (currentProfile.getUnits().equals(Constants.MMOL)) {
if (units.equals(Constants.MMOL)) {
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to1Decimal(hypoTT) + " mmol/l (" + hypoTTDuration + " min)</font>");
} else {
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to0Decimal(hypoTT) + " mg/dl (" + hypoTTDuration + " min)</font>");
@ -398,8 +397,8 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
.duration(finalActivityTTDuration)
.reason(MainApp.gs(R.string.activity))
.source(Source.USER)
.low(Profile.toMgdl(finalActivityTT, currentProfile.getUnits()))
.high(Profile.toMgdl(finalActivityTT, currentProfile.getUnits()));
.low(Profile.toMgdl(finalActivityTT, ProfileFunctions.getSystemUnits()))
.high(Profile.toMgdl(finalActivityTT, ProfileFunctions.getSystemUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
} else if (startEatingSoonTTCheckbox.isChecked()) {
TempTarget tempTarget = new TempTarget()
@ -407,8 +406,8 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
.duration(finalEatingSoonTTDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits()))
.high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits()));
.low(Profile.toMgdl(finalEatigSoonTT, ProfileFunctions.getSystemUnits()))
.high(Profile.toMgdl(finalEatigSoonTT, ProfileFunctions.getSystemUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
} else if (startHypoTTCheckbox.isChecked()) {
TempTarget tempTarget = new TempTarget()
@ -416,8 +415,8 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
.duration(finalHypoTTDuration)
.reason(MainApp.gs(R.string.hypo))
.source(Source.USER)
.low(Profile.toMgdl(finalHypoTT, currentProfile.getUnits()))
.high(Profile.toMgdl(finalHypoTT, currentProfile.getUnits()));
.low(Profile.toMgdl(finalHypoTT, ProfileFunctions.getSystemUnits()))
.high(Profile.toMgdl(finalHypoTT, ProfileFunctions.getSystemUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
}

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.general.overview.dialogs;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.HandlerThread;
import androidx.fragment.app.DialogFragment;
import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
@ -56,9 +55,9 @@ import static info.nightscout.androidaps.utils.DateUtil.now;
public class NewInsulinDialog extends DialogFragment implements OnClickListener {
private static Logger log = LoggerFactory.getLogger(NewInsulinDialog.class);
public static final double PLUS1_DEFAULT = 0.5d;
public static final double PLUS2_DEFAULT = 1d;
public static final double PLUS3_DEFAULT = 2d;
private static final double PLUS1_DEFAULT = 0.5d;
private static final double PLUS2_DEFAULT = 1d;
private static final double PLUS3_DEFAULT = 2d;
private CheckBox startEatingSoonTTCheckbox;
private CheckBox recordOnlyCheckbox;
@ -207,9 +206,8 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
okClicked = true;
try {
Profile currentProfile = ProfileFunctions.getInstance().getProfile();
final PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (currentProfile == null || pump == null)
if (pump == null)
return;
Double insulin = SafeParse.stringToDouble(editInsulin.getText());
@ -228,11 +226,11 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration);
eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration;
double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl);
eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl;
double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, ProfileFunctions.getSystemUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl);
eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : ProfileFunctions.getSystemUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl;
if (startEatingSoonTTCheckbox.isChecked()) {
if (currentProfile.getUnits().equals(Constants.MMOL)) {
if (ProfileFunctions.getSystemUnits().equals(Constants.MMOL)) {
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)</font>");
} else
actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "<font color='" + MainApp.gc(R.color.tempTargetConfirmation) + "'>" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)</font>");
@ -272,8 +270,8 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
.duration(finalEatingSoonTTDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits()))
.high(Profile.toMgdl(finalEatigSoonTT, currentProfile.getUnits()));
.low(Profile.toMgdl(finalEatigSoonTT, ProfileFunctions.getSystemUnits()))
.high(Profile.toMgdl(finalEatigSoonTT, ProfileFunctions.getSystemUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
}

View file

@ -114,6 +114,7 @@ class WizardDialog : DialogFragment() {
log.debug("guarding: ok already clicked")
} else {
okClicked = true
calculateInsulin()
parentContext?.let { context ->
wizard?.confirmAndExecute(context)
}
@ -213,7 +214,7 @@ class WizardDialog : DialogFragment() {
}
val profileList: ArrayList<CharSequence>
profileList = profileStore.profileList
profileList = profileStore.getProfileList()
profileList.add(0, MainApp.gs(R.string.active))
context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
@ -221,7 +222,7 @@ class WizardDialog : DialogFragment() {
} ?: return
val units = profile.units
val units = ProfileFunctions.getSystemUnits()
treatments_wizard_bgunits.text = units
if (units == Constants.MGDL)
treatments_wizard_bg_input.setStep(1.0)
@ -301,7 +302,7 @@ class WizardDialog : DialogFragment() {
treatment_wizard_notes.text.toString(), carbTime)
wizard?.let { wizard ->
treatments_wizard_bg.text = String.format(MainApp.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, specificProfile.units)).valueToUnitsToString(specificProfile.units), wizard.sens)
treatments_wizard_bg.text = String.format(MainApp.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, ProfileFunctions.getSystemUnits())).valueToUnitsToString(ProfileFunctions.getSystemUnits()), wizard.sens)
treatments_wizard_bginsulin.text = StringUtils.formatInsulin(wizard.insulinFromBG)
treatments_wizard_carbs.text = String.format(MainApp.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
@ -319,8 +320,8 @@ class WizardDialog : DialogFragment() {
// Trend
if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "")
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, specificProfile.units)
+ " " + specificProfile.units)
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, ProfileFunctions.getSystemUnits())
+ " " + ProfileFunctions.getSystemUnits())
} else {
treatments_wizard_bgtrend.text = ""
}

View file

@ -69,7 +69,7 @@ public class GraphData {
private IobCobCalculatorPlugin iobCobCalculatorPlugin;
public GraphData(GraphView graph, IobCobCalculatorPlugin iobCobCalculatorPlugin) {
units = ProfileFunctions.getInstance().getProfileUnits();
units = ProfileFunctions.getSystemUnits();
this.graph = graph;
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
}
@ -264,9 +264,9 @@ public class GraphData {
TempTarget tt = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(time);
double value;
if (tt == null) {
value = (profile.getTargetLow(time) + profile.getTargetHigh(time)) / 2;
value = Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, ProfileFunctions.getSystemUnits());
} else {
value = Profile.fromMgdlToUnits(tt.target(), profile.getUnits());
value = Profile.fromMgdlToUnits(tt.target(), ProfileFunctions.getSystemUnits());
}
if (lastTarget != value) {
if (lastTarget != -1)

View file

@ -181,7 +181,7 @@ public class PersistentNotificationPlugin extends PluginBase {
line1 = MainApp.gs(R.string.loading);
} else if (ConfigBuilderPlugin.getPlugin().getActiveProfileInterface() != null && ProfileFunctions.getInstance().isProfileValid("Notification")) {
String line1_aa;
String units = ProfileFunctions.getInstance().getProfileUnits();
String units = ProfileFunctions.getSystemUnits();
BgReading lastBG = DatabaseHelper.lastBg();

View file

@ -1,59 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.utils.DateUtil;
class AuthRequest {
private static Logger log = LoggerFactory.getLogger(L.SMS);
Sms requester;
String confirmCode;
private Runnable action;
private long date;
private boolean processed;
private SmsCommunicatorPlugin plugin;
AuthRequest(SmsCommunicatorPlugin plugin, Sms requester, String requestText, String confirmCode, SmsAction action) {
this.requester = requester;
this.confirmCode = confirmCode;
this.action = action;
this.plugin = plugin;
this.date = DateUtil.now();
plugin.sendSMS(new Sms(requester.phoneNumber, requestText));
}
void action(String codeReceived) {
if (processed) {
if (L.isEnabled(L.SMS))
log.debug("Already processed");
return;
}
if (!confirmCode.equals(codeReceived)) {
processed = true;
if (L.isEnabled(L.SMS))
log.debug("Wrong code");
plugin.sendSMS(new Sms(requester.phoneNumber, R.string.sms_wrongcode));
return;
}
if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) {
processed = true;
if (L.isEnabled(L.SMS))
log.debug("Processing confirmed SMS: " + requester.text);
if (action != null)
action.run();
return;
}
if (L.isEnabled(L.SMS))
log.debug("Timed out SMS: " + requester.text);
}
}

View file

@ -0,0 +1,38 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.utils.DateUtil
import org.slf4j.LoggerFactory
class AuthRequest internal constructor(val plugin: SmsCommunicatorPlugin, var requester: Sms, requestText: String, var confirmCode: String, val action: SmsAction) {
private val log = LoggerFactory.getLogger(L.SMS)
private val date = DateUtil.now()
private var processed = false
init {
plugin.sendSMS(Sms(requester.phoneNumber, requestText))
}
fun action(codeReceived: String) {
if (processed) {
if (L.isEnabled(L.SMS)) log.debug("Already processed")
return
}
if (confirmCode != codeReceived) {
processed = true
if (L.isEnabled(L.SMS)) log.debug("Wrong code")
plugin.sendSMS(Sms(requester.phoneNumber, R.string.sms_wrongcode))
return
}
if (DateUtil.now() - date < Constants.SMS_CONFIRM_TIMEOUT) {
processed = true
if (L.isEnabled(L.SMS)) log.debug("Processing confirmed SMS: " + requester.text)
action.run()
return
}
if (L.isEnabled(L.SMS)) log.debug("Timed out SMS: " + requester.text)
}
}

View file

@ -1,42 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
import android.telephony.SmsMessage;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.utils.DateUtil;
class Sms {
String phoneNumber;
String text;
long date;
boolean received = false;
boolean sent = false;
boolean processed = false;
boolean ignored = false;
Sms(SmsMessage message) {
phoneNumber = message.getOriginatingAddress();
text = message.getMessageBody();
date = message.getTimestampMillis();
received = true;
}
Sms(String phoneNumber, String text) {
this.phoneNumber = phoneNumber;
this.text = text;
this.date = DateUtil.now();
sent = true;
}
Sms(String phoneNumber, int textId) {
this.phoneNumber = phoneNumber;
this.text = MainApp.gs(textId);
this.date = DateUtil.now();
sent = true;
}
public String toString() {
return "SMS from " + phoneNumber + ": " + text;
}
}

View file

@ -0,0 +1,40 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.telephony.SmsMessage
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.utils.DateUtil
class Sms {
var phoneNumber: String
var text: String
var date: Long
var received = false
var sent = false
var processed = false
var ignored = false
internal constructor(message: SmsMessage) {
phoneNumber = message.originatingAddress ?: ""
text = message.messageBody
date = message.timestampMillis
received = true
}
internal constructor(phoneNumber: String, text: String) {
this.phoneNumber = phoneNumber
this.text = text
date = DateUtil.now()
sent = true
}
internal constructor(phoneNumber: String, textId: Int) {
this.phoneNumber = phoneNumber
text = MainApp.gs(textId)
date = DateUtil.now()
sent = true
}
override fun toString(): String {
return "SMS from $phoneNumber: $text"
}
}

View file

@ -1,33 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
abstract class SmsAction implements Runnable {
Double aDouble;
Integer anInteger;
Integer secondInteger;
String aString;
SmsAction() {}
SmsAction(Double aDouble) {
this.aDouble = aDouble;
}
SmsAction(Double aDouble, Integer secondInteger) {
this.aDouble = aDouble;
this.secondInteger = secondInteger;
}
SmsAction(String aString, Integer secondInteger) {
this.aString = aString;
this.secondInteger = secondInteger;
}
SmsAction(Integer anInteger) {
this.anInteger = anInteger;
}
SmsAction(Integer anInteger, Integer secondInteger) {
this.anInteger = anInteger;
this.secondInteger = secondInteger;
}
}

View file

@ -0,0 +1,68 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
abstract class SmsAction : Runnable {
var aDouble: Double? = null
var anInteger: Int? = null
var secondInteger: Int? = null
var secondLong: Long? = null
var aString: String? = null
internal constructor()
internal constructor(aDouble: Double) {
this.aDouble = aDouble
}
internal constructor(aDouble: Double, secondInteger: Int) {
this.aDouble = aDouble
this.secondInteger = secondInteger
}
internal constructor(aString: String, secondInteger: Int) {
this.aString = aString
this.secondInteger = secondInteger
}
internal constructor(anInteger: Int) {
this.anInteger = anInteger
}
internal constructor(anInteger: Int, secondInteger: Int) {
this.anInteger = anInteger
this.secondInteger = secondInteger
}
internal constructor(anInteger: Int, secondLong: Long) {
this.anInteger = anInteger
this.secondLong = secondLong
}
fun aDouble(): Double {
return aDouble?.let {
aDouble
} ?: throw IllegalStateException()
}
fun anInteger(): Int {
return anInteger?.let {
anInteger
} ?: throw IllegalStateException()
}
fun secondInteger(): Int {
return secondInteger?.let {
secondInteger
} ?: throw IllegalStateException()
}
fun secondLong(): Long {
return secondLong?.let {
secondLong
} ?: throw IllegalStateException()
}
fun aString(): String {
return aString?.let {
aString
} ?: throw IllegalStateException()
}
}

View file

@ -1,83 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
import android.os.Bundle;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import java.util.Collections;
import java.util.Comparator;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.FabricPrivacy;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
public class SmsCommunicatorFragment extends Fragment {
private CompositeDisposable disposable = new CompositeDisposable();
TextView logView;
public SmsCommunicatorFragment() {
super();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.smscommunicator_fragment, container, false);
logView = (TextView) view.findViewById(R.id.smscommunicator_log);
return view;
}
@Override
public synchronized void onResume() {
super.onResume();
disposable.add(RxBus.INSTANCE
.toObservable(EventSmsCommunicatorUpdateGui.class)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> updateGui(), FabricPrivacy::logException)
);
updateGui();
}
@Override
public synchronized void onPause() {
super.onPause();
disposable.clear();
}
protected void updateGui() {
class CustomComparator implements Comparator<Sms> {
public int compare(Sms object1, Sms object2) {
return (int) (object1.date - object2.date);
}
}
Collections.sort(SmsCommunicatorPlugin.getPlugin().messages, new CustomComparator());
int messagesToShow = 40;
int start = Math.max(0, SmsCommunicatorPlugin.getPlugin().messages.size() - messagesToShow);
String logText = "";
for (int x = start; x < SmsCommunicatorPlugin.getPlugin().messages.size(); x++) {
Sms sms = SmsCommunicatorPlugin.getPlugin().messages.get(x);
if (sms.ignored) {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + "" + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
} else if (sms.received) {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + (sms.processed ? "" : "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
} else if (sms.sent) {
logText += DateUtil.timeString(sms.date) + " &gt;&gt;&gt; " + (sms.processed ? "" : "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>";
}
}
logView.setText(Html.fromHtml(logText));
}
}

View file

@ -0,0 +1,70 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.bus.RxBus.toObservable
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.smscommunicator_fragment.*
import java.util.*
import kotlin.math.max
class SmsCommunicatorFragment : Fragment() {
private val disposable = CompositeDisposable()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.smscommunicator_fragment, container, false)
}
@Synchronized
override fun onResume() {
super.onResume()
disposable.add(toObservable(EventSmsCommunicatorUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ updateGui() }) { FabricPrivacy.logException(it) }
)
updateGui()
}
@Synchronized
override fun onPause() {
super.onPause()
disposable.clear()
}
fun updateGui() {
class CustomComparator : Comparator<Sms> {
override fun compare(object1: Sms, object2: Sms): Int {
return (object1.date - object2.date).toInt()
}
}
Collections.sort(SmsCommunicatorPlugin.messages, CustomComparator())
val messagesToShow = 40
val start = max(0, SmsCommunicatorPlugin.messages.size - messagesToShow)
var logText = ""
for (x in start until SmsCommunicatorPlugin.messages.size) {
val sms = SmsCommunicatorPlugin.messages[x]
when {
sms.ignored -> {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + "" + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
sms.received -> {
logText += DateUtil.timeString(sms.date) + " &lt;&lt;&lt; " + (if (sms.processed) "" else "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
sms.sent -> {
logText += DateUtil.timeString(sms.date) + " &gt;&gt;&gt; " + (if (sms.processed) "" else "") + sms.phoneNumber + " <b>" + sms.text + "</b><br>"
}
}
}
smscommunicator_log?.text = HtmlHelper.fromHtml(logText)
}
}

View file

@ -1,812 +0,0 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.IobTotal;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.ProfileStore;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileInterface;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.queue.Callback;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.FabricPrivacy;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.utils.SafeParse;
import info.nightscout.androidaps.utils.XdripCalibrations;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
/**
* Created by mike on 05.08.2016.
*/
public class SmsCommunicatorPlugin extends PluginBase {
private static Logger log = LoggerFactory.getLogger(L.SMS);
private CompositeDisposable disposable = new CompositeDisposable();
private static SmsCommunicatorPlugin smsCommunicatorPlugin;
public static SmsCommunicatorPlugin getPlugin() {
if (smsCommunicatorPlugin == null) {
smsCommunicatorPlugin = new SmsCommunicatorPlugin();
}
return smsCommunicatorPlugin;
}
List<String> allowedNumbers = new ArrayList<>();
AuthRequest messageToConfirm = null;
long lastRemoteBolusTime = 0;
ArrayList<Sms> messages = new ArrayList<>();
SmsCommunicatorPlugin() {
super(new PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(SmsCommunicatorFragment.class.getName())
.pluginName(R.string.smscommunicator)
.shortName(R.string.smscommunicator_shortname)
.preferencesId(R.xml.pref_smscommunicator)
.description(R.string.description_sms_communicator)
);
processSettings(null);
}
@Override
protected void onStart() {
super.onStart();
disposable.add(RxBus.INSTANCE
.toObservable(EventPreferenceChange.class)
.observeOn(Schedulers.io())
.subscribe(event -> {
processSettings(event);
}, FabricPrivacy::logException)
);
}
@Override
protected void onStop() {
disposable.clear();
super.onStop();
}
private void processSettings(final EventPreferenceChange ev) {
if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) {
String settings = SP.getString(R.string.key_smscommunicator_allowednumbers, "");
allowedNumbers.clear();
String[] substrings = settings.split(";");
for (String number : substrings) {
String cleaned = number.replaceAll("\\s+", "");
allowedNumbers.add(cleaned);
log.debug("Found allowed number: " + cleaned);
}
}
}
boolean isCommand(String command, String number) {
switch (command.toUpperCase()) {
case "BG":
case "LOOP":
case "TREATMENTS":
case "NSCLIENT":
case "PUMP":
case "BASAL":
case "BOLUS":
case "EXTENDED":
case "CAL":
case "PROFILE":
return true;
}
if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(number))
return true;
return false;
}
boolean isAllowedNumber(String number) {
for (String num : allowedNumbers) {
if (num.equals(number)) return true;
}
return false;
}
public void handleNewData(Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle == null) return;
Object[] pdus = (Object[]) bundle.get("pdus");
if (pdus != null) {
// For every SMS message received
for (Object pdu : pdus) {
SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
processSms(new Sms(message));
}
}
}
void processSms(final Sms receivedSms) {
if (!isEnabled(PluginType.GENERAL)) {
log.debug("Ignoring SMS. Plugin disabled.");
return;
}
if (!isAllowedNumber(receivedSms.phoneNumber)) {
log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed");
receivedSms.ignored = true;
messages.add(receivedSms);
RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui());
return;
}
messages.add(receivedSms);
log.debug(receivedSms.toString());
String[] splitted = receivedSms.text.split("\\s+");
boolean remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false);
if (splitted.length > 0 && isCommand(splitted[0].toUpperCase(), receivedSms.phoneNumber)) {
switch (splitted[0].toUpperCase()) {
case "BG":
processBG(splitted, receivedSms);
break;
case "LOOP":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2 || splitted.length == 3)
processLOOP(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "TREATMENTS":
if (splitted.length == 2)
processTREATMENTS(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "NSCLIENT":
if (splitted.length == 2)
processNSCLIENT(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "PUMP":
processPUMP(splitted, receivedSms);
break;
case "PROFILE":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2 || splitted.length == 3)
processPROFILE(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "BASAL":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2 || splitted.length == 3)
processBASAL(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "EXTENDED":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2 || splitted.length == 3)
processEXTENDED(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "BOLUS":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed));
else if (splitted.length == 2 && ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended())
sendSMS(new Sms(receivedSms.phoneNumber, R.string.pumpsuspended));
else if (splitted.length == 2)
processBOLUS(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "CAL":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2)
processCAL(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
default: // expect passCode here
if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(receivedSms.phoneNumber)) {
messageToConfirm.action(splitted[0]);
messageToConfirm = null;
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand));
break;
}
}
RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui());
}
@SuppressWarnings("unused")
private void processBG(String[] splitted, Sms receivedSms) {
BgReading actualBG = DatabaseHelper.actualBg();
BgReading lastBG = DatabaseHelper.lastBg();
String reply = "";
String units = ProfileFunctions.getInstance().getProfileUnits();
if (actualBG != null) {
reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", ";
} else if (lastBG != null) {
Long agoMsec = System.currentTimeMillis() - lastBG.date;
int agoMin = (int) (agoMsec / 60d / 1000d);
reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", ";
}
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
if (glucoseStatus != null)
reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", ";
TreatmentsPlugin.getPlugin().updateTotalIOBTreatments();
IobTotal bolusIob = TreatmentsPlugin.getPlugin().getLastCalculationTreatments().round();
TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals();
IobTotal basalIob = TreatmentsPlugin.getPlugin().getLastCalculationTempBasals().round();
String cobText = MainApp.gs(R.string.value_unavailable_short);
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB");
reply += MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U ("
+ MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U "
+ MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), "
+ MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString();
sendSMS(new Sms(receivedSms.phoneNumber, reply));
receivedSms.processed = true;
}
private void processLOOP(String[] splitted, Sms receivedSms) {
String reply;
switch (splitted[1].toUpperCase()) {
case "DISABLE":
case "STOP":
LoopPlugin loopPlugin = LoopPlugin.getPlugin();
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, false);
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
@Override
public void run() {
RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_STOP"));
String reply = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " +
MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
});
} else {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled));
}
receivedSms.processed = true;
break;
case "ENABLE":
case "START":
loopPlugin = LoopPlugin.getPlugin();
if (!loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, true);
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled));
RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_START"));
} else {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled));
}
receivedSms.processed = true;
break;
case "STATUS":
loopPlugin = LoopPlugin.getPlugin();
if (loopPlugin.isEnabled(PluginType.LOOP)) {
if (loopPlugin.isSuspended())
reply = String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend());
else
reply = MainApp.gs(R.string.smscommunicator_loopisenabled);
} else {
reply = MainApp.gs(R.string.smscommunicator_loopisdisabled);
}
sendSMS(new Sms(receivedSms.phoneNumber, reply));
receivedSms.processed = true;
break;
case "RESUME":
LoopPlugin.getPlugin().suspendTo(0);
RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_RESUME"));
NSUpload.uploadOpenAPSOffline(0);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed));
break;
case "SUSPEND":
int duration = 0;
if (splitted.length == 3)
duration = SafeParse.stringToInt(splitted[2]);
duration = Math.max(0, duration);
duration = Math.min(180, duration);
if (duration == 0) {
receivedSms.processed = true;
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration));
return;
} else {
String passCode = generatePasscode();
reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(duration) {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
@Override
public void run() {
if (result.success) {
LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger * 60L * 1000);
NSUpload.uploadOpenAPSOffline(anInteger * 60);
RxBus.INSTANCE.send(new EventRefreshOverview("SMS_LOOP_SUSPENDED"));
String reply = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " +
MainApp.gs(result.success ? R.string.smscommunicator_tempbasalcanceled : R.string.smscommunicator_tempbasalcancelfailed);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
}
break;
default:
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
}
}
private void processTREATMENTS(String[] splitted, Sms receivedSms) {
if (splitted[1].toUpperCase().equals("REFRESH")) {
TreatmentsPlugin.getPlugin().getService().resetTreatments();
RxBus.INSTANCE.send(new EventNSClientRestart());
sendSMS(new Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT"));
receivedSms.processed = true;
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
private void processNSCLIENT(String[] splitted, Sms receivedSms) {
if (splitted[1].toUpperCase().equals("RESTART")) {
RxBus.INSTANCE.send(new EventNSClientRestart());
sendSMS(new Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT"));
receivedSms.processed = true;
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
@SuppressWarnings("unused")
private void processPUMP(String[] splitted, Sms receivedSms) {
ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() {
@Override
public void run() {
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (result.success) {
if (pump != null) {
String reply = pump.shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
} else {
String reply = MainApp.gs(R.string.readstatusfailed);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
receivedSms.processed = true;
}
private void processPROFILE(String[] splitted, Sms receivedSms) {
// load profiles
ProfileInterface anInterface = ConfigBuilderPlugin.getPlugin().getActiveProfileInterface();
if (anInterface == null) {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured));
receivedSms.processed = true;
return;
}
ProfileStore store = anInterface.getProfile();
if (store == null) {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.notconfigured));
receivedSms.processed = true;
return;
}
final ArrayList<CharSequence> list = store.getProfileList();
if (splitted[1].toUpperCase().equals("STATUS")) {
sendSMS(new Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().getProfileName()));
} else if (splitted[1].toUpperCase().equals("LIST")) {
if (list.isEmpty())
sendSMS(new Sms(receivedSms.phoneNumber, R.string.invalidprofile));
else {
String reply = "";
for (int i = 0; i < list.size(); i++) {
if (i > 0)
reply += "\n";
reply += (i + 1) + ". ";
reply += list.get(i);
}
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
} else {
int pindex = SafeParse.stringToInt(splitted[1]);
int percentage = 100;
if (splitted.length > 2)
percentage = SafeParse.stringToInt(splitted[2]);
if (pindex > list.size())
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else if (percentage == 0)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else if (pindex == 0)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else {
final Profile profile = store.getSpecificProfile((String) list.get(pindex - 1));
if (profile == null)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile));
else {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list.get(pindex - 1), percentage, passCode);
receivedSms.processed = true;
int finalPercentage = percentage;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction((String) list.get(pindex - 1), finalPercentage) {
@Override
public void run() {
ProfileFunctions.doProfileSwitch(store, (String) list.get(pindex - 1), 0, finalPercentage, 0);
sendSMS(new Sms(receivedSms.phoneNumber, R.string.profileswitchcreated));
}
});
}
}
}
receivedSms.processed = true;
}
private void processBASAL(String[] splitted, Sms receivedSms) {
if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
@Override
public void run() {
if (result.success) {
String reply = MainApp.gs(R.string.smscommunicator_tempbasalcanceled);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
} else if (splitted[1].endsWith("%")) {
int tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%"));
int duration = 30;
if (splitted.length > 2)
duration = SafeParse.stringToInt(splitted[2]);
final Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile));
else if (tempBasalPct == 0 && !splitted[1].equals("0%"))
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else if (duration == 0)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else {
tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(new Constraint<>(tempBasalPct), profile).value();
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasalPct, duration) {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalPercent(anInteger, secondInteger, true, profile, new Callback() {
@Override
public void run() {
if (result.success) {
String reply;
if (result.isPercent)
reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration);
else
reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
}
} else {
Double tempBasal = SafeParse.stringToDouble(splitted[1]);
int duration = 30;
if (splitted.length > 2)
duration = SafeParse.stringToInt(splitted[2]);
final Profile profile = ProfileFunctions.getInstance().getProfile();
if (profile == null)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.noprofile));
else if (tempBasal == 0 && !splitted[1].equals("0"))
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else if (duration == 0)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else {
tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(new Constraint<>(tempBasal), profile).value();
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(tempBasal, duration) {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().tempBasalAbsolute(aDouble, secondInteger, true, profile, new Callback() {
@Override
public void run() {
if (result.success) {
String reply;
if (result.isPercent)
reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration);
else
reply = String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_tempbasalfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
}
}
}
private void processEXTENDED(String[] splitted, Sms receivedSms) {
if (splitted[1].toUpperCase().equals("CANCEL") || splitted[1].toUpperCase().equals("STOP")) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelExtended(new Callback() {
@Override
public void run() {
if (result.success) {
String reply = MainApp.gs(R.string.smscommunicator_extendedcanceled);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_extendedcancelfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
} else if (splitted.length != 3) {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
} else {
Double extended = SafeParse.stringToDouble(splitted[1]);
int duration = SafeParse.stringToInt(splitted[2]);
extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(new Constraint<>(extended)).value();
if (extended == 0 || duration == 0)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
else {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(extended, duration) {
@Override
public void run() {
ConfigBuilderPlugin.getPlugin().getCommandQueue().extendedBolus(aDouble, secondInteger, new Callback() {
@Override
public void run() {
if (result.success) {
String reply = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_extendedfailed);
reply += "\n" + ConfigBuilderPlugin.getPlugin().getActivePump().shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
}
}
}
private void processBOLUS(String[] splitted, Sms receivedSms) {
Double bolus = SafeParse.stringToDouble(splitted[1]);
bolus = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolus)).value();
if (bolus > 0d) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(bolus) {
@Override
public void run() {
DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo();
detailedBolusInfo.insulin = aDouble;
detailedBolusInfo.source = Source.USER;
ConfigBuilderPlugin.getPlugin().getCommandQueue().bolus(detailedBolusInfo, new Callback() {
@Override
public void run() {
final boolean resultSuccess = result.success;
final double resultBolusDelivered = result.bolusDelivered;
ConfigBuilderPlugin.getPlugin().getCommandQueue().readStatus("SMS", new Callback() {
@Override
public void run() {
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (resultSuccess) {
String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered);
if (pump != null)
reply += "\n" + pump.shortStatus(true);
lastRemoteBolusTime = DateUtil.now();
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_bolusfailed);
if (pump != null)
reply += "\n" + pump.shortStatus(true);
sendSMS(new Sms(receivedSms.phoneNumber, reply));
}
}
});
}
});
}
});
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
private void processCAL(String[] splitted, Sms receivedSms) {
Double cal = SafeParse.stringToDouble(splitted[1]);
if (cal > 0d) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(cal) {
@Override
public void run() {
boolean result = XdripCalibrations.sendIntent(aDouble);
if (result)
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent));
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed));
}
});
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
public boolean sendNotificationToAllNumbers(String text) {
boolean result = true;
for (int i = 0; i < allowedNumbers.size(); i++) {
Sms sms = new Sms(allowedNumbers.get(i), text);
result = result && sendSMS(sms);
}
return result;
}
private void sendSMSToAllNumbers(Sms sms) {
for (String number : allowedNumbers) {
sms.phoneNumber = number;
sendSMS(sms);
}
}
boolean sendSMS(Sms sms) {
SmsManager smsManager = SmsManager.getDefault();
sms.text = stripAccents(sms.text);
try {
if (L.isEnabled(L.SMS))
log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text);
if (sms.text.getBytes().length <= 140)
smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null);
else {
ArrayList<String> parts = smsManager.divideMessage(sms.text);
smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts,
null, null);
}
messages.add(sms);
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Invalid message body")) {
Notification notification = new Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL);
RxBus.INSTANCE.send(new EventNewNotification(notification));
return false;
} else {
Notification notification = new Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL);
RxBus.INSTANCE.send(new EventNewNotification(notification));
return false;
}
} catch (java.lang.SecurityException e) {
Notification notification = new Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL);
RxBus.INSTANCE.send(new EventNewNotification(notification));
return false;
}
RxBus.INSTANCE.send(new EventSmsCommunicatorUpdateGui());
return true;
}
private String generatePasscode() {
int startChar1 = 'A'; // on iphone 1st char is uppercase :)
String passCode = Character.toString((char) (startChar1 + Math.random() * ('z' - 'a' + 1)));
int startChar2 = Math.random() > 0.5 ? 'a' : 'A';
passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1)));
int startChar3 = Math.random() > 0.5 ? 'a' : 'A';
passCode += Character.toString((char) (startChar3 + Math.random() * ('z' - 'a' + 1)));
passCode = passCode.replace('l', 'k').replace('I', 'J');
return passCode;
}
private static String stripAccents(String s) {
s = Normalizer.normalize(s, Normalizer.Form.NFD);
s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
return s;
}
}

View file

@ -0,0 +1,914 @@
package info.nightscout.androidaps.plugins.general.smsCommunicator
import android.content.Intent
import android.preference.EditTextPreference
import android.preference.Preference
import android.preference.Preference.OnPreferenceChangeListener
import android.preference.PreferenceFragment
import android.telephony.SmsManager
import android.telephony.SmsMessage
import android.text.TextUtils
import com.andreabaccega.widget.ValidatingEditTextPreference
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBus.send
import info.nightscout.androidaps.plugins.bus.RxBus.toObservable
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.smsCommunicator.events.EventSmsCommunicatorUpdateGui
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.*
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import java.text.Normalizer
import java.util.*
object SmsCommunicatorPlugin : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(SmsCommunicatorFragment::class.java.name)
.pluginName(R.string.smscommunicator)
.shortName(R.string.smscommunicator_shortname)
.preferencesId(R.xml.pref_smscommunicator)
.description(R.string.description_sms_communicator)
) {
private val log = LoggerFactory.getLogger(L.SMS)
private val disposable = CompositeDisposable()
var allowedNumbers: MutableList<String> = ArrayList()
var messageToConfirm: AuthRequest? = null
var lastRemoteBolusTime: Long = 0
var messages = ArrayList<Sms>()
val commands = mapOf(
"BG" to "BG",
"LOOP" to "LOOP STOP/DISABLE/START/ENABLE/RESUME/STATUS\nLOOP SUSPEND 20",
"TREATMENTS" to "TREATMENTS REFRESH",
"NSCLIENT" to "NSCLIENT RESTART",
"PUMP" to "PUMP",
"BASAL" to "BASAL STOP/CANCEL\nBASAL 0.3\nBASAL 0.3 20\nBASAL 30%\nBASAL 30% 20\n",
"BOLUS" to "BOLUS 1.2\nBOLUS 1.2 MEAL",
"EXTENDED" to "EXTENDED STOP/CANCEL\nEXTENDED 2 120",
"CAL" to "CAL 5.6",
"PROFILE" to "PROFILE STATUS/LIST\nPROFILE 1\nPROFILE 2 30",
"TARGET" to "TARGET MEAL/ACTIVITY/HYPO/STOP",
"SMS" to "SMS DISABLE/STOP",
"CARBS" to "CARBS 12\nCARBS 12 23:05\nCARBS 12 11:05PM",
"HELP" to "HELP\nHELP command"
)
init {
processSettings(null)
}
override fun onStart() {
super.onStart()
disposable.add(toObservable(EventPreferenceChange::class.java)
.observeOn(Schedulers.io())
.subscribe({ event: EventPreferenceChange? -> processSettings(event) }) { throwable: Throwable? -> FabricPrivacy.logException(throwable) }
)
}
override fun onStop() {
disposable.clear()
super.onStop()
}
override fun preprocessPreferences(preferenceFragment: PreferenceFragment) {
super.preprocessPreferences(preferenceFragment)
val distance = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_remotebolusmindistance)) as ValidatingEditTextPreference?
?: return
val allowedNumbers = preferenceFragment.findPreference(MainApp.gs(R.string.key_smscommunicator_allowednumbers)) as EditTextPreference?
?: return
if (!areMoreNumbers(allowedNumbers.text)) {
distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat))
distance.isEnabled = false
} else {
distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
distance.isEnabled = true
}
allowedNumbers.onPreferenceChangeListener = OnPreferenceChangeListener { _: Preference?, newValue: Any ->
if (!areMoreNumbers(newValue as String)) {
distance.text = (Constants.remoteBolusMinDistance / (60 * 1000L)).toString()
distance.title = (MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ MainApp.gs(R.string.smscommunicator_remotebolusmindistance_caveat))
distance.isEnabled = false
} else {
distance.title = MainApp.gs(R.string.smscommunicator_remotebolusmindistance)
distance.isEnabled = true
}
true
}
}
override fun updatePreferenceSummary(pref: Preference) {
super.updatePreferenceSummary(pref)
if (pref is EditTextPreference) {
val editTextPref = pref
if (pref.getKey().contains("smscommunicator_allowednumbers") && (editTextPref.text == null || TextUtils.isEmpty(editTextPref.text.trim { it <= ' ' }))) {
pref.setSummary(MainApp.gs(R.string.smscommunicator_allowednumbers_summary))
}
}
}
private fun processSettings(ev: EventPreferenceChange?) {
if (ev == null || ev.isChanged(R.string.key_smscommunicator_allowednumbers)) {
val settings = SP.getString(R.string.key_smscommunicator_allowednumbers, "")
allowedNumbers.clear()
val substrings = settings.split(";").toTypedArray()
for (number in substrings) {
val cleaned = number.replace("\\s+".toRegex(), "")
allowedNumbers.add(cleaned)
log.debug("Found allowed number: $cleaned")
}
}
}
fun isCommand(command: String, number: String): Boolean {
var found = false
commands.forEach { (k, _) ->
if (k == command) found = true
}
return found || messageToConfirm?.requester?.phoneNumber == number
}
fun isAllowedNumber(number: String): Boolean {
for (num in allowedNumbers) {
if (num == number) return true
}
return false
}
fun handleNewData(intent: Intent) {
val bundle = intent.extras ?: return
val format = bundle.getString("format") ?: return
val pdus = bundle["pdus"] as Array<*>
for (pdu in pdus) {
val message = SmsMessage.createFromPdu(pdu as ByteArray, format)
processSms(Sms(message))
}
}
fun processSms(receivedSms: Sms) {
if (!isEnabled(PluginType.GENERAL)) {
log.debug("Ignoring SMS. Plugin disabled.")
return
}
if (!isAllowedNumber(receivedSms.phoneNumber)) {
log.debug("Ignoring SMS from: " + receivedSms.phoneNumber + ". Sender not allowed")
receivedSms.ignored = true
messages.add(receivedSms)
send(EventSmsCommunicatorUpdateGui())
return
}
val pump = ConfigBuilderPlugin.getPlugin().activePump ?: return
messages.add(receivedSms)
log.debug(receivedSms.toString())
val splitted = receivedSms.text.split(Regex("\\s+")).toTypedArray()
val remoteCommandsAllowed = SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
if (splitted.isNotEmpty() && isCommand(splitted[0].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) {
when (splitted[0].toUpperCase(Locale.getDefault())) {
"BG" ->
if (splitted.size == 1) processBG(receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"LOOP" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processLOOP(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"TREATMENTS" ->
if (splitted.size == 2) processTREATMENTS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"NSCLIENT" ->
if (splitted.size == 2) processNSCLIENT(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"PUMP" ->
if (splitted.size == 1) processPUMP(receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"PROFILE" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processPROFILE(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"BASAL" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processBASAL(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"EXTENDED" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processEXTENDED(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"BOLUS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 && DateUtil.now() - lastRemoteBolusTime < Constants.remoteBolusMinDistance) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed))
else if (splitted.size == 2 && pump.isSuspended) sendSMS(Sms(receivedSms.phoneNumber, R.string.pumpsuspended))
else if (splitted.size == 2 || splitted.size == 3) processBOLUS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"CARBS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2 || splitted.size == 3) processCARBS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"CAL" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processCAL(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"TARGET" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processTARGET(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"SMS" ->
if (!remoteCommandsAllowed) sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed))
else if (splitted.size == 2) processSMS(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
"HELP" ->
if (splitted.size == 1 || splitted.size == 2) processHELP(splitted, receivedSms)
else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else ->
if (messageToConfirm?.requester?.phoneNumber == receivedSms.phoneNumber) {
messageToConfirm?.action(splitted[0])
messageToConfirm = null
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand))
}
}
send(EventSmsCommunicatorUpdateGui())
}
private fun processBG(receivedSms: Sms) {
val actualBG = DatabaseHelper.actualBg()
val lastBG = DatabaseHelper.lastBg()
var reply = ""
val units = ProfileFunctions.getSystemUnits()
if (actualBG != null) {
reply = MainApp.gs(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "
} else if (lastBG != null) {
val agoMsec = System.currentTimeMillis() - lastBG.date
val agoMin = (agoMsec / 60.0 / 1000.0).toInt()
reply = MainApp.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsToString(units) + " " + String.format(MainApp.gs(R.string.sms_minago), agoMin) + ", "
}
val glucoseStatus = GlucoseStatus.getGlucoseStatusData()
if (glucoseStatus != null) reply += MainApp.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "
TreatmentsPlugin.getPlugin().updateTotalIOBTreatments()
val bolusIob = TreatmentsPlugin.getPlugin().lastCalculationTreatments.round()
TreatmentsPlugin.getPlugin().updateTotalIOBTempBasals()
val basalIob = TreatmentsPlugin.getPlugin().lastCalculationTempBasals.round()
val cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "SMS COB")
reply += (MainApp.gs(R.string.sms_iob) + " " + DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U ("
+ MainApp.gs(R.string.sms_bolus) + " " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U "
+ MainApp.gs(R.string.sms_basal) + " " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U), "
+ MainApp.gs(R.string.cob) + ": " + cobInfo.generateCOBString())
sendSMS(Sms(receivedSms.phoneNumber, reply))
receivedSms.processed = true
}
private fun processLOOP(splitted: Array<String>, receivedSms: Sms) {
when (splitted[1].toUpperCase(Locale.getDefault())) {
"DISABLE", "STOP" -> {
val loopPlugin = LoopPlugin.getPlugin()
if (loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
send(EventRefreshOverview("SMS_LOOP_STOP"))
val replyText = MainApp.gs(R.string.smscommunicator_loophasbeendisabled) + " " +
MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
})
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisdisabled))
receivedSms.processed = true
}
"ENABLE", "START" -> {
val loopPlugin = LoopPlugin.getPlugin()
if (!loopPlugin.isEnabled(PluginType.LOOP)) {
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loophasbeenenabled))
send(EventRefreshOverview("SMS_LOOP_START"))
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopisenabled))
receivedSms.processed = true
}
"STATUS" -> {
val loopPlugin = LoopPlugin.getPlugin()
val reply = if (loopPlugin.isEnabled(PluginType.LOOP)) {
if (loopPlugin.isSuspended()) String.format(MainApp.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend())
else MainApp.gs(R.string.smscommunicator_loopisenabled)
} else
MainApp.gs(R.string.smscommunicator_loopisdisabled)
sendSMS(Sms(receivedSms.phoneNumber, reply))
receivedSms.processed = true
}
"RESUME" -> {
LoopPlugin.getPlugin().suspendTo(0)
send(EventRefreshOverview("SMS_LOOP_RESUME"))
NSUpload.uploadOpenAPSOffline(0.0)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_loopresumed))
}
"SUSPEND" -> {
var duration = 0
if (splitted.size == 3) duration = SafeParse.stringToInt(splitted[2])
duration = Math.max(0, duration)
duration = Math.min(180, duration)
if (duration == 0) {
receivedSms.processed = true
sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_wrongduration))
return
} else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (result.success) {
LoopPlugin.getPlugin().suspendTo(System.currentTimeMillis() + anInteger() * 60L * 1000)
NSUpload.uploadOpenAPSOffline(anInteger() * 60.toDouble())
send(EventRefreshOverview("SMS_LOOP_SUSPENDED"))
val replyText = MainApp.gs(R.string.smscommunicator_loopsuspended) + " " +
MainApp.gs(if (result.success) R.string.smscommunicator_tempbasalcanceled else R.string.smscommunicator_tempbasalcancelfailed)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
else -> sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
}
private fun processTREATMENTS(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "REFRESH") {
TreatmentsPlugin.getPlugin().service.resetTreatments()
send(EventNSClientRestart())
sendSMS(Sms(receivedSms.phoneNumber, "TREATMENTS REFRESH SENT"))
receivedSms.processed = true
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processNSCLIENT(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "RESTART") {
send(EventNSClientRestart())
sendSMS(Sms(receivedSms.phoneNumber, "NSCLIENT RESTART SENT"))
receivedSms.processed = true
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processHELP(splitted: Array<String>, receivedSms: Sms) {
if (splitted.size == 1) {
sendSMS(Sms(receivedSms.phoneNumber, commands.keys.toString().replace("[", "").replace("]", "")))
receivedSms.processed = true
} else if (isCommand(splitted[1].toUpperCase(Locale.getDefault()), receivedSms.phoneNumber)) {
commands[splitted[1].toUpperCase(Locale.getDefault())]?.let {
sendSMS(Sms(receivedSms.phoneNumber, it))
receivedSms.processed = true
}
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processPUMP(receivedSms: Sms) {
ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() {
override fun run() {
val pump = ConfigBuilderPlugin.getPlugin().activePump
if (result.success) {
if (pump != null) {
val reply = pump.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
} else {
val reply = MainApp.gs(R.string.readstatusfailed)
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
}
})
receivedSms.processed = true
}
private fun processPROFILE(splitted: Array<String>, receivedSms: Sms) { // load profiles
val anInterface = ConfigBuilderPlugin.getPlugin().activeProfileInterface
if (anInterface == null) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured))
receivedSms.processed = true
return
}
val store = anInterface.profile
if (store == null) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.notconfigured))
receivedSms.processed = true
return
}
val list = store.getProfileList()
if (splitted[1].toUpperCase(Locale.getDefault()) == "STATUS") {
sendSMS(Sms(receivedSms.phoneNumber, ProfileFunctions.getInstance().profileName))
} else if (splitted[1].toUpperCase(Locale.getDefault()) == "LIST") {
if (list.isEmpty()) sendSMS(Sms(receivedSms.phoneNumber, R.string.invalidprofile))
else {
var reply = ""
for (i in list.indices) {
if (i > 0) reply += "\n"
reply += (i + 1).toString() + ". "
reply += list[i]
}
sendSMS(Sms(receivedSms.phoneNumber, reply))
}
} else {
val pindex = SafeParse.stringToInt(splitted[1])
var percentage = 100
if (splitted.size > 2) percentage = SafeParse.stringToInt(splitted[2])
if (pindex > list.size) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (percentage == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (pindex == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val profile = store.getSpecificProfile(list[pindex - 1] as String)
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_profilereplywithcode), list[pindex - 1], percentage, passCode)
receivedSms.processed = true
val finalPercentage = percentage
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(list[pindex - 1] as String, finalPercentage) {
override fun run() {
ProfileFunctions.doProfileSwitch(store, list[pindex - 1] as String, 0, finalPercentage, 0)
sendSMS(Sms(receivedSms.phoneNumber, R.string.profileswitchcreated))
}
})
}
}
}
receivedSms.processed = true
}
private fun processBASAL(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalstopreplywithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (result.success) {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcanceled)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
} else if (splitted[1].endsWith("%")) {
var tempBasalPct = SafeParse.stringToInt(StringUtils.removeEnd(splitted[1], "%"))
var duration = 30
if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2])
val profile = ProfileFunctions.getInstance().profile
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else if (tempBasalPct == 0 && splitted[1] != "0%") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
tempBasalPct = MainApp.getConstraintChecker().applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value()
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasalPct, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, object : Callback() {
override fun run() {
if (result.success) {
var replyText: String
replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
} else {
var tempBasal = SafeParse.stringToDouble(splitted[1])
var duration = 30
if (splitted.size > 2) duration = SafeParse.stringToInt(splitted[2])
val profile = ProfileFunctions.getInstance().profile
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, R.string.noprofile))
else if (tempBasal == 0.0 && splitted[1] != "0") sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else if (duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
tempBasal = MainApp.getConstraintChecker().applyBasalConstraints(Constraint(tempBasal), profile).value()
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(tempBasal, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, object : Callback() {
override fun run() {
if (result.success) {
var replyText = if (result.isPercent) String.format(MainApp.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration)
else String.format(MainApp.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_tempbasalfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
}
private fun processEXTENDED(splitted: Array<String>, receivedSms: Sms) {
if (splitted[1].toUpperCase(Locale.getDefault()) == "CANCEL" || splitted[1].toUpperCase(Locale.getDefault()) == "STOP") {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.cancelExtended(object : Callback() {
override fun run() {
if (result.success) {
var replyText = MainApp.gs(R.string.smscommunicator_extendedcanceled)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_extendedcancelfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
} else if (splitted.size != 3) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
} else {
var extended = SafeParse.stringToDouble(splitted[1])
val duration = SafeParse.stringToInt(splitted[2])
extended = MainApp.getConstraintChecker().applyExtendedBolusConstraints(Constraint(extended)).value()
if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(extended, duration) {
override fun run() {
ConfigBuilderPlugin.getPlugin().commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() {
override fun run() {
if (result.success) {
var replyText = String.format(MainApp.gs(R.string.smscommunicator_extendedset), aDouble, duration)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_extendedfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
}
private fun processBOLUS(splitted: Array<String>, receivedSms: Sms) {
var bolus = SafeParse.stringToDouble(splitted[1])
val isMeal = splitted.size > 2 && splitted[2].equals("MEAL", ignoreCase = true)
bolus = MainApp.getConstraintChecker().applyBolusConstraints(Constraint(bolus)).value()
if (splitted.size == 3 && !isMeal) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
} else if (bolus > 0.0) {
val passCode = generatePasscode()
val reply = if (isMeal)
String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode)
else
String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(bolus) {
override fun run() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.insulin = aDouble()
detailedBolusInfo.source = Source.USER
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
val resultSuccess = result.success
val resultBolusDelivered = result.bolusDelivered
ConfigBuilderPlugin.getPlugin().commandQueue.readStatus("SMS", object : Callback() {
override fun run() {
if (resultSuccess) {
var replyText = if (isMeal)
String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered)
else
String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
lastRemoteBolusTime = DateUtil.now()
if (isMeal) {
ProfileFunctions.getInstance().profile?.let { currentProfile ->
var eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration)
eatingSoonTTDuration =
if (eatingSoonTTDuration > 0) eatingSoonTTDuration
else Constants.defaultEatingSoonTTDuration
var eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol else Constants.defaultEatingSoonTTmgdl)
eatingSoonTT =
if (eatingSoonTT > 0) eatingSoonTT
else if (currentProfile.units == Constants.MMOL) Constants.defaultEatingSoonTTmmol
else Constants.defaultEatingSoonTTmgdl
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, currentProfile.units))
.high(Profile.toMgdl(eatingSoonTT, currentProfile.units))
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget)
val tt = if (currentProfile.units == Constants.MMOL) {
DecimalFormatter.to1Decimal(eatingSoonTT)
} else DecimalFormatter.to0Decimal(eatingSoonTT)
replyText += "\n" + String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration)
}
}
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_bolusfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processCARBS(splitted: Array<String>, receivedSms: Sms) {
var grams = SafeParse.stringToInt(splitted[1])
var time = DateUtil.now()
if (splitted.size > 2) {
val seconds = DateUtil.toSeconds(splitted[2].toUpperCase(Locale.getDefault()))
val midnight = MidnightTime.calc()
if (seconds == 0 && (!splitted[2].startsWith("00:00") || !splitted[2].startsWith("12:00"))) {
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
return
}
time = midnight + T.secs(seconds.toLong()).msecs()
}
grams = MainApp.getConstraintChecker().applyCarbsConstraints(Constraint(grams)).value()
if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
else {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_carbsreplywithcode), grams, DateUtil.timeString(time), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(grams, time) {
override fun run() {
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.carbs = anInteger().toDouble()
detailedBolusInfo.date = secondLong()
ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
if (result.success) {
var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
} else {
var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed)
replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true)
sendSMS(Sms(receivedSms.phoneNumber, replyText))
}
}
})
}
})
}
}
private fun processTARGET(splitted: Array<String>, receivedSms: Sms) {
val isMeal = splitted[1].equals("MEAL", ignoreCase = true)
val isActivity = splitted[1].equals("ACTIVITY", ignoreCase = true)
val isHypo = splitted[1].equals("HYPO", ignoreCase = true)
val isStop = splitted[1].equals("STOP", ignoreCase = true) || splitted[1].equals("CANCEL", ignoreCase = true)
if (isMeal || isActivity || isHypo) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(Locale.getDefault()), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
val units = ProfileFunctions.getSystemUnits()
var keyDuration = 0
var defaultTargetDuration = 0
var keyTarget = 0
var defaultTargetMMOL = 0.0
var defaultTargetMGDL = 0.0
if (isMeal) {
keyDuration = R.string.key_eatingsoon_duration
defaultTargetDuration = Constants.defaultEatingSoonTTDuration
keyTarget = R.string.key_eatingsoon_target
defaultTargetMMOL = Constants.defaultEatingSoonTTmmol
defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl
} else if (isActivity) {
keyDuration = R.string.key_activity_duration
defaultTargetDuration = Constants.defaultActivityTTDuration
keyTarget = R.string.key_activity_target
defaultTargetMMOL = Constants.defaultActivityTTmmol
defaultTargetMGDL = Constants.defaultActivityTTmgdl
} else if (isHypo) {
keyDuration = R.string.key_hypo_duration
defaultTargetDuration = Constants.defaultHypoTTDuration
keyTarget = R.string.key_hypo_target
defaultTargetMMOL = Constants.defaultHypoTTmmol
defaultTargetMGDL = Constants.defaultHypoTTmgdl
}
var ttDuration = SP.getInt(keyDuration, defaultTargetDuration)
ttDuration = if (ttDuration > 0) ttDuration else defaultTargetDuration
var tt = SP.getDouble(keyTarget, if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL)
tt = Profile.toCurrentUnits(tt)
tt = if (tt > 0) tt else if (units == Constants.MMOL) defaultTargetMMOL else defaultTargetMGDL
val tempTarget = TempTarget()
.date(System.currentTimeMillis())
.duration(ttDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(tt, units))
.high(Profile.toMgdl(tt, units))
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget)
val ttString = if (units == Constants.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt)
val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
} else if (isStop) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetcancel), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
val tempTarget = TempTarget()
.source(Source.USER)
.date(DateUtil.now())
.duration(0)
.low(0.0)
.high(0.0)
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget)
val replyText = String.format(MainApp.gs(R.string.smscommunicator_tt_canceled))
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
} else
sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processSMS(splitted: Array<String>, receivedSms: Sms) {
val isStop = (splitted[1].equals("STOP", ignoreCase = true)
|| splitted[1].equals("DISABLE", ignoreCase = true))
if (isStop) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction() {
override fun run() {
SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
val replyText = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms))
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
private fun processCAL(splitted: Array<String>, receivedSms: Sms) {
val cal = SafeParse.stringToDouble(splitted[1])
if (cal > 0.0) {
val passCode = generatePasscode()
val reply = String.format(MainApp.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(this, receivedSms, reply, passCode, object : SmsAction(cal) {
override fun run() {
val result = XdripCalibrations.sendIntent(aDouble)
if (result) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationsent)) else sendSMS(Sms(receivedSms.phoneNumber, R.string.smscommunicator_calibrationfailed))
}
})
} else sendSMS(Sms(receivedSms.phoneNumber, R.string.wrongformat))
}
fun sendNotificationToAllNumbers(text: String): Boolean {
var result = true
for (i in allowedNumbers.indices) {
val sms = Sms(allowedNumbers[i], text)
result = result && sendSMS(sms)
}
return result
}
private fun sendSMSToAllNumbers(sms: Sms) {
for (number in allowedNumbers) {
sms.phoneNumber = number
sendSMS(sms)
}
}
fun sendSMS(sms: Sms): Boolean {
val smsManager = SmsManager.getDefault()
sms.text = stripAccents(sms.text)
try {
if (L.isEnabled(L.SMS)) log.debug("Sending SMS to " + sms.phoneNumber + ": " + sms.text)
if (sms.text.toByteArray().size <= 140) smsManager.sendTextMessage(sms.phoneNumber, null, sms.text, null, null)
else {
val parts = smsManager.divideMessage(sms.text)
smsManager.sendMultipartTextMessage(sms.phoneNumber, null, parts,
null, null)
}
messages.add(sms)
} catch (e: IllegalArgumentException) {
return if (e.message == "Invalid message body") {
val notification = Notification(Notification.INVALID_MESSAGE_BODY, MainApp.gs(R.string.smscommunicator_messagebody), Notification.NORMAL)
send(EventNewNotification(notification))
false
} else {
val notification = Notification(Notification.INVALID_PHONE_NUMBER, MainApp.gs(R.string.smscommunicator_invalidphonennumber), Notification.NORMAL)
send(EventNewNotification(notification))
false
}
} catch (e: SecurityException) {
val notification = Notification(Notification.MISSING_SMS_PERMISSION, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.NORMAL)
send(EventNewNotification(notification))
return false
}
send(EventSmsCommunicatorUpdateGui())
return true
}
private fun generatePasscode(): String {
val startChar1 = 'A'.toInt() // on iphone 1st char is uppercase :)
var passCode = Character.toString((startChar1 + Math.random() * ('z' - 'a' + 1)).toChar())
val startChar2: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt()
passCode += Character.toString((startChar2 + Math.random() * ('z' - 'a' + 1)).toChar())
val startChar3: Int = if (Math.random() > 0.5) 'a'.toInt() else 'A'.toInt()
passCode += Character.toString((startChar3 + Math.random() * ('z' - 'a' + 1)).toChar())
passCode = passCode.replace('l', 'k').replace('I', 'J')
return passCode
}
private fun stripAccents(str: String): String {
var s = str
s = Normalizer.normalize(s, Normalizer.Form.NFD)
s = s.replace("[\\p{InCombiningDiacriticalMarks}]".toRegex(), "")
return s
}
fun areMoreNumbers(allowednumbers: String?): Boolean {
return allowednumbers?.let {
var countNumbers = 0
val substrings = it.split(";").toTypedArray()
for (number in substrings) {
var cleaned = number.replace(Regex("\\s+"), "")
if (cleaned.length < 4) continue
cleaned = cleaned.replace("+", "")
cleaned = cleaned.replace("-", "")
if (!cleaned.matches(Regex("[0-9]+"))) continue
countNumbers++
}
countNumbers > 1
} ?: false
}
}

Some files were not shown because too many files have changed in this diff Show more