diff --git a/app/build.gradle b/app/build.gradle index f53f766ed4..ed2be13a95 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -52,7 +52,7 @@ def generateGitBuild = { -> stringBuilder.append('NoGitSystemAvailable') } stringBuilder.append('-') - stringBuilder.append((new Date()).format('yyyy.MM.dd')) + stringBuilder.append((new Date()).format('yyyy.MM.dd-HH:mm')) stringBuilder.append('"') return stringBuilder.toString() } @@ -71,7 +71,7 @@ android { targetSdkVersion 25 multiDexEnabled true versionCode 1500 - version "1.60e-dev" + version "2.0a-dev" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", generateGitBuild() testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -107,7 +107,8 @@ android { resValue "string", "app_name", "AndroidAPS" versionName version manifestPlaceholders = [ - appIcon: "@mipmap/blueowl" + appIcon: "@mipmap/ic_launcher", + appIconRound: "@mipmap/ic_launcher_round" ] buildConfigField "boolean", "APS", "true" buildConfigField "boolean", "PUMPDRIVERS", "true" @@ -120,7 +121,8 @@ android { resValue "string", "app_name", "AndroidAPS" versionName version manifestPlaceholders = [ - appIcon: "@mipmap/blueowl" + appIcon: "@mipmap/blueowl", + appIconRound: "@null" ] buildConfigField "boolean", "APS", "false" buildConfigField "boolean", "PUMPDRIVERS", "true" @@ -133,7 +135,8 @@ android { resValue "string", "app_name", "NSClient" versionName version + "-nsclient" manifestPlaceholders = [ - appIcon: "@mipmap/yellowowl" + appIcon: "@mipmap/yellowowl", + appIconRound: "@null" ] buildConfigField "boolean", "APS", "false" buildConfigField "boolean", "PUMPDRIVERS", "false" @@ -146,7 +149,8 @@ android { resValue "string", "app_name", "NSClient" versionName version + "-nsclient" manifestPlaceholders = [ - appIcon: "@mipmap/yellowowl" + appIcon: "@mipmap/yellowowl", + appIconRound: "@null" ] buildConfigField "boolean", "APS", "false" buildConfigField "boolean", "PUMPDRIVERS", "false" diff --git a/app/libs/sightparser-release.aar b/app/libs/sightparser-release.aar index 302b9e836d..1f0c16ee34 100644 Binary files a/app/libs/sightparser-release.aar and b/app/libs/sightparser-release.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4b53b1a76e..11839c5a5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,9 +30,10 @@ android:name=".MainApp" android:allowBackup="true" android:icon="${appIcon}" + android:roundIcon="${appIconRound}" android:label="@string/app_name" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme.NoActionBar"> @@ -49,6 +50,7 @@ android:theme="@style/Theme.AppCompat.Translucent" /> + @@ -90,6 +92,8 @@ + + @@ -169,7 +173,11 @@ - + + + @@ -177,12 +185,20 @@ + + + - \ No newline at end of file + diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 84bd684cf6..c2db0f270b 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -406,8 +406,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { console.error("SMB disabled (no enableSMB preferences active)"); } - // enable UAM (if enabled in preferences) if SMB is enabled - var enableUAM=(profile.enableUAM && enableSMB); + // enable UAM (if enabled in preferences) + var enableUAM=(profile.enableUAM); //console.error(meal_data); @@ -942,10 +942,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); if (durationReq < 0) { durationReq = 0; - // don't set a temp longer than 120 minutes + // don't set an SMB zero temp longer than 60 minutess } else { durationReq = round(durationReq/30)*30; - durationReq = Math.min(120,Math.max(0,durationReq)); + durationReq = Math.min(60,Math.max(0,durationReq)); } //console.error(durationReq); //rT.reason += "insulinReq " + insulinReq + "; " diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml index d6facdb7dd..a09a312301 100644 --- a/app/src/main/assets/logback.xml +++ b/app/src/main/assets/logback.xml @@ -1,36 +1,39 @@ - - - - ${EXT_FILES_DIR}/AndroidAPS.log - + + + + ${EXT_FILES_DIR}/AndroidAPS.log + - ${EXT_FILES_DIR}/AndroidAPS._%d{yyyy-MM-dd}_%d{HH-mm-ss, aux}_.%i.zip + ${EXT_FILES_DIR}/AndroidAPS._%d{yyyy-MM-dd}_%d{HH-mm-ss, aux}_.%i.zip + - + 5MB 120 - - - %d{HH:mm:ss.SSS} [%thread] %-5level [%class:%line]: %msg%n - - + + + %d{HH:mm:ss.SSS} [%thread] %-5level [%class{0}.%M\(\):%line]: %msg%n + + - - %logger{0} - - - [%thread] %-5level [%class:%line]: %msg%n - - - - - - - - + + %logger{0} + + + [%thread] [%class{0}.%M\(\):%line]: %msg%n + + + + + + + + diff --git a/app/src/main/blueowl-web.png b/app/src/main/blueowl-web.png new file mode 100644 index 0000000000..22339763e2 Binary files /dev/null and b/app/src/main/blueowl-web.png differ diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000000..22339763e2 Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/ic_launcher_round-web.png b/app/src/main/ic_launcher_round-web.png new file mode 100644 index 0000000000..22339763e2 Binary files /dev/null and b/app/src/main/ic_launcher_round-web.png differ diff --git a/app/src/main/java/info/nightscout/androidaps/Constants.java b/app/src/main/java/info/nightscout/androidaps/Constants.java index 56fd2b6de9..a66d4e77f1 100644 --- a/app/src/main/java/info/nightscout/androidaps/Constants.java +++ b/app/src/main/java/info/nightscout/androidaps/Constants.java @@ -12,7 +12,7 @@ public class Constants { public static final double MMOLL_TO_MGDL = 18; // 18.0182; public static final double MGDL_TO_MMOLL = 1 / MMOLL_TO_MGDL; - public static final double defaultDIA = 3d; + public static final double defaultDIA = 5d; public static final Double REALLYHIGHBASALRATE = 1111111d; public static final Integer REALLYHIGHPERCENTBASALRATE = 1111111; diff --git a/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java index c308cd2efe..636db14086 100644 --- a/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/HistoryBrowseActivity.java @@ -176,13 +176,19 @@ public class HistoryBrowseActivity extends AppCompatActivity { activity.runOnUiThread(new Runnable() { @Override public void run() { - updateGUI("EventAutosensCalculationFinished"); + synchronized (HistoryBrowseActivity.this) { + updateGUI("EventAutosensCalculationFinished"); + } } }); } } void updateGUI(String from) { + + if (noProfile == null || buttonDate == null || buttonZoom == null || bgGraph == null || iobGraph == null || seekBar == null) + return; + final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); final Profile profile = MainApp.getConfigBuilder().getProfile(); diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 9a80c61904..8a19a05f4d 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -1,35 +1,34 @@ package info.nightscout.androidaps; -import android.Manifest; -import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Rect; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; +import android.os.PersistableBundle; import android.os.PowerManager; -import android.provider.Settings; +import android.support.annotation.Nullable; +import android.support.design.widget.NavigationView; +import android.support.design.widget.TabLayout; import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.PopupMenu; +import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.text.util.Linkify; -import android.view.MenuInflater; +import android.util.TypedValue; +import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -import android.widget.ImageButton; +import android.widget.LinearLayout; import android.widget.TextView; import com.joanzapata.iconify.Iconify; @@ -47,67 +46,128 @@ import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Food.FoodPlugin; -import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; -import info.nightscout.androidaps.tabs.SlidingTabLayout; +import info.nightscout.androidaps.setupwizard.SetupWizardActivity; import info.nightscout.androidaps.tabs.TabPageAdapter; +import info.nightscout.utils.AndroidPermission; import info.nightscout.utils.ImportExportPrefs; import info.nightscout.utils.LocaleHelper; import info.nightscout.utils.LogDialog; import info.nightscout.utils.OKDialog; import info.nightscout.utils.PasswordProtection; import info.nightscout.utils.SP; -import info.nightscout.utils.ToastUtils; -public class MainActivity extends AppCompatActivity implements View.OnClickListener { +public class MainActivity extends AppCompatActivity { private static Logger log = LoggerFactory.getLogger(MainActivity.class); - static final int CASE_STORAGE = 0x1; - static final int CASE_SMS = 0x2; - static final int CASE_LOCATION = 0x3; - - private boolean askForSMS = false; - private boolean askForLocation = true; - - ImageButton menuButton; - protected PowerManager.WakeLock mWakeLock; + private ActionBarDrawerToggle actionBarDrawerToggle; + + private MenuItem pluginPreferencesMenuItem; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Iconify.with(new FontAwesomeModule()); - LocaleHelper.onCreate(this, "en"); - setContentView(R.layout.activity_main); - menuButton = (ImageButton) findViewById(R.id.overview_menuButton); - menuButton.setOnClickListener(this); - checkEula(); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - askForPermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE}, CASE_STORAGE); - } - askForBatteryOptimizationPermission(); - doMigrations(); if (Config.logFunctionCalls) log.debug("onCreate"); - onStatusEvent(new EventSetWakeLock(SP.getBoolean("lockscreen", false))); + Iconify.with(new FontAwesomeModule()); + LocaleHelper.onCreate(this, "en"); + + setContentView(R.layout.activity_main); + setSupportActionBar(findViewById(R.id.toolbar)); + getSupportActionBar().setDisplayShowTitleEnabled(false); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); + actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.open_navigation, R.string.close_navigation); + drawerLayout.addDrawerListener(actionBarDrawerToggle); + actionBarDrawerToggle.syncState(); + + // initialize screen wake lock + onEventPreferenceChange(new EventPreferenceChange(R.string.key_keep_screen_on)); + + doMigrations(); registerBus(); - setUpTabs(false); + setupTabs(); + setupViews(false); + + final ViewPager viewPager = findViewById(R.id.pager); + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + checkPluginPreferences(viewPager); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + } + + private void checkPluginPreferences(ViewPager viewPager) { + if (pluginPreferencesMenuItem == null) return; + if (((TabPageAdapter) viewPager.getAdapter()).getPluginAt(viewPager.getCurrentItem()).getPreferencesId() != -1) + pluginPreferencesMenuItem.setEnabled(true); + else pluginPreferencesMenuItem.setEnabled(false); + } + + @Override + public void onPostCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { + super.onPostCreate(savedInstanceState, persistentState); + actionBarDrawerToggle.syncState(); + } + + @Override + protected void onResume() { + super.onResume(); + + if (!SP.getBoolean(R.string.key_setupwizard_processed, false)) { + Intent intent = new Intent(this, SetupWizardActivity.class); + startActivity(intent); + } else { + checkEula(); + } + + AndroidPermission.notifyForStoragePermission(this); + AndroidPermission.notifyForBatteryOptimizationPermission(this); + if (BuildConfig.APS || BuildConfig.PUMPCONTROL) { + AndroidPermission.notifyForLocationPermissions(this); + AndroidPermission.notifyForSMSPermissions(this); + } + + MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.MAIN)); + } + + @Override + public void onDestroy() { + if (mWakeLock != null) + if (mWakeLock.isHeld()) + mWakeLock.release(); + super.onDestroy(); } @Subscribe - public void onStatusEvent(final EventSetWakeLock ev) { - final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (ev.lock) { - mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AAPS"); - if (!mWakeLock.isHeld()) - mWakeLock.acquire(); - } else { - if (mWakeLock != null && mWakeLock.isHeld()) - mWakeLock.release(); + public void onEventPreferenceChange(final EventPreferenceChange ev) { + if (ev.isChanged(R.string.key_keep_screen_on)) { + boolean keepScreenOn = SP.getBoolean(R.string.key_keep_screen_on, false); + final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (keepScreenOn) { + mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "AAPS"); + if (!mWakeLock.isHeld()) + mWakeLock.acquire(); + } else { + if (mWakeLock != null && mWakeLock.isHeld()) + mWakeLock.release(); + } } } @@ -115,39 +175,75 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe public void onStatusEvent(final EventRefreshGui ev) { String lang = SP.getString("language", "en"); LocaleHelper.setLocale(getApplicationContext(), lang); - runOnUiThread(new Runnable() { - @Override - public void run() { - if (ev.recreate) { - recreate(); - } else { - try { // activity may be destroyed - setUpTabs(true); - } catch (IllegalStateException e) { - log.error("Unhandled exception", e); - } + runOnUiThread(() -> { + if (ev.recreate) { + recreate(); + } else { + try { // activity may be destroyed + setupTabs(); + setupViews(true); + } catch (IllegalStateException e) { + log.error("Unhandled exception", e); } - - boolean lockScreen = BuildConfig.NSCLIENTOLNY && SP.getBoolean("lockscreen", false); - if (lockScreen) - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - else - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + + boolean keepScreenOn = BuildConfig.NSCLIENTOLNY && SP.getBoolean(R.string.key_keep_screen_on, false); + if (keepScreenOn) + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); }); } - private void setUpTabs(boolean switchToLast) { + private void setupViews(boolean switchToLast) { TabPageAdapter pageAdapter = new TabPageAdapter(getSupportFragmentManager(), this); + NavigationView navigationView = findViewById(R.id.navigation_view); + navigationView.setNavigationItemSelectedListener(menuItem -> { + return true; + }); + Menu menu = navigationView.getMenu(); + menu.clear(); for (PluginBase p : MainApp.getPluginsList()) { pageAdapter.registerNewFragment(p); + if (p.hasFragment() && !p.isFragmentVisible() && p.isEnabled(p.pluginDescription.getType()) && !p.pluginDescription.neverVisible) { + MenuItem menuItem = menu.add(p.getName()); + menuItem.setCheckable(true); + menuItem.setOnMenuItemClickListener(item -> { + Intent intent = new Intent(this, SingleFragmentActivity.class); + intent.putExtra("plugin", MainApp.getPluginsList().indexOf(p)); + startActivity(intent); + ((DrawerLayout) findViewById(R.id.drawer_layout)).closeDrawers(); + return true; + }); + } } - ViewPager mPager = (ViewPager) findViewById(R.id.pager); + ViewPager mPager = findViewById(R.id.pager); mPager.setAdapter(pageAdapter); - SlidingTabLayout mTabs = (SlidingTabLayout) findViewById(R.id.tabs); - mTabs.setViewPager(mPager); if (switchToLast) mPager.setCurrentItem(pageAdapter.getCount() - 1, false); + checkPluginPreferences(mPager); + } + + private void setupTabs() { + ViewPager viewPager = findViewById(R.id.pager); + TabLayout normalTabs = findViewById(R.id.tabs_normal); + normalTabs.setupWithViewPager(viewPager, true); + TabLayout compactTabs = findViewById(R.id.tabs_compact); + compactTabs.setupWithViewPager(viewPager, true); + Toolbar toolbar = findViewById(R.id.toolbar); + if (SP.getBoolean("short_tabtitles", false)) { + normalTabs.setVisibility(View.GONE); + compactTabs.setVisibility(View.VISIBLE); + toolbar.setLayoutParams(new LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, (int) getResources().getDimension(R.dimen.compact_height))); + } else { + normalTabs.setVisibility(View.VISIBLE); + compactTabs.setVisibility(View.GONE); + TypedValue typedValue = new TypedValue(); + if (getTheme().resolveAttribute(R.attr.actionBarSize, typedValue, true)) { + toolbar.setLayoutParams(new LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, + TypedValue.complexToDimensionPixelSize(typedValue.data, getResources().getDisplayMetrics()))); + } + } } private void registerBus() { @@ -205,120 +301,22 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } } - //check for sms permission if enable in prefernces - @Subscribe - public void onStatusEvent(final EventPreferenceChange ev) { - if (ev.isChanged(R.string.key_smscommunicator_remotecommandsallowed)) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - synchronized (this) { - if (SP.getBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)) { - setAskForSMS(); - } - } - } - } - } - - private synchronized void setAskForSMS() { - askForSMS = true; - } - - @Override - protected void onResume() { - super.onResume(); - askForSMSPermissions(); - askForLocationPermissions(); - MainApp.bus().post(new EventFeatureRunning(EventFeatureRunning.Feature.MAIN)); - } - - @Override - public void onDestroy() { - if (mWakeLock != null) - if (mWakeLock.isHeld()) - mWakeLock.release(); - super.onDestroy(); - } - - private void askForBatteryOptimizationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - final String packageName = getPackageName(); - - final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (!pm.isIgnoringBatteryOptimizations(packageName)) { - log.debug("Requesting ignore battery optimization"); - - OKDialog.show(this, MainApp.gs(R.string.pleaseallowpermission), String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), new Runnable() { - - @Override - public void run() { - try { - final Intent intent = new Intent(); - - // ignoring battery optimizations required for constant connection - intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - intent.setData(Uri.parse("package:" + packageName)); - startActivity(intent); - - } catch (ActivityNotFoundException e) { - final String msg = MainApp.gs(R.string.batteryoptimalizationerror); - ToastUtils.showToastInUiThread(getApplicationContext(), msg); - log.error(msg); - } - } - }); - } - } - } - - private synchronized void askForSMSPermissions() { - if (askForSMS) { //only when settings were changed an MainActivity resumes. - askForSMS = false; - if (SP.getBoolean(R.string.smscommunicator_remotecommandsallowed, false)) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - askForPermission(new String[]{Manifest.permission.RECEIVE_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.RECEIVE_MMS}, CASE_SMS); - } - } - } - } - - private synchronized void askForLocationPermissions() { - if (askForLocation) { //only when settings were changed an MainActivity resumes. - askForLocation = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - askForPermission(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION}, CASE_LOCATION); - } - } - } - - private void askForPermission(String[] permission, Integer requestCode) { - boolean test = false; - for (int i = 0; i < permission.length; i++) { - test = test || (ContextCompat.checkSelfPermission(this, permission[i]) != PackageManager.PERMISSION_GRANTED); - } - if (test) { - ActivityCompat.requestPermissions(this, permission, requestCode); - } - } - @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (permissions.length != 0) { if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { switch (requestCode) { - case CASE_STORAGE: + case AndroidPermission.CASE_STORAGE: //show dialog after permission is granted AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setMessage(R.string.alert_dialog_storage_permission_text); alert.setPositiveButton(R.string.ok, null); alert.show(); break; - case CASE_LOCATION: - case CASE_SMS: + case AndroidPermission.CASE_LOCATION: + case AndroidPermission.CASE_SMS: + case AndroidPermission.CASE_BATTERY: break; } } @@ -343,96 +341,97 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe } @Override - public void onClick(final View v) { - final Activity activity = this; - switch (v.getId()) { - case R.id.overview_menuButton: - PopupMenu popup = new PopupMenu(v.getContext(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.menu_main, popup.getMenu()); - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - int id = item.getItemId(); - switch (id) { - case R.id.nav_preferences: - PasswordProtection.QueryPassword(v.getContext(), R.string.settings_password, "settings_password", new Runnable() { - @Override - public void run() { - Intent i = new Intent(v.getContext(), PreferencesActivity.class); - i.putExtra("id", -1); - startActivity(i); - } - }, null); - break; - case R.id.nav_historybrowser: - startActivity(new Intent(v.getContext(), HistoryBrowseActivity.class)); - break; - case R.id.nav_resetdb: - new AlertDialog.Builder(v.getContext()) - .setTitle(R.string.nav_resetdb) - .setMessage(R.string.reset_db_confirm) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - MainApp.getDbHelper().resetDatabases(); - // should be handled by Plugin-Interface and - // additional service interface and plugin registry - FoodPlugin.getPlugin().getService().resetFood(); - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - } - }) - .create() - .show(); - break; - case R.id.nav_export: - ImportExportPrefs.verifyStoragePermissions(activity); - ImportExportPrefs.exportSharedPreferences(activity); - break; - case R.id.nav_import: - ImportExportPrefs.verifyStoragePermissions(activity); - ImportExportPrefs.importSharedPreferences(activity); - break; - case R.id.nav_show_logcat: - LogDialog.showLogcat(v.getContext()); - break; - case R.id.nav_about: - AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext()); - builder.setTitle(MainApp.gs(R.string.app_name) + " " + BuildConfig.VERSION); - if (Config.NSCLIENT || Config.G5UPLOADER) - builder.setIcon(R.mipmap.yellowowl); - else - builder.setIcon(R.mipmap.blueowl); - String message = "Build: " + BuildConfig.BUILDVERSION + "\n"; - message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n"; - message += MainApp.gs(R.string.configbuilder_nightscoutversion_label) + " " + ConfigBuilderPlugin.nightscoutVersionName; - if (MainApp.engineeringMode) - message += "\n" + MainApp.gs(R.string.engineering_mode_enabled); - message += MainApp.gs(R.string.about_link_urls); - final SpannableString messageSpanned = new SpannableString(message); - Linkify.addLinks(messageSpanned, Linkify.WEB_URLS); - builder.setMessage(messageSpanned); - builder.setPositiveButton(MainApp.gs(R.string.ok), null); - AlertDialog alertDialog = builder.create(); - alertDialog.show(); - ((TextView)alertDialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); - break; - case R.id.nav_exit: - log.debug("Exiting"); - MainApp.instance().stopKeepAliveService(); - MainApp.bus().post(new EventAppExit()); - MainApp.closeDbHelper(); - finish(); - System.runFinalization(); - System.exit(0); - break; - } - return false; - } - }); - popup.show(); - break; + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences); + menu.findItem(R.id.nav_historybrowser).setVisible(MainApp.devBranch); + checkPluginPreferences(findViewById(R.id.pager)); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + switch (id) { + case R.id.nav_preferences: + PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(this, PreferencesActivity.class); + i.putExtra("id", -1); + startActivity(i); + }, null); + return true; + case R.id.nav_historybrowser: + startActivity(new Intent(this, HistoryBrowseActivity.class)); + return true; + case R.id.nav_setupwizard: + startActivity(new Intent(this, SetupWizardActivity.class)); + return true; + case R.id.nav_resetdb: + new AlertDialog.Builder(this) + .setTitle(R.string.nav_resetdb) + .setMessage(R.string.reset_db_confirm) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, (dialog, which) -> { + MainApp.getDbHelper().resetDatabases(); + // should be handled by Plugin-Interface and + // additional service interface and plugin registry + FoodPlugin.getPlugin().getService().resetFood(); + TreatmentsPlugin.getPlugin().getService().resetTreatments(); + }) + .create() + .show(); + return true; + case R.id.nav_export: + ImportExportPrefs.verifyStoragePermissions(this); + ImportExportPrefs.exportSharedPreferences(this); + return true; + case R.id.nav_import: + ImportExportPrefs.verifyStoragePermissions(this); + ImportExportPrefs.importSharedPreferences(this); + return true; + case R.id.nav_show_logcat: + LogDialog.showLogcat(this); + return true; + case R.id.nav_about: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(MainApp.gs(R.string.app_name) + " " + BuildConfig.VERSION); + if (Config.NSCLIENT || Config.G5UPLOADER) + builder.setIcon(R.mipmap.yellowowl); + else + builder.setIcon(R.mipmap.blueowl); + String message = "Build: " + BuildConfig.BUILDVERSION + "\n"; + message += "Flavor: " + BuildConfig.FLAVOR + BuildConfig.BUILD_TYPE + "\n"; + message += MainApp.gs(R.string.configbuilder_nightscoutversion_label) + " " + ConfigBuilderPlugin.nightscoutVersionName; + if (MainApp.engineeringMode) + message += "\n" + MainApp.gs(R.string.engineering_mode_enabled); + message += MainApp.gs(R.string.about_link_urls); + final SpannableString messageSpanned = new SpannableString(message); + Linkify.addLinks(messageSpanned, Linkify.WEB_URLS); + builder.setMessage(messageSpanned); + builder.setPositiveButton(MainApp.gs(R.string.ok), null); + AlertDialog alertDialog = builder.create(); + alertDialog.show(); + ((TextView) alertDialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); + return true; + case R.id.nav_exit: + log.debug("Exiting"); + MainApp.instance().stopKeepAliveService(); + MainApp.bus().post(new EventAppExit()); + MainApp.closeDbHelper(); + finish(); + System.runFinalization(); + System.exit(0); + return true; + case R.id.nav_plugin_preferences: + ViewPager viewPager = findViewById(R.id.pager); + final PluginBase plugin = ((TabPageAdapter) viewPager.getAdapter()).getPluginAt(viewPager.getCurrentItem()); + PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(this, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + startActivity(i); + }, null); + return true; } + return actionBarDrawerToggle.onOptionsItemSelected(item); } } diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index c7ea215d19..683658918e 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -9,6 +9,7 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.os.SystemClock; import android.support.annotation.Nullable; +import android.support.annotation.PluralsRes; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -67,14 +68,16 @@ import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin; import info.nightscout.androidaps.plugins.PumpMDI.MDIPlugin; import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +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.SmsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.Source.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.Source.SourceMM640gPlugin; import info.nightscout.androidaps.plugins.Source.SourceNSClientPlugin; +import info.nightscout.androidaps.plugins.Source.SourcePoctechPlugin; import info.nightscout.androidaps.plugins.Source.SourceXdripPlugin; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.Wear.WearPlugin; @@ -157,6 +160,7 @@ public class MainApp extends Application { pluginsList.add(SensitivityOref0Plugin.getPlugin()); pluginsList.add(SensitivityAAPSPlugin.getPlugin()); pluginsList.add(SensitivityWeightedAveragePlugin.getPlugin()); + pluginsList.add(SensitivityOref1Plugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRPlugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRKoreanPlugin.getPlugin()); if (Config.HWPUMPS) pluginsList.add(DanaRv2Plugin.getPlugin()); @@ -188,30 +192,22 @@ public class MainApp extends Application { pluginsList.add(SourceGlimpPlugin.getPlugin()); if (!Config.NSCLIENT) pluginsList.add(SourceDexcomG5Plugin.getPlugin()); + if (!Config.NSCLIENT) + pluginsList.add(SourcePoctechPlugin.getPlugin()); if (Config.SMSCOMMUNICATORENABLED) pluginsList.add(SmsCommunicatorPlugin.getPlugin()); pluginsList.add(FoodPlugin.getPlugin()); pluginsList.add(WearPlugin.initPlugin(this)); pluginsList.add(StatuslinePlugin.initPlugin(this)); - pluginsList.add(new PersistentNotificationPlugin(this)); + pluginsList.add(PersistentNotificationPlugin.getPlugin()); pluginsList.add(NSClientPlugin.getPlugin()); pluginsList.add(sConfigBuilder = ConfigBuilderPlugin.getPlugin()); MainApp.getConfigBuilder().initialize(); } - NSUpload.uploadAppStart(); - if (Config.NSCLIENT) - FabricPrivacy.getInstance().logCustom(new CustomEvent("AppStart-NSClient")); - else if (Config.G5UPLOADER) - FabricPrivacy.getInstance().logCustom(new CustomEvent("AppStart-G5Uploader")); - else if (Config.PUMPCONTROL) - FabricPrivacy.getInstance().logCustom(new CustomEvent("AppStart-PumpControl")); - else if (MainApp.getConstraintChecker().isClosedLoopAllowed().value()) - FabricPrivacy.getInstance().logCustom(new CustomEvent("AppStart-ClosedLoop")); - else - FabricPrivacy.getInstance().logCustom(new CustomEvent("AppStart-OpenLoop")); + NSUpload.uploadAppStart(); final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); if (pump != null) { @@ -319,6 +315,10 @@ public class MainApp extends Application { return sResources.getString(id, args); } + public static String gq(@PluralsRes int id, int quantity, Object... args) { + return sResources.getQuantityString(id, quantity, args); + } + public static int gc(int id) { return sResources.getColor(id); } diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index 1069f1a157..b84f407b33 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -31,9 +31,10 @@ import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin; import info.nightscout.androidaps.plugins.PumpMedtronic.MedtronicPumpPlugin; import info.nightscout.androidaps.plugins.PumpInsight.InsightPlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +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.SmsCommunicator.SmsCommunicatorPlugin; import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.Wear.WearPlugin; @@ -154,6 +155,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResourceIfEnabled(SensitivityAAPSPlugin.getPlugin(), PluginType.SENSITIVITY); addPreferencesFromResourceIfEnabled(SensitivityWeightedAveragePlugin.getPlugin(), PluginType.SENSITIVITY); addPreferencesFromResourceIfEnabled(SensitivityOref0Plugin.getPlugin(), PluginType.SENSITIVITY); + addPreferencesFromResourceIfEnabled(SensitivityOref1Plugin.getPlugin(), PluginType.SENSITIVITY); if (Config.HWPUMPS) { addPreferencesFromResourceIfEnabled(DanaRPlugin.getPlugin(), PluginType.PUMP); diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index 881f2e3bac..ca7d760cc4 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.ProfileStore; @@ -38,6 +39,7 @@ import info.nightscout.androidaps.plugins.Source.SourceDexcomG5Plugin; import info.nightscout.androidaps.plugins.Source.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.Source.SourceMM640gPlugin; import info.nightscout.androidaps.plugins.Source.SourceNSClientPlugin; +import info.nightscout.androidaps.plugins.Source.SourcePoctechPlugin; import info.nightscout.androidaps.plugins.Source.SourceXdripPlugin; import info.nightscout.androidaps.receivers.DataReceiver; import info.nightscout.utils.BundleLogger; @@ -54,6 +56,7 @@ public class DataService extends IntentService { boolean mm640gEnabled = false; boolean glimpEnabled = false; boolean dexcomG5Enabled = false; + boolean poctechEnabled = false; public DataService() { super("DataService"); @@ -70,36 +73,49 @@ public class DataService extends IntentService { mm640gEnabled = false; glimpEnabled = false; dexcomG5Enabled = false; + poctechEnabled = false; } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourceXdripPlugin.class)) { xDripEnabled = true; nsClientEnabled = false; mm640gEnabled = false; glimpEnabled = false; dexcomG5Enabled = false; + poctechEnabled = false; } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourceNSClientPlugin.class)) { xDripEnabled = false; nsClientEnabled = true; mm640gEnabled = false; glimpEnabled = false; dexcomG5Enabled = false; + poctechEnabled = false; } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourceMM640gPlugin.class)) { xDripEnabled = false; nsClientEnabled = false; mm640gEnabled = true; glimpEnabled = false; dexcomG5Enabled = false; + poctechEnabled = false; } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourceGlimpPlugin.class)) { xDripEnabled = false; nsClientEnabled = false; mm640gEnabled = false; glimpEnabled = true; dexcomG5Enabled = false; + poctechEnabled = false; } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourceDexcomG5Plugin.class)) { xDripEnabled = false; nsClientEnabled = false; mm640gEnabled = false; glimpEnabled = false; dexcomG5Enabled = true; + poctechEnabled = false; + } else if (ConfigBuilderPlugin.getPlugin().getActiveBgSource().getClass().equals(SourcePoctechPlugin.class)) { + xDripEnabled = false; + nsClientEnabled = false; + mm640gEnabled = false; + glimpEnabled = false; + dexcomG5Enabled = false; + poctechEnabled = true; } boolean isNSProfile = MainApp.getConfigBuilder().getActiveProfileInterface() != null && MainApp.getConfigBuilder().getActiveProfileInterface().getClass().equals(NSProfilePlugin.class); @@ -129,6 +145,10 @@ public class DataService extends IntentService { if (dexcomG5Enabled) { handleNewDataFromDexcomG5(intent); } + } else if (Intents.POCTECH_BG.equals(action)) { + if (poctechEnabled) { + handleNewDataFromPoctech(intent); + } } else if (Intents.ACTION_NEW_SGV.equals(action)) { if (nsClientEnabled || SP.getBoolean(R.string.key_ns_autobackfill, true)) handleNewDataFromNSClient(intent); @@ -250,6 +270,41 @@ public class DataService extends IntentService { } } + private void handleNewDataFromPoctech(Intent intent) { + + Bundle bundle = intent.getExtras(); + if (bundle == null) return; + + BgReading bgReading = new BgReading(); + + String data = bundle.getString("data"); + log.debug("Received Poctech Data", data); + + try { + JSONArray jsonArray = new JSONArray(data); + log.debug("Received Poctech Data size:" + jsonArray.length()); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject json = jsonArray.getJSONObject(i); + bgReading.value = json.getDouble("current"); + bgReading.direction = json.getString("direction"); + bgReading.date = json.getLong("date"); + bgReading.raw = json.getDouble("raw"); + if (JsonHelper.safeGetString(json, "units", Constants.MGDL).equals("mmol/L")) + bgReading.value = bgReading.value * Constants.MMOLL_TO_MGDL; + boolean isNew = MainApp.getDbHelper().createIfNotExists(bgReading, "Poctech"); + if (isNew && SP.getBoolean(R.string.key_dexcomg5_nsupload, false)) { + NSUpload.uploadBg(bgReading); + } + if (isNew && SP.getBoolean(R.string.key_dexcomg5_xdripupload, false)) { + NSUpload.sendToXdrip(bgReading); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + } + } + private void handleNewDataFromMM640g(Intent intent) { Bundle bundle = intent.getExtras(); if (bundle == null) return; @@ -427,9 +482,7 @@ public class DataService extends IntentService { if (bundles.containsKey("sgv")) { String sgvstring = bundles.getString("sgv"); JSONObject sgvJson = new JSONObject(sgvstring); - NSSgv nsSgv = new NSSgv(sgvJson); - BgReading bgReading = new BgReading(nsSgv); - MainApp.getDbHelper().createIfNotExists(bgReading, "NS"); + storeSgv(sgvJson); } if (bundles.containsKey("sgvs")) { @@ -437,9 +490,7 @@ public class DataService extends IntentService { JSONArray jsonArray = new JSONArray(sgvstring); for (int i = 0; i < jsonArray.length(); i++) { JSONObject sgvJson = jsonArray.getJSONObject(i); - NSSgv nsSgv = new NSSgv(sgvJson); - BgReading bgReading = new BgReading(nsSgv); - MainApp.getDbHelper().createIfNotExists(bgReading, "NS"); + storeSgv(sgvJson); } } } catch (Exception e) { @@ -452,11 +503,7 @@ public class DataService extends IntentService { if (bundles.containsKey("mbg")) { String mbgstring = bundles.getString("mbg"); JSONObject mbgJson = new JSONObject(mbgstring); - NSMbg nsMbg = new NSMbg(mbgJson); - CareportalEvent careportalEvent = new CareportalEvent(nsMbg); - MainApp.getDbHelper().createOrUpdate(careportalEvent); - if (Config.logIncommingData) - log.debug("Adding/Updating new MBG: " + careportalEvent.log()); + storeMbg(mbgJson); } if (bundles.containsKey("mbgs")) { @@ -464,11 +511,7 @@ public class DataService extends IntentService { JSONArray jsonArray = new JSONArray(sgvstring); for (int i = 0; i < jsonArray.length(); i++) { JSONObject mbgJson = jsonArray.getJSONObject(i); - NSMbg nsMbg = new NSMbg(mbgJson); - CareportalEvent careportalEvent = new CareportalEvent(nsMbg); - MainApp.getDbHelper().createOrUpdate(careportalEvent); - if (Config.logIncommingData) - log.debug("Adding/Updating new MBG: " + careportalEvent.log()); + storeMbg(mbgJson); } } } catch (Exception e) { @@ -549,6 +592,21 @@ public class DataService extends IntentService { } } + private void storeMbg(JSONObject mbgJson) { + NSMbg nsMbg = new NSMbg(mbgJson); + CareportalEvent careportalEvent = new CareportalEvent(nsMbg); + MainApp.getDbHelper().createOrUpdate(careportalEvent); + if (Config.logIncommingData) + log.debug("Adding/Updating new MBG: " + careportalEvent.log()); + } + + private void storeSgv(JSONObject sgvJson) { + NSSgv nsSgv = new NSSgv(sgvJson); + BgReading bgReading = new BgReading(nsSgv); + MainApp.getDbHelper().createIfNotExists(bgReading, "NS"); + SourceNSClientPlugin.getPlugin().detectSource(JsonHelper.safeGetString(sgvJson, "device"), JsonHelper.safeGetLong(sgvJson, "mills")); + } + private void handleNewSMS(Intent intent) { Bundle bundle = intent.getExtras(); if (bundle == null) return; diff --git a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java index 744530c8f2..25485fb34d 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java @@ -49,4 +49,6 @@ public interface Intents { String GLIMP_BG = "it.ct.glicemia.ACTION_GLUCOSE_MEASURED"; String DEXCOMG5_BG = "com.dexcom.cgm.DATA"; + + String POCTECH_BG = "com.china.poctech.data"; } diff --git a/app/src/main/java/info/nightscout/androidaps/SingleFragmentActivity.java b/app/src/main/java/info/nightscout/androidaps/SingleFragmentActivity.java new file mode 100644 index 0000000000..567799fe5e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/SingleFragmentActivity.java @@ -0,0 +1,59 @@ +package info.nightscout.androidaps; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.tabs.TabPageAdapter; +import info.nightscout.utils.PasswordProtection; + +public class SingleFragmentActivity extends AppCompatActivity { + + private PluginBase plugin; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_single_fragment); + + this.plugin = MainApp.getPluginsList().get(getIntent().getIntExtra("plugin", -1)); + setTitle(plugin.getName()); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayShowHomeEnabled(true); + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout, + Fragment.instantiate(this, plugin.pluginDescription.getFragmentClass())).commit(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + else if (item.getItemId() == R.id.nav_plugin_preferences) { + PasswordProtection.QueryPassword(this, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(this, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + startActivity(i); + }, null); + return true; + } + return false; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (plugin.getPreferencesId() != -1) + getMenuInflater().inflate(R.menu.menu_single_fragment, menu); + return super.onCreateOptionsMenu(menu); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java index 3161cb19f3..bfbbfedbd1 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ConstraintChecker.java @@ -4,12 +4,10 @@ import java.util.ArrayList; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; /** * Created by mike on 19.03.2018. @@ -25,7 +23,7 @@ public class ConstraintChecker implements ConstraintsInterface { public Constraint isLoopInvokationAllowed() { - return isLoopInvokationAllowed(new Constraint<>(true)); + return isLoopInvocationAllowed(new Constraint<>(true)); } public Constraint isClosedLoopAllowed() { @@ -69,13 +67,13 @@ public class ConstraintChecker implements ConstraintsInterface { } @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { ArrayList constraintsPlugins = mainApp.getSpecificPluginsListByInterface(ConstraintsInterface.class); for (PluginBase p : constraintsPlugins) { ConstraintsInterface constraint = (ConstraintsInterface) p; if (!p.isEnabled(PluginType.CONSTRAINTS)) continue; - constraint.isLoopInvokationAllowed(value); + constraint.isLoopInvocationAllowed(value); } return value; } diff --git a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java index c2f9d16dc3..6e65e6d6d3 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java +++ b/app/src/main/java/info/nightscout/androidaps/data/DetailedBolusInfo.java @@ -10,6 +10,7 @@ import java.util.Date; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; /** * Created by mike on 29.05.2017. @@ -17,6 +18,7 @@ import info.nightscout.androidaps.db.Source; public class DetailedBolusInfo { public long date = System.currentTimeMillis(); + public long lastKnownBolusTime; public String eventType = CareportalEvent.MEALBOLUS; public double insulin = 0; public double carbs = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 374612f2f5..7412f8ce4c 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -202,6 +202,12 @@ public class Profile { if (targetHigh_v == null) targetHigh_v = convertToSparseArray(targetHigh); validate(targetHigh_v); + + if (targetHigh_v.size() != targetLow_v.size()) isValid = false; + else for (int i = 0; i < targetHigh_v.size(); i++) + if (targetHigh_v.valueAt(i) < targetLow_v.valueAt(i)) + isValid = false; + isValidated = true; } @@ -226,6 +232,10 @@ public class Profile { basal_v.setValueAt(i, description.basalMinimumRate); if (notify) sendBelowMinimumNotification(from); + } else if (basal_v.valueAt(i) > description.basalMaximumRate) { + basal_v.setValueAt(i, description.basalMaximumRate); + if (notify) + sendAboveMaximumNotification(from); } } } else { @@ -235,12 +245,16 @@ public class Profile { isValidated = false; } - } + } return isValid; } protected void sendBelowMinimumNotification(String from) { - MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + MainApp.bus().post(new EventNewNotification(new Notification(Notification.MINIMAL_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.minimalbasalvaluereplaced), from), Notification.NORMAL))); + } + + protected void sendAboveMaximumNotification(String from) { + MainApp.bus().post(new EventNewNotification(new Notification(Notification.MAXIMUM_BASAL_VALUE_REPLACED, String.format(MainApp.gs(R.string.maximumbasalvaluereplaced), from), Notification.NORMAL))); } private void validate(LongSparseArray array) { @@ -444,12 +458,12 @@ public class Profile { return ret; } - public double getTarget(){ - return getTarget(secondsFromMidnight(System.currentTimeMillis())); + public double getTarget() { + return getTarget(secondsFromMidnight(System.currentTimeMillis())); } protected double getTarget(int timeAsSeconds) { - return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds))/2; + return (getTargetLowTimeFromMidnight(timeAsSeconds) + getTargetHighTimeFromMidnight(timeAsSeconds)) / 2; } public double getTargetLow() { @@ -544,6 +558,12 @@ public class Profile { else return DecimalFormatter.to1Decimal(valueInMmol); } + 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); + } + // 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); diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index d5a1c90100..6cda2ab4ce 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -24,15 +24,17 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSMbg; import info.nightscout.androidaps.plugins.Overview.OverviewFragment; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.T; import info.nightscout.utils.Translator; @DatabaseTable(tableName = DatabaseHelper.DATABASE_CAREPORTALEVENTS) -public class CareportalEvent implements DataPointWithLabelInterface { +public class CareportalEvent implements DataPointWithLabelInterface, Interval { private static Logger log = LoggerFactory.getLogger(CareportalEvent.class); @DatabaseField(id = true) @@ -87,7 +89,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { } public long getHoursFromStart() { - return (System.currentTimeMillis() - date) / (60 * 1000); + return (System.currentTimeMillis() - date) / (60 * 60 * 1000); } public String age() { @@ -98,13 +100,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { return diff.get(TimeUnit.DAYS) + " " + MainApp.gs(R.string.days) + " " + diff.get(TimeUnit.HOURS) + " " + MainApp.gs(R.string.hours); } - public boolean isOlderThan(double hours) { - Map diff = computeDiff(date, System.currentTimeMillis()); - if(diff.get(TimeUnit.DAYS)*24 + diff.get(TimeUnit.HOURS) > hours) - return true; - else - return false; - } + public boolean isOlderThan(double hours) { return getHoursFromStart() > hours; } public String log() { return "CareportalEvent{" + @@ -133,6 +129,19 @@ public class CareportalEvent implements DataPointWithLabelInterface { return result; } + + public static boolean isEvent5minBack(List list, long time) { + for (int i = 0; i < list.size(); i++) { + CareportalEvent event = list.get(i); + if (event.date <= time && event.date > (time - T.mins(5).msecs())) { + //log.debug("Found event for time: " + DateUtil.dateAndTimeString(time) + " " + event.toString()); + return true; + } + } + //log.debug("WWWWWW No found event for time: " + DateUtil.dateAndTimeString(time)); + return false; + } + // -------- DataPointWithLabelInterface ------- @Override @@ -213,14 +222,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { @Override public long getDuration() { - try { - JSONObject object = new JSONObject(json); - if (object.has("duration")) - return object.getInt("duration") * 60 * 1000L; - } catch (JSONException e) { - log.error("Unhandled exception", e); - } - return 0; + return end() - start(); } @Override @@ -259,8 +261,79 @@ public class CareportalEvent implements DataPointWithLabelInterface { if (eventType.equals(EXERCISE)) return Color.BLUE; if (eventType.equals(OPENAPSOFFLINE)) - return Color.GRAY; + return Color.GRAY & 0x80FFFFFF; return Color.GRAY; } + // Interval interface + Long cuttedEnd = null; + + @Override + public long durationInMsec() { + try { + JSONObject object = new JSONObject(json); + if (object.has("duration")) + return object.getInt("duration") * 60 * 1000L; + } catch (JSONException e) { + log.error("Unhandled exception", e); + } + return 0; + } + + @Override + public long start() { + return date; + } + + @Override + public long originalEnd() { + return date + durationInMsec(); + } + + @Override + public long end() { + if (cuttedEnd != null) + return cuttedEnd; + return originalEnd(); + } + + @Override + public void cutEndTo(long end) { + cuttedEnd = end; + } + + @Override + public boolean match(long time) { + if (start() <= time && end() >= time) + return true; + return false; + } + + public boolean before(long time) { + if (end() < time) + return true; + return false; + } + + public boolean after(long time) { + if (start() > time) + return true; + return false; + } + + @Override + public boolean isInProgress() { + return match(System.currentTimeMillis()); + } + + @Override + public boolean isEndingEvent() { + return durationInMsec() == 0; + } + + @Override + public boolean isValid() { + return eventType.equals(OPENAPSOFFLINE); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 7174c19ec4..f869ca36c5 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -30,6 +30,7 @@ import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.OverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventCareportalEventChange; @@ -675,7 +676,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { .source(Source.NIGHTSCOUT); createOrUpdate(tempTarget); } catch (JSONException e) { - log.error("Unhandled exception", e); + log.error("Unhandled exception: " + trJson.toString(), e); } } @@ -761,7 +762,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } } } catch (SQLException | JSONException e) { - log.error("Unhandled exception", e); + log.error("Unhandled exception: " + trJson.toString(), e); } } @@ -966,7 +967,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { createOrUpdate(tempBasal); } } catch (JSONException e) { - log.error("Unhandled exception", e); + log.error("Unhandled exception: " + trJson.toString(), e); } } @@ -1242,6 +1243,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { where.ge("date", mills); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1249,13 +1251,41 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return new ArrayList<>(); } - public List getCareportalEventsFromTime(boolean ascending) { + public void preprocessOpenAPSOfflineEvents(List list) { + OverlappingIntervals offlineEvents = new OverlappingIntervals(); + for (int i = 0; i < list.size(); i++) { + CareportalEvent event = list.get(i); + if (!event.eventType.equals(CareportalEvent.OPENAPSOFFLINE)) continue; + offlineEvents.add(event); + } + + } + + public List getCareportalEventsFromTime(long mills, String type, boolean ascending) { + try { + List careportalEvents; + QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); + queryBuilder.orderBy("date", ascending); + Where where = queryBuilder.where(); + where.ge("date", mills).and().eq("eventType", type); + PreparedQuery preparedQuery = queryBuilder.prepare(); + careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); + return careportalEvents; + } catch (SQLException e) { + log.error("Unhandled exception", e); + } + return new ArrayList<>(); + } + + public List getCareportalEvents(boolean ascending) { try { List careportalEvents; QueryBuilder queryBuilder = getDaoCareportalEvents().queryBuilder(); queryBuilder.orderBy("date", ascending); PreparedQuery preparedQuery = queryBuilder.prepare(); careportalEvents = getDaoCareportalEvents().query(preparedQuery); + preprocessOpenAPSOfflineEvents(careportalEvents); return careportalEvents; } catch (SQLException e) { log.error("Unhandled exception", e); @@ -1315,7 +1345,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { careportalEvent._id = trJson.getString("_id"); createOrUpdate(careportalEvent); } catch (SQLException | JSONException e) { - log.error("Unhandled exception", e); + log.error("Unhandled exception: " + trJson.toString(), e); } } @@ -1470,14 +1500,19 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { profileSwitch.profileJson = trJson.getString("profileJson"); else { ProfileStore store = MainApp.getConfigBuilder().getActiveProfileInterface().getProfile(); - Profile profile = store.getSpecificProfile(profileSwitch.profileName); - if (profile != null) { - profileSwitch.profileJson = profile.getData().toString(); - log.debug("Profile switch prefilled with JSON from local store"); - // Update data in NS - NSUpload.updateProfileSwitch(profileSwitch); + if (store != null) { + Profile profile = store.getSpecificProfile(profileSwitch.profileName); + if (profile != null) { + profileSwitch.profileJson = profile.getData().toString(); + log.debug("Profile switch prefilled with JSON from local store"); + // Update data in NS + NSUpload.updateProfileSwitch(profileSwitch); + } else { + log.debug("JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); + return; + } } else { - log.debug("JSON for profile switch doesn't exist. Ignoring: " + trJson.toString()); + log.debug("Store for profile switch doesn't exist. Ignoring: " + trJson.toString()); return; } } @@ -1485,7 +1520,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { profileSwitch.profilePlugin = trJson.getString("profilePlugin"); createOrUpdate(profileSwitch); } catch (JSONException e) { - log.error("Unhandled exception", e); + log.error("Unhandled exception: " + trJson.toString(), e); } } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index 1123cb58df..ea97f4ddca 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -58,7 +58,7 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { public int durationInMinutes = 0; // duration == 0 means end of extended bolus @DatabaseField - public int insulinInterfaceID = InsulinInterface.FASTACTINGINSULIN; + public int insulinInterfaceID = InsulinInterface.OREF_RAPID_ACTING; @DatabaseField public double dia = Constants.defaultDIA; diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java index 03df71f31b..57050bdcc9 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.events; + +import info.nightscout.utils.StringUtils; + public class EventNetworkChange extends Event { public boolean mobileConnected = false; @@ -9,6 +12,6 @@ public class EventNetworkChange extends Event { public boolean roaming = false; public String getSsid() { - return ssid.replace("SSID: ","").replaceAll("\"",""); + return StringUtils.removeSurroundingQuotes(ssid); } } diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java b/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java new file mode 100644 index 0000000000..0b2d933c12 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventProfileStoreChanged.java @@ -0,0 +1,4 @@ +package info.nightscout.androidaps.events; + +public class EventProfileStoreChanged extends Event { +} diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java index a1daa08a56..820170dc0f 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ConstraintsInterface.java @@ -7,7 +7,7 @@ import info.nightscout.androidaps.data.Profile; */ public interface ConstraintsInterface { - default Constraint isLoopInvokationAllowed(Constraint value) { + default Constraint isLoopInvocationAllowed(Constraint value) { return value; } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java index 1c5ee83834..374d96890a 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java @@ -9,4 +9,5 @@ import info.nightscout.androidaps.data.PumpEnactResult; public interface DanaRInterface { PumpEnactResult loadHistory(byte type); // for history browser PumpEnactResult loadEvents(); // events history to build treatments from + PumpEnactResult setUserOptions(); // like AnyDana does } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java index a123554ae8..34b14d9b5d 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/InsulinInterface.java @@ -8,8 +8,8 @@ import info.nightscout.androidaps.plugins.Treatments.Treatment; */ public interface InsulinInterface { - int FASTACTINGINSULIN = 0; - int FASTACTINGINSULINPROLONGED = 1; + // int FASTACTINGINSULIN = 0; // old model no longer available + // int FASTACTINGINSULINPROLONGED = 1; // old model no longer available int OREF_RAPID_ACTING = 2; int OREF_ULTRA_RAPID_ACTING = 3; int OREF_FREE_PEAK = 4; diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java index 1173936430..9f3e4fe5c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginBase.java @@ -1,11 +1,13 @@ package info.nightscout.androidaps.interfaces; import android.os.SystemClock; +import android.support.v4.app.FragmentActivity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; /** @@ -32,6 +34,12 @@ public abstract class PluginBase { this.pluginDescription = pluginDescription; } + // Default always calls invoke + // Plugins that have special constraints if they get switched to may override this method + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) { + pluginSwitcher.invoke(); + } + // public PluginType getType() { // return mainType; // } @@ -57,6 +65,11 @@ public abstract class PluginBase { return getName(); } + public String getDescription() { + if (pluginDescription.description == -1) return null; + else return MainApp.gs(pluginDescription.description); + } + public PluginType getType() { return pluginDescription.mainType; } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java index c86ae639fa..1634fc672d 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PluginDescription.java @@ -9,6 +9,7 @@ public class PluginDescription { boolean showInList = true; int pluginName = -1; int shortName = -1; + int description = -1; int preferencesId = -1; int advancedPreferencesId = -1; public boolean enableByDefault = false; @@ -74,6 +75,11 @@ public class PluginDescription { return this; } + public PluginDescription description(int description) { + this.description = description; + return this; + } + public String getFragmentClass() { return fragmentClass; } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java index 4bd212f562..ac2e5076a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/PumpDescription.java @@ -35,6 +35,7 @@ public class PumpDescription { public boolean isSetBasalProfileCapable = true; public double basalStep = 0.01d; public double basalMinimumRate = 0.04d; + public double basalMaximumRate = 25d; public boolean isRefillingCapable = false; diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java index deb649c21f..81af28eaa4 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/SensitivityInterface.java @@ -7,5 +7,10 @@ import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; */ public interface SensitivityInterface { + + double MIN_HOURS = 1; + double MIN_HOURS_FULL_AUTOSENS = 4; + AutosensResult detectSensitivity(long fromTime, long toTime); + } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 27a6a0846b..dc437e04ab 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -50,7 +50,7 @@ public interface TreatmentsInterface { boolean addToHistoryExtendedBolus(ExtendedBolus extendedBolus); - boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo); + boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate); TempTarget getTempTargetFromHistory(); TempTarget getTempTargetFromHistory(long time); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsFragment.java index 84086d6117..d45fb10b4e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsFragment.java @@ -87,6 +87,7 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL tempBasalCancel.setOnClickListener(this); fill.setOnClickListener(this); history.setOnClickListener(this); + history.setVisibility(MainApp.devBranch ? View.VISIBLE : View.GONE); tddStats.setOnClickListener(this); updateGUI(); @@ -125,7 +126,7 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL activity.runOnUiThread(new Runnable() { @Override public void run() { - if (MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() != null) { + if (MainApp.getConfigBuilder().getActiveProfileInterface() != null && MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() != null) { profileSwitch.setVisibility(View.VISIBLE); } else { profileSwitch.setVisibility(View.GONE); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsPlugin.java index 2397410693..ff6275c9c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/ActionsPlugin.java @@ -17,6 +17,7 @@ public class ActionsPlugin extends PluginBase { .fragmentClass(ActionsFragment.class.getName()) .pluginName(R.string.actions) .shortName(R.string.actions_shortname) + .description(R.string.description_actions) ); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java index dda2e61006..558a7c381c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Actions/dialogs/FillDialog.java @@ -61,6 +61,10 @@ public class FillDialog extends DialogFragment implements OnClickListener { private EditText notesEdit; + //one shot guards + private boolean accepted; + private boolean okClicked; + final private TextWatcher textWatcher = new TextWatcher() { @Override public void afterTextChanged(Editable s) { @@ -163,7 +167,14 @@ public class FillDialog extends DialogFragment implements OnClickListener { } - private void confirmAndDeliver() { + private synchronized void confirmAndDeliver() { + if (okClicked) { + log.debug("guarding: ok already clicked"); + dismiss(); + return; + } + okClicked = true; + try { Double insulin = SafeParse.stringToDouble(editInsulin.getText()); @@ -179,10 +190,10 @@ public class FillDialog extends DialogFragment implements OnClickListener { } if (pumpSiteChangeCheckbox.isChecked()) - confirmMessage.add("" + "" + MainApp.gs(R.string.record_pump_site_change) + ""); + confirmMessage.add("" + "" + MainApp.gs(R.string.record_pump_site_change) + ""); if (insulinCartridgeChangeCheckbox.isChecked()) - confirmMessage.add("" + "" + MainApp.gs(R.string.record_insulin_cartridge_change) + ""); + confirmMessage.add("" + "" + MainApp.gs(R.string.record_insulin_cartridge_change) + ""); final String notes = notesEdit.getText().toString(); if (!notes.isEmpty()) { @@ -198,32 +209,40 @@ public class FillDialog extends DialogFragment implements OnClickListener { if (insulinAfterConstraints > 0 || pumpSiteChangeCheckbox.isChecked() || insulinCartridgeChangeCheckbox.isChecked()) { builder.setMessage(Html.fromHtml(Joiner.on("
").join(confirmMessage))); builder.setPositiveButton(MainApp.gs(R.string.primefill), (dialog, id) -> { - if (finalInsulinAfterConstraints > 0) { - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.insulin = finalInsulinAfterConstraints; - detailedBolusInfo.context = context; - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.isValid = false; // do not count it in IOB (for pump history) - detailedBolusInfo.notes = notes; - ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - MainApp.instance().startActivity(i); + synchronized (builder) { + if (accepted) { + log.debug("guarding: already accepted"); + return; + } + accepted = true; + + if (finalInsulinAfterConstraints > 0) { + DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.insulin = finalInsulinAfterConstraints; + detailedBolusInfo.context = context; + detailedBolusInfo.source = Source.USER; + detailedBolusInfo.isValid = false; // do not count it in IOB (for pump history) + detailedBolusInfo.notes = notes; + ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { + @Override + public void run() { + if (!result.success) { + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.boluserror); + i.putExtra("status", result.comment); + i.putExtra("title", MainApp.gs(R.string.treatmentdeliveryerror)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + } } - } - }); - FabricPrivacy.getInstance().logCustom(new CustomEvent("Fill")); + }); + FabricPrivacy.getInstance().logCustom(new CustomEvent("Fill")); + } + if (pumpSiteChangeCheckbox.isChecked()) + NSUpload.uploadEvent(CareportalEvent.SITECHANGE, now(), notes); + if (insulinCartridgeChangeCheckbox.isChecked()) + NSUpload.uploadEvent(CareportalEvent.INSULINCHANGE, now() + 1000, notes); } - if (pumpSiteChangeCheckbox.isChecked()) - NSUpload.uploadEvent(CareportalEvent.SITECHANGE, now(), notes); - if (insulinCartridgeChangeCheckbox.isChecked()) - NSUpload.uploadEvent(CareportalEvent.INSULINCHANGE, now() + 1000, notes); }); } else { builder.setMessage(MainApp.gs(R.string.no_action_selected)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalFragment.java index 4a4210f29e..264488f684 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalFragment.java @@ -218,50 +218,19 @@ public class CareportalFragment extends SubscriberFragment implements View.OnCli double iageUrgent = nsSettings.getExtendedWarnValue("iage", "urgent", 96); double iageWarn = nsSettings.getExtendedWarnValue("iage", "warn", 72); + handleAge(iage, CareportalEvent.INSULINCHANGE, iageWarn, iageUrgent); + double cageUrgent = nsSettings.getExtendedWarnValue("cage", "urgent", 72); double cageWarn = nsSettings.getExtendedWarnValue("cage", "warn", 48); + handleAge(cage, CareportalEvent.SITECHANGE, cageWarn, cageUrgent); + double sageUrgent = nsSettings.getExtendedWarnValue("sage", "urgent", 166); double sageWarn = nsSettings.getExtendedWarnValue("sage", "warn", 164); + handleAge(sage, CareportalEvent.SENSORCHANGE, sageWarn, sageUrgent); + double pbageUrgent = nsSettings.getExtendedWarnValue("pgage", "urgent", 360); double pbageWarn = nsSettings.getExtendedWarnValue("pgage", "warn", 240); - - String notavailable = OverviewFragment.shorttextmode ? "-" : MainApp.gs(R.string.notavailable); - if (sage != null) { - careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(CareportalEvent.SENSORCHANGE); - if (careportalEvent != null) { - sage.setTextColor(CareportalFragment.determineTextColor(careportalEvent, sageWarn, sageUrgent)); - sage.setText(careportalEvent.age()); - } else { - sage.setText(notavailable); - } - } - if (iage != null) { - careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(CareportalEvent.INSULINCHANGE); - if (careportalEvent != null) { - iage.setTextColor(CareportalFragment.determineTextColor(careportalEvent, iageWarn, iageUrgent)); - iage.setText(careportalEvent.age()); - } else { - iage.setText(notavailable); - } - } - if (cage != null) { - careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(CareportalEvent.SITECHANGE); - if (careportalEvent != null) { - cage.setTextColor(CareportalFragment.determineTextColor(careportalEvent, cageWarn, cageUrgent)); - cage.setText(careportalEvent.age()); - } else { - cage.setText(notavailable); - } - } - if (pbage != null) { - careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(CareportalEvent.PUMPBATTERYCHANGE); - if (careportalEvent != null) { - pbage.setTextColor(CareportalFragment.determineTextColor(careportalEvent, pbageWarn, pbageUrgent)); - pbage.setText(careportalEvent.age()); - } else { - pbage.setText(notavailable); - } - } + handleAge(pbage, CareportalEvent.PUMPBATTERYCHANGE, pbageWarn, pbageUrgent); } ); } @@ -277,5 +246,21 @@ public class CareportalFragment extends SubscriberFragment implements View.OnCli } } + + private static TextView handleAge(final TextView age, String eventType, double warnThreshold, double urgentThreshold) { + String notavailable = OverviewFragment.shorttextmode ? "-" : MainApp.gs(R.string.notavailable); + + if (age != null) { + CareportalEvent careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(eventType); + if (careportalEvent != null) { + age.setTextColor(CareportalFragment.determineTextColor(careportalEvent, warnThreshold, urgentThreshold)); + age.setText(careportalEvent.age()); + } else { + age.setText(notavailable); + } + } + + return age; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalPlugin.java index 1428880a81..22aa4686b2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/CareportalPlugin.java @@ -23,6 +23,9 @@ public class CareportalPlugin extends PluginBase { .fragmentClass(CareportalFragment.class.getName()) .pluginName(R.string.careportal) .shortName(R.string.careportal_shortname) + .visibleByDefault(true) + .enableByDefault(true) + .description(R.string.description_careportal) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Common/SubscriberFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Common/SubscriberFragment.java index 5ca9232646..fabd83f0ea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Common/SubscriberFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Common/SubscriberFragment.java @@ -21,7 +21,7 @@ abstract public class SubscriberFragment extends Fragment { updateGUI(); } - @Override public void onDestroyView() { + @Override public synchronized void onDestroyView() { super.onDestroyView(); if (unbinder != null) unbinder.unbind(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java index 70b5eb466b..3e60ca2b77 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderFragment.java @@ -1,30 +1,30 @@ package info.nightscout.androidaps.plugins.ConfigBuilder; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.StringRes; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; -import android.widget.ImageView; +import android.widget.ImageButton; import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ListView; +import android.widget.RadioButton; +import android.widget.ScrollView; import android.widget.TextView; import com.crashlytics.android.answers.CustomEvent; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import info.nightscout.androidaps.Config; +import butterknife.Unbinder; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.PreferencesActivity; import info.nightscout.androidaps.R; @@ -43,76 +43,36 @@ import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.Insulin.InsulinOrefRapidActingPlugin; import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.PasswordProtection; public class ConfigBuilderFragment extends SubscriberFragment { - @BindView(R.id.configbuilder_insulinlistview) - ListView insulinListView; - @BindView(R.id.configbuilder_sensitivitylistview) - ListView sensitivityListView; - @BindView(R.id.configbuilder_bgsourcelistview) - ListView bgsourceListView; - @BindView(R.id.configbuilder_bgsourcelabel) - TextView bgsourceLabel; - @BindView(R.id.configbuilder_pumplistview) - ListView pumpListView; - @BindView(R.id.configbuilder_pumplabel) - TextView pumpLabel; - @BindView(R.id.configbuilder_looplistview) - ListView loopListView; - @BindView(R.id.configbuilder_looplabel) - TextView loopLabel; - @BindView(R.id.configbuilder_treatmentslistview) - ListView treatmentsListView; - @BindView(R.id.configbuilder_treatmentslabel) - TextView treatmentsLabel; - @BindView(R.id.configbuilder_profilelistview) - ListView profileListView; - @BindView(R.id.configbuilder_profilelabel) - TextView profileLabel; - @BindView(R.id.configbuilder_apslistview) - ListView apsListView; - @BindView(R.id.configbuilder_apslabel) - TextView apsLabel; - @BindView(R.id.configbuilder_constraintslistview) - ListView constraintsListView; - @BindView(R.id.configbuilder_constraintslabel) - TextView constraintsLabel; - @BindView(R.id.configbuilder_generallistview) - ListView generalListView; + private List pluginViewHolders = new ArrayList<>(); - @BindView(R.id.configbuilder_mainlayout) - LinearLayout mainLayout; - @BindView(R.id.configbuilder_unlock) + @BindView(R.id.categories) + LinearLayout categories; + + @BindView(R.id.main_layout) + ScrollView mainLayout; + @BindView(R.id.unlock) Button unlock; - PluginCustomAdapter insulinDataAdapter = null; - PluginCustomAdapter sensivityDataAdapter = null; - PluginCustomAdapter bgsourceDataAdapter = null; - PluginCustomAdapter pumpDataAdapter = null; - PluginCustomAdapter loopDataAdapter = null; - PluginCustomAdapter treatmentDataAdapter = null; - PluginCustomAdapter profileDataAdapter = null; - PluginCustomAdapter apsDataAdapter = null; - PluginCustomAdapter constraintsDataAdapter = null; - PluginCustomAdapter generalDataAdapter = null; - @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { try { View view = inflater.inflate(R.layout.configbuilder_fragment, container, false); - unbinder = ButterKnife.bind(this, view); if (PasswordProtection.isLocked("settings_password")) mainLayout.setVisibility(View.GONE); - else - unlock.setVisibility(View.GONE); + else unlock.setVisibility(View.GONE); + + createViews(); + return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -121,225 +81,58 @@ public class ConfigBuilderFragment extends SubscriberFragment { return null; } - @OnClick(R.id.configbuilder_unlock) - public void onClickUnlock() { + @OnClick(R.id.unlock) + void onClickUnlock() { PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { mainLayout.setVisibility(View.VISIBLE); unlock.setVisibility(View.GONE); }, null); } + @Override + public void onDestroyView() { + super.onDestroyView(); + for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.unbind(); + pluginViewHolders.clear(); + } @Override protected void updateGUI() { - - insulinDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface.class, PluginType.INSULIN), PluginType.INSULIN); - insulinListView.setAdapter(insulinDataAdapter); - setListViewHeightBasedOnChildren(insulinListView); - bgsourceDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface.class, PluginType.BGSOURCE), PluginType.BGSOURCE); - bgsourceListView.setAdapter(bgsourceDataAdapter); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.BGSOURCE).size() == 0) - bgsourceLabel.setVisibility(View.GONE); - setListViewHeightBasedOnChildren(bgsourceListView); - pumpDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP), PluginType.PUMP); - pumpListView.setAdapter(pumpDataAdapter); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP).size() == 0 || Config.NSCLIENT || Config.G5UPLOADER) { - pumpLabel.setVisibility(View.GONE); - pumpListView.setVisibility(View.GONE); - } - setListViewHeightBasedOnChildren(pumpListView); - loopDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP), PluginType.LOOP); - loopListView.setAdapter(loopDataAdapter); - setListViewHeightBasedOnChildren(loopListView); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP).size() == 0) - loopLabel.setVisibility(View.GONE); - treatmentDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT), PluginType.TREATMENT); - treatmentsListView.setAdapter(treatmentDataAdapter); - setListViewHeightBasedOnChildren(treatmentsListView); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT).size() == 0) - treatmentsLabel.setVisibility(View.GONE); - profileDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface.class, PluginType.PROFILE), PluginType.PROFILE); - profileListView.setAdapter(profileDataAdapter); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.PROFILE).size() == 0) - profileLabel.setVisibility(View.GONE); - setListViewHeightBasedOnChildren(profileListView); - apsDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInList(PluginType.APS), PluginType.APS); - apsListView.setAdapter(apsDataAdapter); - setListViewHeightBasedOnChildren(apsListView); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.APS).size() == 0) - apsLabel.setVisibility(View.GONE); - sensivityDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginType.SENSITIVITY), PluginType.SENSITIVITY); - sensitivityListView.setAdapter(sensivityDataAdapter); - setListViewHeightBasedOnChildren(sensitivityListView); - constraintsDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginType.CONSTRAINTS), PluginType.CONSTRAINTS); - constraintsListView.setAdapter(constraintsDataAdapter); - setListViewHeightBasedOnChildren(constraintsListView); - if (MainApp.getSpecificPluginsVisibleInList(PluginType.CONSTRAINTS).size() == 0) - constraintsLabel.setVisibility(View.GONE); - generalDataAdapter = new PluginCustomAdapter(getContext(), R.layout.configbuilder_simpleitem, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL), PluginType.GENERAL); - generalListView.setAdapter(generalDataAdapter); - setListViewHeightBasedOnChildren(generalListView); + for (PluginViewHolder pluginViewHolder : pluginViewHolders) pluginViewHolder.update(); } - /* - * ConfigBuilderFragment code - */ - - private class PluginCustomAdapter extends ArrayAdapter { - - private ArrayList pluginList; - final private PluginType type; - - PluginCustomAdapter(Context context, int textViewResourceId, - ArrayList pluginList, PluginType type) { - super(context, textViewResourceId, pluginList); - this.pluginList = new ArrayList<>(); - this.pluginList.addAll(pluginList); - this.type = type; - } - - private class PluginViewHolder { - TextView name; - CheckBox checkboxEnabled; - CheckBox checkboxVisible; - ImageView settings; - } - - @NonNull - @Override - public View getView(int position, View view, @NonNull ViewGroup parent) { - - PluginViewHolder holder; - PluginBase plugin = pluginList.get(position); - - if (view == null) { - view = LayoutInflater.from(parent.getContext()).inflate(R.layout.configbuilder_simpleitem, null); - - holder = new PluginViewHolder(); - holder.name = (TextView) view.findViewById(R.id.configbuilder_simpleitem_name); - holder.checkboxEnabled = (CheckBox) view.findViewById(R.id.configbuilder_simpleitem_checkboxenabled); - holder.checkboxVisible = (CheckBox) view.findViewById(R.id.configbuilder_simpleitem_checkboxvisible); - holder.settings = (ImageView) view.findViewById(R.id.configbuilder_simpleitem_settings); - - if (plugin.isEnabled(type) && plugin.getPreferencesId() != -1) - holder.settings.setVisibility(View.VISIBLE); - else - holder.settings.setVisibility(View.INVISIBLE); - - view.setTag(holder); - - holder.checkboxEnabled.setOnClickListener(v -> { - CheckBox cb = (CheckBox) v; - PluginBase plugin1 = (PluginBase) cb.getTag(); - plugin1.setPluginEnabled(type, cb.isChecked()); - plugin1.setFragmentVisible(type, cb.isChecked()); - onEnabledCategoryChanged(plugin1, type); - ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); - MainApp.bus().post(new EventRefreshGui()); - MainApp.bus().post(new EventConfigBuilderChange()); - ConfigBuilderPlugin.getPlugin().logPluginStatus(); - FabricPrivacy.getInstance().logCustom(new CustomEvent("ConfigurationChange")); - }); - - holder.checkboxVisible.setOnClickListener(v -> { - CheckBox cb = (CheckBox) v; - PluginBase plugin12 = (PluginBase) cb.getTag(); - plugin12.setFragmentVisible(type, cb.isChecked()); - ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxVisible"); - MainApp.bus().post(new EventRefreshGui()); - ConfigBuilderPlugin.getPlugin().logPluginStatus(); - }); - - holder.settings.setOnClickListener(v -> { - final PluginBase plugin13 = (PluginBase) v.getTag(); - PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(getContext(), PreferencesActivity.class); - i.putExtra("id", plugin13.getPreferencesId()); - startActivity(i); - }, null); - }); - - holder.name.setOnLongClickListener(v -> { - final PluginBase plugin14 = (PluginBase) v.getTag(); - PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { - Intent i = new Intent(getContext(), PreferencesActivity.class); - i.putExtra("id", plugin14.getPreferencesId()); - startActivity(i); - }, null); - return false; - }); - - } else { - holder = (PluginViewHolder) view.getTag(); - } - - holder.name.setText(plugin.getName()); - holder.checkboxEnabled.setChecked(plugin.isEnabled(type)); - holder.checkboxVisible.setChecked(plugin.isFragmentVisible()); - holder.name.setTag(plugin); - holder.checkboxEnabled.setTag(plugin); - holder.checkboxVisible.setTag(plugin); - holder.settings.setTag(plugin); - - if (plugin.pluginDescription.alwaysEnabled) { - holder.checkboxEnabled.setEnabled(false); - } - - if (plugin.pluginDescription.alwayVisible) { - holder.checkboxEnabled.setEnabled(false); - } - - if (!plugin.isEnabled(type)) { - holder.checkboxVisible.setEnabled(false); - } - - if (!plugin.hasFragment()) { - holder.checkboxVisible.setVisibility(View.INVISIBLE); - } - - // Hide enabled control and force enabled plugin if there is only one plugin available - if (type == PluginType.INSULIN || type == PluginType.PUMP || type == PluginType.SENSITIVITY) - if (pluginList.size() < 2) { - holder.checkboxEnabled.setEnabled(false); - plugin.setPluginEnabled(type, true); - ConfigBuilderPlugin.getPlugin().storeSettings("ForceEnable"); - } - - // Constraints cannot be disabled - if (type == PluginType.CONSTRAINTS) - holder.checkboxEnabled.setEnabled(false); - - // Hide disabled profiles by default - if (type == PluginType.PROFILE) { - if (!plugin.isEnabled(type)) { - holder.checkboxVisible.setEnabled(false); - holder.checkboxVisible.setChecked(false); - } else { - holder.checkboxVisible.setEnabled(true); - } - } - - // Disable profile control for pump profiles if pump is not enabled - if (type == PluginType.PROFILE) { - if (PumpInterface.class.isAssignableFrom(plugin.getClass())) { - if (!plugin.isEnabled(PluginType.PUMP)) { - holder.checkboxEnabled.setEnabled(false); - holder.checkboxEnabled.setChecked(false); - } - } - } - - if (plugin.isEnabled(type)) { - view.setBackgroundColor(MainApp.gc(R.color.configBuilderSelectedBackground)); - } - - return view; - - } - + private void createViews() { + createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, MainApp.getSpecificPluginsVisibleInListByInterface(ProfileInterface.class, PluginType.PROFILE)); + createViewsForPlugins(R.string.configbuilder_insulin, R.string.configbuilder_insulin_description, PluginType.INSULIN, MainApp.getSpecificPluginsVisibleInListByInterface(InsulinInterface.class, PluginType.INSULIN)); + createViewsForPlugins(R.string.configbuilder_bgsource, R.string.configbuilder_bgsource_description, PluginType.BGSOURCE, MainApp.getSpecificPluginsVisibleInListByInterface(BgSourceInterface.class, PluginType.BGSOURCE)); + createViewsForPlugins(R.string.configbuilder_pump, R.string.configbuilder_pump_description, PluginType.PUMP, MainApp.getSpecificPluginsVisibleInList(PluginType.PUMP)); + createViewsForPlugins(R.string.configbuilder_sensitivity, R.string.configbuilder_sensitivity_description, PluginType.SENSITIVITY, MainApp.getSpecificPluginsVisibleInListByInterface(SensitivityInterface.class, PluginType.SENSITIVITY)); + createViewsForPlugins(R.string.configbuilder_aps, R.string.configbuilder_aps_description, PluginType.APS, MainApp.getSpecificPluginsVisibleInList(PluginType.APS)); + createViewsForPlugins(R.string.configbuilder_loop, R.string.configbuilder_loop_description, PluginType.LOOP, MainApp.getSpecificPluginsVisibleInList(PluginType.LOOP)); + createViewsForPlugins(R.string.constraints, R.string.configbuilder_constraints_description, PluginType.CONSTRAINTS, MainApp.getSpecificPluginsVisibleInListByInterface(ConstraintsInterface.class, PluginType.CONSTRAINTS)); + createViewsForPlugins(R.string.configbuilder_treatments, R.string.configbuilder_treatments_description, PluginType.TREATMENT, MainApp.getSpecificPluginsVisibleInList(PluginType.TREATMENT)); + createViewsForPlugins(R.string.configbuilder_general, R.string.configbuilder_general_description, PluginType.GENERAL, MainApp.getSpecificPluginsVisibleInList(PluginType.GENERAL)); } - void onEnabledCategoryChanged(PluginBase changedPlugin, PluginType type) { + private void createViewsForPlugins(@StringRes int title, @StringRes int description, PluginType pluginType, List plugins) { + if (plugins.size() == 0) return; + LinearLayout parent = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_category, null); + ((TextView) parent.findViewById(R.id.category_title)).setText(MainApp.gs(title)); + ((TextView) parent.findViewById(R.id.category_description)).setText(MainApp.gs(description)); + LinearLayout pluginContainer = parent.findViewById(R.id.category_plugins); + for (PluginBase plugin: plugins) { + PluginViewHolder pluginViewHolder = new PluginViewHolder(pluginType, plugin); + pluginContainer.addView(pluginViewHolder.getBaseView()); + pluginViewHolders.add(pluginViewHolder); + } + categories.addView(parent); + } + + private boolean areMultipleSelectionsAllowed(PluginType type) { + return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS ||type == PluginType.LOOP; + } + + public static void processOnEnabledCategoryChanged(PluginBase changedPlugin, PluginType type) { ArrayList pluginsInCategory = null; switch (type) { // Multiple selection allowed @@ -391,34 +184,103 @@ public class ConfigBuilderFragment extends SubscriberFragment { else pluginsInCategory.get(0).setPluginEnabled(type, true); } - updateGUI(); } } - /**** - * Method for Setting the Height of the ListView dynamically. - * *** Hack to fix the issue of not showing all the items of the ListView - * *** when placed inside a ScrollView - ****/ - public static void setListViewHeightBasedOnChildren(ListView listView) { - ListAdapter listAdapter = listView.getAdapter(); - if (listAdapter == null) - return; + public class PluginViewHolder { - int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.UNSPECIFIED); - int totalHeight = 0; - View view = null; - for (int i = 0; i < listAdapter.getCount(); i++) { - view = listAdapter.getView(i, view, listView); - if (i == 0) - view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT)); + private Unbinder unbinder; + private PluginType pluginType; + private PluginBase plugin; - view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED); - totalHeight += view.getMeasuredHeight(); + LinearLayout baseView; + @BindView(R.id.plugin_enabled_exclusive) + RadioButton enabledExclusive; + @BindView(R.id.plugin_enabled_inclusive) + CheckBox enabledInclusive; + @BindView(R.id.plugin_name) + TextView pluginName; + @BindView(R.id.plugin_description) + TextView pluginDescription; + @BindView(R.id.plugin_preferences) + ImageButton pluginPreferences; + @BindView(R.id.plugin_visibility) + CheckBox pluginVisibility; + + public PluginViewHolder(PluginType pluginType, PluginBase plugin) { + this.pluginType = pluginType; + this.plugin = plugin; + baseView = (LinearLayout) getLayoutInflater().inflate(R.layout.configbuilder_single_plugin, null); + unbinder = ButterKnife.bind(this, baseView); + update(); } - ViewGroup.LayoutParams params = listView.getLayoutParams(); - params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); - listView.setLayoutParams(params); - } + public LinearLayout getBaseView() { + return baseView; + } + + public void update() { + enabledExclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.GONE : View.VISIBLE); + enabledInclusive.setVisibility(areMultipleSelectionsAllowed(pluginType) ? View.VISIBLE : View.GONE); + enabledExclusive.setChecked(plugin.isEnabled(pluginType)); + enabledInclusive.setChecked(plugin.isEnabled(pluginType)); + enabledInclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); + enabledExclusive.setEnabled(!plugin.pluginDescription.alwaysEnabled); + pluginName.setText(plugin.getName()); + if (plugin.getDescription() == null) pluginDescription.setVisibility(View.GONE); + else { + pluginDescription.setVisibility(View.VISIBLE); + pluginDescription.setText(plugin.getDescription()); + } + pluginPreferences.setVisibility(plugin.getPreferencesId() == -1 || !plugin.isEnabled(pluginType) ? View.INVISIBLE : View.VISIBLE); + pluginVisibility.setVisibility(plugin.hasFragment() ? View.VISIBLE : View.INVISIBLE); + pluginVisibility.setEnabled(!(plugin.pluginDescription.neverVisible || plugin.pluginDescription.alwayVisible) && plugin.isEnabled(pluginType)); + pluginVisibility.setChecked(plugin.isFragmentVisible()); + } + + @OnClick(R.id.plugin_visibility) + void onVisibilityChanged() { + plugin.setFragmentVisible(pluginType, pluginVisibility.isChecked()); + ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxVisible"); + MainApp.bus().post(new EventRefreshGui()); + ConfigBuilderPlugin.getPlugin().logPluginStatus(); + } + + @OnClick({R.id.plugin_enabled_exclusive, R.id.plugin_enabled_inclusive}) + void onEnabledChanged() { + plugin.switchAllowed(new PluginSwitcher(), getActivity()); + } + + @OnClick(R.id.plugin_preferences) + void onPluginPreferencesClicked() { + PasswordProtection.QueryPassword(getContext(), R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(getContext(), PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + startActivity(i); + }, null); + } + + public void unbind() { + unbinder.unbind(); + } + + public class PluginSwitcher { + public void invoke() { + boolean enabled = enabledExclusive.getVisibility() == View.VISIBLE ? enabledExclusive.isChecked() : enabledInclusive.isChecked(); + plugin.setPluginEnabled(pluginType, enabled); + plugin.setFragmentVisible(pluginType, enabled); + processOnEnabledCategoryChanged(plugin, pluginType); + updateGUI(); + ConfigBuilderPlugin.getPlugin().storeSettings("CheckedCheckboxEnabled"); + MainApp.bus().post(new EventRefreshGui()); + MainApp.bus().post(new EventConfigBuilderChange()); + ConfigBuilderPlugin.getPlugin().logPluginStatus(); + FabricPrivacy.getInstance().logCustom(new CustomEvent("ConfigurationChange")); + } + + public void cancel(){ + updateGUI(); + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 35130c5209..886eeb3db7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -43,7 +43,7 @@ import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.SensitivityOref0.SensitivityOref0Plugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref0Plugin; import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.CommandQueue; import info.nightscout.utils.FabricPrivacy; @@ -87,11 +87,12 @@ public class ConfigBuilderPlugin extends PluginBase { super(new PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(ConfigBuilderFragment.class.getName()) - .showInList(false) + .showInList(true) .alwaysEnabled(true) - .alwayVisible(true) + .alwayVisible(false) .pluginName(R.string.configbuilder) .shortName(R.string.configbuilder_shortname) + .description(R.string.description_config_builder) ); } @@ -112,9 +113,17 @@ public class ConfigBuilderPlugin extends PluginBase { pluginList = MainApp.getPluginsList(); upgradeSettings(); loadSettings(); + setAlwaysEnabledPluginsEnabled(); MainApp.bus().post(new EventAppInitialized()); } + private void setAlwaysEnabledPluginsEnabled() { + for (PluginBase plugin : pluginList) { + if (plugin.pluginDescription.alwaysEnabled) plugin.setPluginEnabled(plugin.getType(), true); + } + storeSettings("setAlwaysEnabledPluginsEnabled"); + } + public void storeSettings(String from) { if (pluginList != null) { if (Config.logPrefsChange) @@ -519,6 +528,7 @@ public class ConfigBuilderPlugin extends PluginBase { // deliver SMB DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); + detailedBolusInfo.lastKnownBolusTime = activeTreatments.getLastBolusTime(); detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; detailedBolusInfo.insulin = request.smb; detailedBolusInfo.isSMB = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java index 1424013440..2f896ba91b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesFragment.java @@ -1,12 +1,18 @@ package info.nightscout.androidaps.plugins.ConstraintsObjectives; +import android.animation.LayoutTransition; import android.app.Activity; -import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.CardView; import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; +import android.text.Html; +import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,203 +22,40 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.squareup.otto.Subscribe; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventConfigBuilderChange; +import info.nightscout.androidaps.events.EventNewBG; +import info.nightscout.androidaps.events.EventProfileSwitchChange; +import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective; import info.nightscout.utils.FabricPrivacy; -import info.nightscout.utils.T; public class ObjectivesFragment extends SubscriberFragment { private static Logger log = LoggerFactory.getLogger(ObjectivesFragment.class); RecyclerView recyclerView; - LinearLayoutManager llm; CheckBox enableFake; - LinearLayout fake_layout; TextView reset; + ObjectivesAdapter objectivesAdapter = new ObjectivesAdapter(); + Handler handler = new Handler(Looper.getMainLooper()); - public class RecyclerViewAdapter extends RecyclerView.Adapter { - - List objectives; - - RecyclerViewAdapter(List objectives) { - this.objectives = objectives; - } - + private Runnable objectiveUpdater = new Runnable() { @Override - public ObjectiveViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.objectives_item, viewGroup, false); - return new ObjectiveViewHolder(v); + public void run() { + handler.postDelayed(this, 60 * 1000); + updateGUI(); } - - @Override - public void onBindViewHolder(ObjectiveViewHolder holder, int position) { - ObjectivesPlugin.Objective o = objectives.get(position); - ObjectivesPlugin.RequirementResult requirementsMet = ObjectivesPlugin.getPlugin().requirementsMet(position); - Context context = MainApp.instance().getApplicationContext(); - holder.position.setText(String.valueOf(position + 1)); - holder.objective.setText(o.objective); - holder.gate.setText(o.gate); - holder.duration.setText(MainApp.gs(R.string.objectives_minimalduration) + " " + o.durationInDays + " " + MainApp.gs(R.string.days)); - holder.progress.setText(requirementsMet.comment); - holder.started.setText(o.started.toLocaleString()); - holder.accomplished.setText(o.accomplished.toLocaleString()); - - holder.startButton.setTag(o); - holder.verifyButton.setTag(o); - - holder.startButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - ObjectivesPlugin.Objective o = (ObjectivesPlugin.Objective) v.getTag(); - o.started = new Date(); - updateGUI(); - ObjectivesPlugin.saveProgress(); - } - }); - holder.verifyButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - ObjectivesPlugin.Objective o = (ObjectivesPlugin.Objective) v.getTag(); - if (ObjectivesPlugin.getPlugin().requirementsMet(o.num).done || enableFake.isChecked()) { - o.accomplished = new Date(); - updateGUI(); - ObjectivesPlugin.saveProgress(); - } - } - }); - - long prevObjectiveAccomplishedTime = position > 0 ? - objectives.get(position - 1).accomplished.getTime() : -1; - - int phase = modifyVisibility(position, prevObjectiveAccomplishedTime, - o.started.getTime(), o.durationInDays, - o.accomplished.getTime(), requirementsMet.done, enableFake.isChecked()); - - switch (phase) { - case 0: - // Phase 0: previous not completed - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - break; - case 1: - // Phase 1: not started - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyLayout.setVisibility(View.GONE); - holder.started.setVisibility(View.GONE); - break; - case 2: - // Phase 2: started, waiting for duration and met requirements - holder.startButton.setEnabled(false); - holder.verifyLayout.setVisibility(View.GONE); - break; - case 3: - // Phase 3: started, after duration, requirements met - holder.startButton.setEnabled(false); - holder.accomplished.setVisibility(View.INVISIBLE); - break; - case 4: - // Phase 4: verified - holder.gateLayout.setVisibility(View.GONE); - holder.startedLayout.setVisibility(View.GONE); - holder.durationLayout.setVisibility(View.GONE); - holder.progressLayout.setVisibility(View.GONE); - holder.verifyButton.setVisibility(View.INVISIBLE); - break; - default: - // should not happen - } - } - - - @Override - public int getItemCount() { - return objectives.size(); - } - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - public class ObjectiveViewHolder extends RecyclerView.ViewHolder { - CardView cv; - TextView position; - TextView objective; - LinearLayout gateLayout; - TextView gate; - TextView duration; - LinearLayout durationLayout; - TextView progress; - LinearLayout progressLayout; - TextView started; - Button startButton; - LinearLayout startedLayout; - TextView accomplished; - Button verifyButton; - LinearLayout verifyLayout; - - ObjectiveViewHolder(View itemView) { - super(itemView); - cv = (CardView) itemView.findViewById(R.id.objectives_cardview); - position = (TextView) itemView.findViewById(R.id.objectives_position); - objective = (TextView) itemView.findViewById(R.id.objectives_objective); - durationLayout = (LinearLayout) itemView.findViewById(R.id.objectives_duration_linearlayout); - duration = (TextView) itemView.findViewById(R.id.objectives_duration); - progressLayout = (LinearLayout) itemView.findViewById(R.id.objectives_progresslayout); - progress = (TextView) itemView.findViewById(R.id.objectives_progress); - gateLayout = (LinearLayout) itemView.findViewById(R.id.objectives_gate_linearlayout); - gate = (TextView) itemView.findViewById(R.id.objectives_gate); - startedLayout = (LinearLayout) itemView.findViewById(R.id.objectives_start_linearlayout); - started = (TextView) itemView.findViewById(R.id.objectives_started); - startButton = (Button) itemView.findViewById(R.id.objectives_start); - verifyLayout = (LinearLayout) itemView.findViewById(R.id.objectives_verify_linearlayout); - accomplished = (TextView) itemView.findViewById(R.id.objectives_accomplished); - verifyButton = (Button) itemView.findViewById(R.id.objectives_verify); - } - } - } - - /** - * returns an int, which represents the phase the current objective is at. - * - * this is mainly used for unit-testing the conditions - * - * @param currentPosition - * @param prevObjectiveAccomplishedTime - * @param objectiveStartedTime - * @param durationInDays - * @param objectiveAccomplishedTime - * @param requirementsMet - * @return - */ - public int modifyVisibility(int currentPosition, - long prevObjectiveAccomplishedTime, - long objectiveStartedTime, int durationInDays, - long objectiveAccomplishedTime, boolean requirementsMet, - boolean enableFakeValue) { - Long now = System.currentTimeMillis(); - if (currentPosition > 0 && prevObjectiveAccomplishedTime == 0) { - return 0; - } else if (objectiveStartedTime == 0) { - return 1; - } else if (objectiveStartedTime > 0 && !enableFakeValue - && objectiveAccomplishedTime == 0 - && !(objectiveStartedTime + T.days(durationInDays).msecs() < now && requirementsMet)) { - return 2; - } else if (objectiveAccomplishedTime == 0) { - return 3; - } else { - return 4; - } - } + }; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -220,44 +63,20 @@ public class ObjectivesFragment extends SubscriberFragment { try { View view = inflater.inflate(R.layout.objectives_fragment, container, false); - recyclerView = (RecyclerView) view.findViewById(R.id.objectives_recyclerview); - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(view.getContext()); - recyclerView.setLayoutManager(llm); - enableFake = (CheckBox) view.findViewById(R.id.objectives_fake); - fake_layout = (LinearLayout) view.findViewById(R.id.objectives_fake_layout); - reset = (TextView) view.findViewById(R.id.objectives_reset); - enableFake.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - updateGUI(); - } + recyclerView = view.findViewById(R.id.objectives_recyclerview); + recyclerView.setLayoutManager(new LinearLayoutManager(view.getContext())); + recyclerView.setAdapter(objectivesAdapter); + enableFake = view.findViewById(R.id.objectives_fake); + reset = view.findViewById(R.id.objectives_reset); + enableFake.setOnClickListener(v -> updateGUI()); + reset.setOnClickListener(v -> { + ObjectivesPlugin.getPlugin().reset(); + ObjectivesPlugin.saveProgress(); + recyclerView.getAdapter().notifyDataSetChanged(); + scrollToCurrentObjective(); }); - reset.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - ObjectivesPlugin.getPlugin().initializeData(); - ObjectivesPlugin.saveProgress(); - updateGUI(); - } - }); - - // Add correct translations to array after app is initialized - ObjectivesPlugin.objectives.get(0).objective = MainApp.gs(R.string.objectives_0_objective); - ObjectivesPlugin.objectives.get(1).objective = MainApp.gs(R.string.objectives_1_objective); - ObjectivesPlugin.objectives.get(2).objective = MainApp.gs(R.string.objectives_2_objective); - ObjectivesPlugin.objectives.get(3).objective = MainApp.gs(R.string.objectives_3_objective); - ObjectivesPlugin.objectives.get(4).objective = MainApp.gs(R.string.objectives_4_objective); - ObjectivesPlugin.objectives.get(5).objective = MainApp.gs(R.string.objectives_5_objective); - ObjectivesPlugin.objectives.get(6).objective = MainApp.gs(R.string.objectives_6_objective); - ObjectivesPlugin.objectives.get(7).objective = MainApp.gs(R.string.objectives_7_objective); - ObjectivesPlugin.objectives.get(0).gate = MainApp.gs(R.string.objectives_0_gate); - ObjectivesPlugin.objectives.get(1).gate = MainApp.gs(R.string.objectives_1_gate); - ObjectivesPlugin.objectives.get(2).gate = MainApp.gs(R.string.objectives_2_gate); - ObjectivesPlugin.objectives.get(3).gate = MainApp.gs(R.string.objectives_3_gate); - ObjectivesPlugin.objectives.get(4).gate = MainApp.gs(R.string.objectives_4_gate); - ObjectivesPlugin.objectives.get(5).gate = MainApp.gs(R.string.objectives_5_gate); - - updateGUI(); - + scrollToCurrentObjective(); + startUpdateTimer(); return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -266,14 +85,143 @@ public class ObjectivesFragment extends SubscriberFragment { return null; } + @Override + public synchronized void onDestroyView() { + super.onDestroyView(); + handler.removeCallbacks(objectiveUpdater); + } + + private void startUpdateTimer() { + handler.removeCallbacks(objectiveUpdater); + for (Objective objective : ObjectivesPlugin.getObjectives()) { + if (objective.isStarted() && !objective.isAccomplished()) { + long timeTillNextMinute = (System.currentTimeMillis() - objective.getStartedOn().getTime()) % (60 * 1000); + handler.postDelayed(objectiveUpdater, timeTillNextMinute); + break; + } + } + } + + private void scrollToCurrentObjective() { + for (int i = 0; i < ObjectivesPlugin.getObjectives().size(); i++) { + Objective objective = ObjectivesPlugin.getObjectives().get(i); + if (!objective.isStarted() || !objective.isAccomplished()) { + RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) { + @Override + protected int getVerticalSnapPreference() { + return LinearSmoothScroller.SNAP_TO_START; + } + + @Override + protected int calculateTimeForScrolling(int dx) { + return super.calculateTimeForScrolling(dx) * 4; + } + }; + smoothScroller.setTargetPosition(i); + recyclerView.getLayoutManager().startSmoothScroll(smoothScroller); + break; + } + } + } + + private class ObjectivesAdapter extends RecyclerView.Adapter { + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.objectives_item, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Objective objective = ObjectivesPlugin.getObjectives().get(position); + holder.title.setText(MainApp.gs(R.string.nth_objective, position + 1)); + if (objective.getObjective() != 0) { + holder.objective.setVisibility(View.VISIBLE); + holder.objective.setText(MainApp.gs(objective.getObjective())); + } else holder.objective.setVisibility(View.GONE); + if (objective.getGate() != 0) { + holder.gate.setVisibility(View.VISIBLE); + holder.gate.setText(MainApp.gs(objective.getGate())); + } else holder.gate.setVisibility(View.GONE); + if (!objective.isStarted()) { + holder.gate.setTextColor(0xFFFFFFFF); + holder.verify.setVisibility(View.GONE); + holder.progress.setVisibility(View.GONE); + if (position == 0 || ObjectivesPlugin.getObjectives().get(position - 1).isAccomplished()) + holder.start.setVisibility(View.VISIBLE); + else holder.start.setVisibility(View.GONE); + } else if (objective.isAccomplished()) { + holder.gate.setTextColor(0xFF4CAF50); + holder.verify.setVisibility(View.GONE); + holder.progress.setVisibility(View.GONE); + holder.start.setVisibility(View.GONE); + } else if (objective.isStarted()) { + holder.gate.setTextColor(0xFFFFFFFF); + holder.verify.setVisibility(View.VISIBLE); + holder.verify.setEnabled(objective.isCompleted() || enableFake.isChecked()); + holder.start.setVisibility(View.GONE); + holder.progress.setVisibility(View.VISIBLE); + holder.progress.removeAllViews(); + for (Objective.Task task : objective.getTasks()) { + if (task.shouldBeIgnored()) continue; + TextView textView = new TextView(holder.progress.getContext()); + textView.setTextColor(0xFFFFFFFF); + String basicHTML = "%2$s: %3$s"; + String formattedHTML = String.format(basicHTML, task.isCompleted() ? "#4CAF50" : "#FF9800", MainApp.gs(task.getTask()), task.getProgress()); + textView.setText(Html.fromHtml(formattedHTML)); + holder.progress.addView(textView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + } + } + holder.verify.setOnClickListener((view) -> { + objective.setAccomplishedOn(new Date()); + notifyDataSetChanged(); + scrollToCurrentObjective(); + startUpdateTimer(); + }); + holder.start.setOnClickListener((view) -> { + objective.setStartedOn(new Date()); + notifyDataSetChanged(); + scrollToCurrentObjective(); + startUpdateTimer(); + }); + } + + @Override + public int getItemCount() { + return ObjectivesPlugin.getObjectives().size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + + public CardView cardView; + public TextView title; + public TextView objective; + public TextView gate; + public LinearLayout progress; + public Button verify; + public Button start; + + public ViewHolder(View itemView) { + super(itemView); + cardView = (CardView) itemView; + title = itemView.findViewById(R.id.objective_title); + objective = itemView.findViewById(R.id.objective_objective); + gate = itemView.findViewById(R.id.objective_gate); + progress = itemView.findViewById(R.id.objective_progress); + verify = itemView.findViewById(R.id.objective_verify); + start = itemView.findViewById(R.id.objective_start); + } + } + } + @Override public void updateGUI() { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - RecyclerViewAdapter adapter = new RecyclerViewAdapter(ObjectivesPlugin.objectives); - recyclerView.setAdapter(adapter); + objectivesAdapter.notifyDataSetChanged(); }); } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java index 5a899bebe5..a67f694eb8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/ObjectivesPlugin.java @@ -1,20 +1,14 @@ package info.nightscout.androidaps.plugins.ConstraintsObjectives; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Date; import java.util.List; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; @@ -22,12 +16,16 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; -import info.nightscout.androidaps.plugins.Loop.LoopPlugin; -import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; -import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; -import info.nightscout.utils.DateUtil; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective1; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective2; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective3; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective4; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective5; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective6; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective7; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives.Objective8; import info.nightscout.utils.SP; /** @@ -38,6 +36,11 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface private static ObjectivesPlugin objectivesPlugin; + public static List objectives = new ArrayList<>(); + public static boolean bgIsAvailableInNS = false; + public static boolean pumpStatusIsAvailableInNS = false; + public static Integer manualEnacts = 0; + public static ObjectivesPlugin getPlugin() { if (objectivesPlugin == null) { objectivesPlugin = new ObjectivesPlugin(); @@ -45,8 +48,6 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface return objectivesPlugin; } - public static List objectives; - private ObjectivesPlugin() { super(new PluginDescription() .mainType(PluginType.CONSTRAINTS) @@ -55,8 +56,9 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface .showInList(!Config.NSCLIENT && !Config.G5UPLOADER) .pluginName(R.string.objectives) .shortName(R.string.objectives_shortname) + .description(R.string.description_objectives) ); - initializeData(); + setupObjectives(); loadProgress(); } @@ -66,186 +68,38 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface return pump == null || pump.getPumpDescription().isTempBasalCapable; } - public class Objective { - Integer num; - String objective; - String gate; - Date started; - Integer durationInDays; - Date accomplished; - - Objective(Integer num, String objective, String gate, Date started, Integer durationInDays, Date accomplished) { - this.num = num; - this.objective = objective; - this.gate = gate; - this.started = started; - this.durationInDays = durationInDays; - this.accomplished = accomplished; - } - - public void setStarted(Date started) { - this.started = started; - } - - boolean isStarted() { - return started.getTime() > 0; - } - - boolean isFinished() { - return accomplished.getTime() != 0; - } + private void setupObjectives() { + objectives.add(new Objective1()); + objectives.add(new Objective2()); + objectives.add(new Objective3()); + objectives.add(new Objective4()); + objectives.add(new Objective5()); + objectives.add(new Objective6()); + objectives.add(new Objective7()); + objectives.add(new Objective8()); } - // Objective 0 - public static boolean bgIsAvailableInNS = false; - public static boolean pumpStatusIsAvailableInNS = false; - // Objective 1 - public static Integer manualEnacts = 0; - private static final Integer manualEnactsNeeded = 20; - - class RequirementResult { - boolean done = false; - String comment = ""; - - RequirementResult(boolean done, String comment) { - this.done = done; - this.comment = comment; + public void reset() { + for (Objective objective : objectives) { + objective.setStartedOn(null); + objective.setAccomplishedOn(null); } - } - - private String yesOrNo(boolean yes) { - if (yes) return "☺"; - else return "---"; - } - - RequirementResult requirementsMet(Integer objNum) { - switch (objNum) { - case 0: - boolean isVirtualPump = VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); - boolean vpUploadEnabled = SP.getBoolean("virtualpump_uploadstatus", false); - boolean vpUploadNeeded = !isVirtualPump || vpUploadEnabled; - boolean hasBGData = DatabaseHelper.lastBg() != null; - - boolean apsEnabled = false; - APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS(); - if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS)) - apsEnabled = true; - - boolean profileSwitchExists = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null; - - return new RequirementResult(hasBGData && bgIsAvailableInNS && pumpStatusIsAvailableInNS && NSClientPlugin.getPlugin().hasWritePermission() && LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && apsEnabled && vpUploadNeeded && profileSwitchExists, - MainApp.gs(R.string.objectives_bgavailableinns) + ": " + yesOrNo(bgIsAvailableInNS) - + "\n" + MainApp.gs(R.string.nsclienthaswritepermission) + ": " + yesOrNo(NSClientPlugin.getPlugin().hasWritePermission()) - + (isVirtualPump ? "\n" + MainApp.gs(R.string.virtualpump_uploadstatus_title) + ": " + yesOrNo(vpUploadEnabled) : "") - + "\n" + MainApp.gs(R.string.objectives_pumpstatusavailableinns) + ": " + yesOrNo(pumpStatusIsAvailableInNS) - + "\n" + MainApp.gs(R.string.hasbgdata) + ": " + yesOrNo(hasBGData) - + "\n" + MainApp.gs(R.string.loopenabled) + ": " + yesOrNo(LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) - + "\n" + MainApp.gs(R.string.apsselected) + ": " + yesOrNo(apsEnabled) - + "\n" + MainApp.gs(R.string.activate_profile) + ": " + yesOrNo(profileSwitchExists) - ); - case 1: - return new RequirementResult(manualEnacts >= manualEnactsNeeded, - MainApp.gs(R.string.objectives_manualenacts) + ": " + manualEnacts + "/" + manualEnactsNeeded); - case 2: - return new RequirementResult(true, ""); - case 3: - Constraint closedLoopEnabled = new Constraint<>(true); - SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled); - return new RequirementResult(closedLoopEnabled.value(), MainApp.gs(R.string.closedmodeenabled) + ": " + yesOrNo(closedLoopEnabled.value())); - case 4: - double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value(); - boolean maxIobSet = maxIOB > 0; - return new RequirementResult(maxIobSet, MainApp.gs(R.string.maxiobset) + ": " + yesOrNo(maxIobSet)); - default: - return new RequirementResult(true, ""); - } - } - - - void initializeData() { bgIsAvailableInNS = false; pumpStatusIsAvailableInNS = false; manualEnacts = 0; - - objectives = new ArrayList<>(); - objectives.add(new Objective(0, - MainApp.gs(R.string.objectives_0_objective), - MainApp.gs(R.string.objectives_0_gate), - new Date(0), - 0, // 0 day - new Date(0))); - objectives.add(new Objective(1, - MainApp.gs(R.string.objectives_1_objective), - MainApp.gs(R.string.objectives_1_gate), - new Date(0), - 7, // 7 days - new Date(0))); - objectives.add(new Objective(2, - MainApp.gs(R.string.objectives_2_objective), - MainApp.gs(R.string.objectives_2_gate), - new Date(0), - 0, // 0 days - new Date(0))); - objectives.add(new Objective(3, - MainApp.gs(R.string.objectives_3_objective), - MainApp.gs(R.string.objectives_3_gate), - new Date(0), - 5, // 5 days - new Date(0))); - objectives.add(new Objective(4, - MainApp.gs(R.string.objectives_4_objective), - MainApp.gs(R.string.objectives_4_gate), - new Date(0), - 1, - new Date(0))); - objectives.add(new Objective(5, - MainApp.gs(R.string.objectives_5_objective), - MainApp.gs(R.string.objectives_5_gate), - new Date(0), - 7, - new Date(0))); - objectives.add(new Objective(6, - MainApp.gs(R.string.objectives_6_objective), - "", - new Date(0), - 28, - new Date(0))); - objectives.add(new Objective(7, - MainApp.gs(R.string.objectives_7_objective), - "", - new Date(0), - 28, - new Date(0))); + saveProgress(); } public static void saveProgress() { - if (objectives != null) { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); - SharedPreferences.Editor editor = settings.edit(); - for (int num = 0; num < objectives.size(); num++) { - Objective o = objectives.get(num); - editor.putString("Objectives" + num + "started", Long.toString(o.started.getTime())); - editor.putString("Objectives" + num + "accomplished", Long.toString(o.accomplished.getTime())); - } - editor.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS); - editor.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS); - editor.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts)); - editor.apply(); - if (Config.logPrefsChange) - log.debug("Objectives stored"); - } + SP.putBoolean("Objectives" + "bgIsAvailableInNS", bgIsAvailableInNS); + SP.putBoolean("Objectives" + "pumpStatusIsAvailableInNS", pumpStatusIsAvailableInNS); + SP.putString("Objectives" + "manualEnacts", Integer.toString(manualEnacts)); + if (Config.logPrefsChange) + log.debug("Objectives stored"); + MainApp.bus().post(new EventObjectivesSaved()); } private void loadProgress() { - for (int num = 0; num < objectives.size(); num++) { - Objective o = objectives.get(num); - try { - o.started = new Date(SP.getLong("Objectives" + num + "started", 0L)); - o.accomplished = new Date(SP.getLong("Objectives" + num + "accomplished", 0L)); - } catch (Exception e) { - log.error("Unhandled exception", e); - } - } bgIsAvailableInNS = SP.getBoolean("Objectives" + "bgIsAvailableInNS", false); pumpStatusIsAvailableInNS = SP.getBoolean("Objectives" + "pumpStatusIsAvailableInNS", false); try { @@ -257,11 +111,15 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface log.debug("Objectives loaded"); } + public static List getObjectives() { + return objectives; + } + /** * Constraints interface **/ @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!objectives.get(0).isStarted()) value.set(false, String.format(MainApp.gs(R.string.objectivenotstarted), 1), this); return value; @@ -297,7 +155,7 @@ public class ObjectivesPlugin extends PluginBase implements ConstraintsInterface @Override public Constraint applyMaxIOBConstraints(Constraint maxIob) { - if (objectives.get(3).isStarted() && !objectives.get(3).isFinished()) + if (objectives.get(3).isStarted() && !objectives.get(3).isAccomplished()) maxIob.set(0d, String.format(MainApp.gs(R.string.objectivenotfinished), 4), this); return maxIob; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/events/EventObjectivesSaved.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/events/EventObjectivesSaved.java new file mode 100644 index 0000000000..1822368cf9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/events/EventObjectivesSaved.java @@ -0,0 +1,6 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.events; + +import info.nightscout.androidaps.events.Event; + +public class EventObjectivesSaved extends Event { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java new file mode 100644 index 0000000000..bba29c5201 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective.java @@ -0,0 +1,140 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import android.support.annotation.StringRes; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.utils.SP; + +public abstract class Objective { + + private int number; + @StringRes + private int objective; + @StringRes + private int gate; + private Date startedOn; + private Date accomplishedOn; + private List tasks = new ArrayList<>(); + + public Objective(int number, @StringRes int objective, @StringRes int gate) { + this.number = number; + this.objective = objective; + this.gate = gate; + startedOn = new Date(SP.getLong("Objectives" + number + "started", 0L)); + if (startedOn.getTime() == 0L) startedOn = null; + accomplishedOn = new Date(SP.getLong("Objectives" + number + "accomplished", 0L)); + if (accomplishedOn.getTime() == 0L) accomplishedOn = null; + setupTasks(tasks); + for (Task task : tasks) task.objective = this; + } + + public boolean isCompleted() { + for (Task task : tasks) { + if (!task.shouldBeIgnored() && !task.isCompleted()) + return false; + } + return true; + } + + public boolean isAccomplished() { + return accomplishedOn != null; + } + + public boolean isStarted() { + return startedOn != null; + } + + public Date getStartedOn() { + return startedOn; + } + + public int getObjective() { + return objective; + } + + public int getGate() { + return gate; + } + + public void setStartedOn(Date startedOn) { + this.startedOn = startedOn; + SP.putLong("Objectives" + number + "started", startedOn == null ? 0 : startedOn.getTime()); + } + + public void setAccomplishedOn(Date accomplishedOn) { + this.accomplishedOn = accomplishedOn; + SP.putLong("Objectives" + number + "accomplished", accomplishedOn == null ? 0 : accomplishedOn.getTime()); + } + + protected void setupTasks(List tasks) { + + } + + public List getTasks() { + return tasks; + } + + public abstract class Task { + @StringRes + private int task; + private Objective objective; + + public Task(@StringRes int task) { + this.task = task; + } + + public int getTask() { + return task; + } + + protected Objective getObjective() { + return objective; + } + + public abstract boolean isCompleted(); + + public String getProgress() { + return MainApp.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet); + } + + public boolean shouldBeIgnored() { + return false; + } + } + + public class MinimumDurationTask extends Task { + + private long minimumDuration; + + public MinimumDurationTask(long minimumDuration) { + super(R.string.time_elapsed); + this.minimumDuration = minimumDuration; + } + + @Override + public boolean isCompleted() { + return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn().getTime() >= minimumDuration; + } + + @Override + public String getProgress() { + return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn().getTime()) + + " / " + getDurationText(minimumDuration); + } + + private String getDurationText(long duration) { + int days = (int) Math.floor((double) duration / (24D * 60D * 60D * 1000D)); + int hours = (int) Math.floor((double) duration / (60D * 60D * 1000D)); + int minutes = (int) Math.floor((double) duration / (60D * 1000D)); + if (days > 0) return MainApp.gq(R.plurals.objective_days, days, days); + else if (hours > 0) return MainApp.gq(R.plurals.objective_hours, hours, hours); + else return MainApp.gq(R.plurals.objective_minutes, minutes, minutes); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java new file mode 100644 index 0000000000..e374126b11 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective1.java @@ -0,0 +1,84 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.DatabaseHelper; +import info.nightscout.androidaps.interfaces.APSInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.Loop.LoopPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.SP; + +public class Objective1 extends Objective { + + public Objective1() { + super(0, R.string.objectives_0_objective, R.string.objectives_0_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new Task(R.string.objectives_bgavailableinns) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.bgIsAvailableInNS; + } + }); + tasks.add(new Task(R.string.nsclienthaswritepermission) { + @Override + public boolean isCompleted() { + return NSClientPlugin.getPlugin().hasWritePermission(); + } + }); + tasks.add(new Task(R.string.virtualpump_uploadstatus_title) { + @Override + public boolean isCompleted() { + return SP.getBoolean("virtualpump_uploadstatus", false); + } + + @Override + public boolean shouldBeIgnored() { + return !VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); + } + }); + tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.pumpStatusIsAvailableInNS; + } + }); + tasks.add(new Task(R.string.hasbgdata) { + @Override + public boolean isCompleted() { + return DatabaseHelper.lastBg() != null; + } + }); + tasks.add(new Task(R.string.loopenabled) { + @Override + public boolean isCompleted() { + return LoopPlugin.getPlugin().isEnabled(PluginType.LOOP); + } + }); + tasks.add(new Task(R.string.apsselected) { + @Override + public boolean isCompleted() { + APSInterface usedAPS = ConfigBuilderPlugin.getActiveAPS(); + if (usedAPS != null && ((PluginBase) usedAPS).isEnabled(PluginType.APS)) + return true; + return false; + } + }); + tasks.add(new Task(R.string.activate_profile) { + @Override + public boolean isCompleted() { + return TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now()) != null; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java new file mode 100644 index 0000000000..305f00ec6f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective2.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; + +public class Objective2 extends Objective { + + public static final int MANUAL_ENACTS_NEEDED = 20; + + public Objective2() { + super(1, R.string.objectives_1_objective, R.string.objectives_1_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.objectives_manualenacts) { + @Override + public boolean isCompleted() { + return ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED; + } + + @Override + public String getProgress() { + if (ObjectivesPlugin.manualEnacts >= MANUAL_ENACTS_NEEDED) return MainApp.gs(R.string.completed_well_done); + else return ObjectivesPlugin.manualEnacts + " / " + MANUAL_ENACTS_NEEDED; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java new file mode 100644 index 0000000000..8a84e3c83c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective3.java @@ -0,0 +1,10 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import info.nightscout.androidaps.R; + +public class Objective3 extends Objective { + + public Objective3() { + super(2, R.string.objectives_2_objective, R.string.objectives_2_gate); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java new file mode 100644 index 0000000000..36a98babc1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective4.java @@ -0,0 +1,27 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.plugins.ConstraintsSafety.SafetyPlugin; + +public class Objective4 extends Objective { + + public Objective4() { + super(3, R.string.objectives_3_objective, R.string.objectives_3_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(5L * 24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.closedmodeenabled) { + @Override + public boolean isCompleted() { + Constraint closedLoopEnabled = new Constraint<>(true); + SafetyPlugin.getPlugin().isClosedLoopAllowed(closedLoopEnabled); + return closedLoopEnabled.value(); + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java new file mode 100644 index 0000000000..563a114e05 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective5.java @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +public class Objective5 extends Objective { + + public Objective5() { + super(4, R.string.objectives_4_objective, R.string.objectives_4_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(24L * 60L * 60L * 1000L)); + tasks.add(new Task(R.string.maxiobset) { + @Override + public boolean isCompleted() { + double maxIOB = MainApp.getConstraintChecker().getMaxIOBAllowed().value(); + return maxIOB > 0; + } + }); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java new file mode 100644 index 0000000000..7106d7522b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective6.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective6 extends Objective { + + public Objective6() { + super(5, R.string.objectives_5_objective, R.string.objectives_5_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(7L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java new file mode 100644 index 0000000000..cfd041bf13 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective7.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective7 extends Objective { + + public Objective7() { + super(6, R.string.objectives_6_objective, 0); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java new file mode 100644 index 0000000000..376d5fb7a2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsObjectives/objectives/Objective8.java @@ -0,0 +1,17 @@ +package info.nightscout.androidaps.plugins.ConstraintsObjectives.objectives; + +import java.util.List; + +import info.nightscout.androidaps.R; + +public class Objective8 extends Objective { + + public Objective8() { + super(7, R.string.objectives_7_objective, R.string.objectives_7_gate); + } + + @Override + protected void setupTasks(List tasks) { + tasks.add(new MinimumDurationTask(28L * 24L * 60L * 60L * 1000L)); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java index 6a9613e92a..3d17970edc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java @@ -49,7 +49,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { * Constraints interface **/ @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!ConfigBuilderPlugin.getActivePump().getPumpDescription().isTempBasalCapable) value.set(false, MainApp.gs(R.string.pumpisnottempbasalcapable), this); return value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java index 14eb318af4..84996f1a37 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Food/FoodPlugin.java @@ -26,6 +26,7 @@ public class FoodPlugin extends PluginBase { .fragmentClass(FoodFragment.class.getName()) .pluginName(R.string.food) .shortName(R.string.food_short) + .description(R.string.description_food) ); this.service = new FoodService(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java index 2c46cd7bb6..5d4d1187ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefBasePlugin.java @@ -29,6 +29,7 @@ public abstract class InsulinOrefBasePlugin extends PluginBase implements Insuli .fragmentClass(InsulinFragment.class.getName()) .pluginName(R.string.fastactinginsulin) .shortName(R.string.insulin_shortname) + .visibleByDefault(false) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefFreePeakPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefFreePeakPlugin.java index a710f742a7..c1fc44438f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefFreePeakPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefFreePeakPlugin.java @@ -24,7 +24,8 @@ public class InsulinOrefFreePeakPlugin extends InsulinOrefBasePlugin { super(); pluginDescription .pluginName(R.string.free_peak_oref) - .preferencesId(R.xml.pref_insulinoreffreepeak); + .preferencesId(R.xml.pref_insulinoreffreepeak) + .description(R.string.description_insulin_free_peak); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefRapidActingPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefRapidActingPlugin.java index a8f9761771..c275aef3fd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefRapidActingPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefRapidActingPlugin.java @@ -22,7 +22,8 @@ public class InsulinOrefRapidActingPlugin extends InsulinOrefBasePlugin { private InsulinOrefRapidActingPlugin() { super(); pluginDescription - .pluginName(R.string.rapid_acting_oref); + .pluginName(R.string.rapid_acting_oref) + .description(R.string.description_insulin_rapid); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefUltraRapidActingPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefUltraRapidActingPlugin.java index ba5fc99011..94e5910470 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefUltraRapidActingPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Insulin/InsulinOrefUltraRapidActingPlugin.java @@ -22,7 +22,8 @@ public class InsulinOrefUltraRapidActingPlugin extends InsulinOrefBasePlugin { private InsulinOrefUltraRapidActingPlugin() { super(); pluginDescription - .pluginName(R.string.ultrarapid_oref); + .pluginName(R.string.ultrarapid_oref) + .description(R.string.description_insulin_ultra_rapid); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java index 79ab63e2ac..dd2c50d8c9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -16,8 +16,8 @@ import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; import info.nightscout.androidaps.plugins.Overview.graphExtensions.Scale; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.utils.SP; @@ -26,7 +26,7 @@ import info.nightscout.utils.SP; */ public class AutosensData implements DataPointWithLabelInterface { - private static Logger log = LoggerFactory.getLogger(AutosensData.class); + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); public void setChartTime(long chartTime) { this.chartTime = chartTime; @@ -56,11 +56,11 @@ public class AutosensData implements DataPointWithLabelInterface { } public long time = 0L; - long chartTime; + public double bg = 0; // mgdl + private long chartTime; public String pastSensitivity = ""; public double deviation = 0d; - boolean nonCarbsDeviation = false; - public boolean nonEqualDeviation = false; + public boolean validDeviation = false; List activeCarbsList = new ArrayList<>(); double absorbed = 0d; public double carbsFromBolus = 0d; @@ -70,15 +70,23 @@ public class AutosensData implements DataPointWithLabelInterface { public double avgDelta = 0d; public double avgDeviation = 0d; - public double autosensRatio = 1d; + public AutosensResult autosensResult = new AutosensResult(); public double slopeFromMaxDeviation = 0; public double slopeFromMinDeviation = 999; public double usedMinCarbsImpact = 0d; public boolean failoverToMinAbsorbtionRate = false; + // Oref1 + public boolean absorbing = false; + public double mealCarbs = 0; + public int mealStartCounter = 999; + public String type = ""; + public boolean uam = false; + public List extraDeviation = new ArrayList<>(); + @Override public String toString() { - return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensRatio + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation =" + slopeFromMinDeviation; + return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Delta=" + delta + " avgDelta=" + avgDelta + " Bgi=" + bgi + " Deviation=" + deviation + " avgDeviation=" + avgDeviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob + " autosensRatio=" + autosensResult.ratio + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation=" + slopeFromMinDeviation; } public int minOld() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java index 4e0f8ef107..c83095e620 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensResult.java @@ -9,12 +9,12 @@ import org.slf4j.LoggerFactory; * Created by mike on 06.01.2017. */ public class AutosensResult { - private static Logger log = LoggerFactory.getLogger(AutosensResult.class); + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); //default values to show when autosens algorithm is not called public double ratio = 1d; public double carbsAbsorbed = 0d; - public String sensResult = "autosens deactivated"; + public String sensResult = "autosens not available"; public String pastSensitivity = ""; public String ratioLimit = ""; @@ -32,4 +32,8 @@ public class AutosensResult { return ret; } + @Override + public String toString() { + return json().toString(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 89d66fd891..096668b996 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -35,9 +35,11 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.OpenAPSSMB.OpenAPSSMBPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityOref1Plugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.T; import static info.nightscout.utils.DateUtil.now; @@ -46,7 +48,7 @@ import static info.nightscout.utils.DateUtil.now; */ public class IobCobCalculatorPlugin extends PluginBase { - private Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); + private Logger log = LoggerFactory.getLogger("AUTOSENS"); private static IobCobCalculatorPlugin plugin = null; @@ -68,7 +70,7 @@ public class IobCobCalculatorPlugin extends PluginBase { final Object dataLock = new Object(); boolean stopCalculationTrigger = false; - private IobCobThread thread = null; + private Thread thread = null; public IobCobCalculatorPlugin() { super(new PluginDescription() @@ -130,11 +132,15 @@ public class IobCobCalculatorPlugin extends PluginBase { public static long roundUpTime(long time) { if (time % 60000 == 0) return time; - long rouded = (time / 60000 + 1) * 60000; - return rouded; + long rounded = (time / 60000 + 1) * 60000; + return rounded; } void loadBgData(long start) { + if (start < oldestDataAvailable()) { + start = oldestDataAvailable(); + log.debug("Limiting BG data to oldest data available: " + DateUtil.dateAndTimeString(start)); + } bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime((long) (start - 60 * 60 * 1000L * (24 + dia)), false); log.debug("BG data loaded. Size: " + bgReadings.size() + " Start date: " + DateUtil.dateAndTimeString(start)); } @@ -292,7 +298,7 @@ public class IobCobCalculatorPlugin extends PluginBase { long now = System.currentTimeMillis(); long oldestDataAvailable = TreatmentsPlugin.getPlugin().oldestDataAvailable(); - long getBGDataFrom = Math.max(oldestDataAvailable, (long) (now - 60 * 60 * 1000L * (24 + MainApp.getConfigBuilder().getProfile().getDia()))); + long getBGDataFrom = Math.max(oldestDataAvailable, (long) (now - T.hours(1).msecs() * (24 + MainApp.getConfigBuilder().getProfile().getDia()))); log.debug("Limiting data to oldest available temps: " + new Date(oldestDataAvailable).toString()); return getBGDataFrom; } @@ -347,13 +353,12 @@ public class IobCobCalculatorPlugin extends PluginBase { return null; } - public BasalData getBasalData(long time) { + public BasalData getBasalData(Profile profile, long time) { long now = System.currentTimeMillis(); time = roundUpTime(time); BasalData retval = basalDataTable.get(time); if (retval == null) { retval = new BasalData(); - Profile profile = MainApp.getConfigBuilder().getProfile(time); TemporaryBasal tb = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(time); retval.basal = profile.getBasal(time); if (tb != null) { @@ -377,22 +382,20 @@ public class IobCobCalculatorPlugin extends PluginBase { public AutosensData getAutosensData(long time) { synchronized (dataLock) { long now = System.currentTimeMillis(); - if (time > now) + if (time > now) { return null; + } Long previous = findPreviousTimeFromBucketedData(time); - if (previous == null) + if (previous == null) { return null; + } time = roundUpTime(previous); AutosensData data = autosensDataTable.get(time); if (data != null) { - //log.debug(">>> getAutosensData Cache hit " + data.log(time)); + //log.debug(">>> AUTOSENSDATA Cache hit " + data.toString()); return data; } else { - if (time > now) { - // data may not be calculated yet, use last data - return getLastAutosensData("getAutosensData"); - } - //log.debug(">>> getAutosensData Cache miss " + new Date(time).toLocaleString()); + //log.debug(">>> AUTOSENSDATA Cache miss " + new Date(time).toLocaleString()); return null; } } @@ -400,6 +403,14 @@ public class IobCobCalculatorPlugin extends PluginBase { @Nullable public AutosensData getLastAutosensDataSynchronized(String reason) { + if (thread != null && thread.isAlive()) { + log.debug("AUTOSENSDATA is waiting for calculation thread: " + reason); + try { + thread.join(5000); + } catch (InterruptedException ignored) { + } + log.debug("AUTOSENSDATA finished waiting for calculation thread: " + reason); + } synchronized (dataLock) { return getLastAutosensData(reason); } @@ -453,10 +464,18 @@ public class IobCobCalculatorPlugin extends PluginBase { log.debug("AUTOSENSDATA null: data is old (" + reason + ") size()=" + autosensDataTable.size() + " lastdata=" + DateUtil.dateAndTimeString(data.time)); return null; } else { + log.debug("AUTOSENSDATA (" + reason + ") " + data.toString()); return data; } } + public String lastDataTime() { + if (autosensDataTable.size() > 0) + return DateUtil.dateAndTimeString(autosensDataTable.valueAt(autosensDataTable.size() - 1).time); + else + return "autosensDataTable empty"; + } + public IobTotal[] calculateIobArrayInDia(Profile profile) { // predict IOB out to DIA plus 30m long time = System.currentTimeMillis(); @@ -491,14 +510,10 @@ public class IobCobCalculatorPlugin extends PluginBase { public AutosensResult detectSensitivityWithLock(long fromTime, long toTime) { synchronized (dataLock) { - return detectSensitivity(fromTime, toTime); + return ConfigBuilderPlugin.getActiveSensitivity().detectSensitivity(fromTime, toTime); } } - static AutosensResult detectSensitivity(long fromTime, long toTime) { - return ConfigBuilderPlugin.getActiveSensitivity().detectSensitivity(fromTime, toTime); - } - public static JSONArray convertToJSONArray(IobTotal[] iobArray) { JSONArray array = new JSONArray(); for (int i = 0; i < iobArray.length; i++) { @@ -542,7 +557,10 @@ public class IobCobCalculatorPlugin extends PluginBase { public void runCalculation(String from, long start, boolean bgDataReload, Event cause) { log.debug("Starting calculation thread: " + from); if (thread == null || thread.getState() == Thread.State.TERMINATED) { - thread = new IobCobThread(this, from, start, bgDataReload, cause); + if (SensitivityOref1Plugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) + thread = new IobCobOref1Thread(this, from, start, bgDataReload, cause); + else + thread = new IobCobThread(this, from, start, bgDataReload, cause); thread.start(); } } @@ -581,7 +599,9 @@ public class IobCobCalculatorPlugin extends PluginBase { ev.isChanged(R.string.key_age) || ev.isChanged(R.string.key_absorption_maxtime) || ev.isChanged(R.string.key_openapsama_min_5m_carbimpact) || - ev.isChanged(R.string.key_absorption_cutoff) + ev.isChanged(R.string.key_absorption_cutoff) || + ev.isChanged(R.string.key_openapsama_autosens_max) || + ev.isChanged(R.string.key_openapsama_autosens_min) ) { stopCalculation("onEventPreferenceChange"); synchronized (dataLock) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java new file mode 100644 index 0000000000..d10d0ebe29 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobOref1Thread.java @@ -0,0 +1,362 @@ +package info.nightscout.androidaps.plugins.IobCobCalculator; + +import android.content.Context; +import android.os.PowerManager; +import android.os.SystemClock; +import android.support.v4.util.LongSparseArray; + +import com.crashlytics.android.answers.CustomEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.List; + +import info.nightscout.androidaps.BuildConfig; +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.db.TempTarget; +import info.nightscout.androidaps.events.Event; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; +import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; +import info.nightscout.androidaps.plugins.Treatments.Treatment; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.FabricPrivacy; +import info.nightscout.utils.SP; + +import static info.nightscout.utils.DateUtil.now; +import static java.util.Calendar.MINUTE; + +/** + * Created by mike on 23.01.2018. + */ + +public class IobCobOref1Thread extends Thread { + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); + private final Event cause; + + private IobCobCalculatorPlugin iobCobCalculatorPlugin; + private boolean bgDataReload; + private String from; + private long start; + + private PowerManager.WakeLock mWakeLock; + + public IobCobOref1Thread(IobCobCalculatorPlugin plugin, String from, long start, boolean bgDataReload, Event cause) { + super(); + + this.iobCobCalculatorPlugin = plugin; + this.bgDataReload = bgDataReload; + this.from = from; + this.cause = cause; + this.start = start; + + PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "iobCobThread"); + } + + @Override + public final void run() { + mWakeLock.acquire(); + try { + log.debug("AUTOSENSDATA thread started: " + from); + if (MainApp.getConfigBuilder() == null) { + log.debug("Aborting calculation thread (ConfigBuilder not ready): " + from); + return; // app still initializing + } + if (!MainApp.getConfigBuilder().isProfileValid("IobCobThread")) { + log.debug("Aborting calculation thread (No profile): " + from); + return; // app still initializing + } + //log.debug("Locking calculateSensitivityData"); + + long oldestTimeWithData = iobCobCalculatorPlugin.oldestDataAvailable(); + + synchronized (iobCobCalculatorPlugin.dataLock) { + if (bgDataReload) { + iobCobCalculatorPlugin.loadBgData(start); + iobCobCalculatorPlugin.createBucketedData(); + } + List bucketed_data = iobCobCalculatorPlugin.getBucketedData(); + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); + + if (bucketed_data == null || bucketed_data.size() < 3) { + log.debug("Aborting calculation thread (No bucketed data available): " + from); + return; + } + + long prevDataTime = IobCobCalculatorPlugin.roundUpTime(bucketed_data.get(bucketed_data.size() - 3).date); + log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString()); + AutosensData previous = autosensDataTable.get(prevDataTime); + // start from oldest to be able sub cob + for (int i = bucketed_data.size() - 4; i >= 0; i--) { + String progress = i + (MainApp.isDev() ? " (" + from + ")" : ""); + MainApp.bus().post(new EventIobCalculationProgress(progress)); + + if (iobCobCalculatorPlugin.stopCalculationTrigger) { + iobCobCalculatorPlugin.stopCalculationTrigger = false; + log.debug("Aborting calculation thread (trigger): " + from); + return; + } + // check if data already exists + long bgTime = bucketed_data.get(i).date; + bgTime = IobCobCalculatorPlugin.roundUpTime(bgTime); + if (bgTime > IobCobCalculatorPlugin.roundUpTime(now())) + continue; + + AutosensData existing; + if ((existing = autosensDataTable.get(bgTime)) != null) { + previous = existing; + continue; + } + + Profile profile = MainApp.getConfigBuilder().getProfile(bgTime); + if (profile == null) { + log.debug("Aborting calculation thread (no profile): " + from); + return; // profile not set yet + } + + if (Config.logAutosensData) + log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); + + double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); + + AutosensData autosensData = new AutosensData(); + autosensData.time = bgTime; + if (previous != null) + autosensData.activeCarbsList = new ArrayList<>(previous.activeCarbsList); + else + autosensData.activeCarbsList = new ArrayList<>(); + + //console.error(bgTime , bucketed_data[i].glucose); + double bg; + double avgDelta; + double delta; + bg = bucketed_data.get(i).value; + if (bg < 39 || bucketed_data.get(i + 3).value < 39) { + log.error("! value < 39"); + continue; + } + autosensData.bg = bg; + delta = (bg - bucketed_data.get(i + 1).value); + avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; + + IobTotal iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile); + + double bgi = -iob.activity * sens * 5; + double deviation = delta - bgi; + double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000; + + double slopeFromMaxDeviation = 0; + double slopeFromMinDeviation = 999; + double maxDeviation = 0; + double minDeviation = 999; + + // https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169 + if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope + long hourago = bgTime + 10 * 1000 - 60 * 60 * 1000L; + AutosensData hourAgoData = IobCobCalculatorPlugin.getPlugin().getAutosensData(hourago); + if (hourAgoData != null) { + int initialIndex = autosensDataTable.indexOfKey(hourAgoData.time); + if (Config.logAutosensData) + log.debug(">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + "hourAgoData=" + hourAgoData.toString()); + int past = 1; + try { + for (; past < 12; past++) { + AutosensData ad = autosensDataTable.valueAt(initialIndex + past); + double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5; + if (ad.avgDeviation > maxDeviation) { + slopeFromMaxDeviation = Math.min(0, deviationSlope); + maxDeviation = ad.avgDeviation; + } + if (ad.avgDeviation < minDeviation) { + slopeFromMinDeviation = Math.max(0, deviationSlope); + minDeviation = ad.avgDeviation; + } + + //if (Config.logAutosensData) + // log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation=" + slopeFromMinDeviation); + } + } catch (Exception e) { + log.error("Unhandled exception", e); + FabricPrivacy.logException(e); + FabricPrivacy.getInstance().logCustom(new CustomEvent("CatchedError") + .putCustomAttribute("buildversion", BuildConfig.BUILDVERSION) + .putCustomAttribute("version", BuildConfig.VERSION) + .putCustomAttribute("autosensDataTable", iobCobCalculatorPlugin.getAutosensDataTable().toString()) + .putCustomAttribute("for_data", ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + "hourAgoData=" + hourAgoData.toString()) + .putCustomAttribute("past", past) + ); + } + } + } + + List recentTreatments = TreatmentsPlugin.getPlugin().getTreatments5MinBackFromHistory(bgTime); + for (int ir = 0; ir < recentTreatments.size(); ir++) { + autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; + autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); + } + + + // if we are absorbing carbs + if (previous != null && previous.cob > 0) { + // calculate sum of min carb impact from all active treatments + double totalMinCarbsImpact = 0d; +// if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) { + //when the impact depends on a max time, sum them up as smaller carb sizes make them smaller +// for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) { +// AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii); +// totalMinCarbsImpact += c.min5minCarbImpact; +// } +// } else { + //Oref sensitivity + totalMinCarbsImpact = SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact); +// } + + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption per active treatment + double ci = Math.max(deviation, totalMinCarbsImpact); + if (ci != deviation) + autosensData.failoverToMinAbsorbtionRate = true; + autosensData.absorbed = ci * profile.getIc(bgTime) / sens; + // and add that to the running total carbsAbsorbed + autosensData.cob = Math.max(previous.cob - autosensData.absorbed, 0d); + autosensData.mealCarbs = previous.mealCarbs; + autosensData.substractAbosorbedCarbs(); + autosensData.usedMinCarbsImpact = totalMinCarbsImpact; + autosensData.absorbing = previous.absorbing; + autosensData.mealStartCounter = previous.mealStartCounter; + autosensData.type = previous.type; + autosensData.uam = previous.uam; + } + + autosensData.removeOldCarbs(bgTime); + autosensData.cob += autosensData.carbsFromBolus; + autosensData.mealCarbs += autosensData.carbsFromBolus; + autosensData.deviation = deviation; + autosensData.bgi = bgi; + autosensData.delta = delta; + autosensData.avgDelta = avgDelta; + autosensData.avgDeviation = avgDeviation; + autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation; + autosensData.slopeFromMinDeviation = slopeFromMinDeviation; + + + // If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens + if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) { + if (deviation > 0) + autosensData.absorbing = true; + else + autosensData.absorbing = false; + // stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h + if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) { + autosensData.absorbing = false; + } + if (!autosensData.absorbing && autosensData.cob < 0.5) { + autosensData.mealCarbs = 0; + } + // check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag + if (!autosensData.type.equals("csf")) { +// process.stderr.write("("); + autosensData.mealStartCounter = 0; + } + autosensData.mealStartCounter++; + autosensData.type = "csf"; + } else { + // check previous "type" value, and if it was csf, set a mealAbsorption end flag + if (autosensData.type.equals("csf")) { +// process.stderr.write(")"); + } + + double currentBasal = profile.getBasal(bgTime); + // always exclude the first 45m after each carb entry + //if (iob.iob > currentBasal || uam ) { + if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) { + autosensData.mealStartCounter++; + if (deviation > 0) + autosensData.uam = true; + else + autosensData.uam = false; + if (!autosensData.type.equals("uam")) { +// process.stderr.write("u("); + } + autosensData.type = "uam"; + } else { + if (autosensData.type.equals("uam")) { +// process.stderr.write(")"); + } + autosensData.type = "non-meal"; + } + } + + // Exclude meal-related deviations (carb absorption) from autosens + if (autosensData.type.equals("non-meal")) { + if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { + autosensData.pastSensitivity = "="; + autosensData.validDeviation = true; + } else if (deviation > 0) { + autosensData.pastSensitivity = "+"; + autosensData.validDeviation = true; + } else { + autosensData.pastSensitivity = "-"; + autosensData.validDeviation = true; + } + } else if (autosensData.type.equals("uam")) { + autosensData.pastSensitivity = "u"; + } else { + autosensData.pastSensitivity = "x"; + } + //log.debug("TIME: " + new Date(bgTime).toString() + " BG: " + bg + " SENS: " + sens + " DELTA: " + delta + " AVGDELTA: " + avgDelta + " IOB: " + iob.iob + " ACTIVITY: " + iob.activity + " BGI: " + bgi + " DEVIATION: " + deviation); + + // add an extra negative deviation if a high temptarget is running and exercise mode is set + if (SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) { + TempTarget tempTarget = TreatmentsPlugin.getPlugin().getTempTargetFromHistory(bgTime); + if (tempTarget != null && tempTarget.target() >= 100) { + autosensData.extraDeviation.add(-(tempTarget.target() - 100) / 20); + } + } + + // add one neutral deviation every 2 hours to help decay over long exclusion periods + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(bgTime); + int min = calendar.get(MINUTE); + int hours = calendar.get(Calendar.HOUR_OF_DAY); + if (min >= 0 && min < 5 && hours % 2 == 0) + autosensData.extraDeviation.add(0d); + + previous = autosensData; + if (bgTime < now()) + autosensDataTable.put(bgTime, autosensData); + if (Config.logAutosensData) + log.debug("Running detectSensitivity from: " + DateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + DateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); + AutosensResult sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime); + if (Config.logAutosensData) + log.debug("Sensitivity result: " + sensitivity.toString()); + autosensData.autosensResult = sensitivity; + if (Config.logAutosensData) + log.debug(autosensData.toString()); + } + } + new Thread(() -> { + SystemClock.sleep(1000); + MainApp.bus().post(new EventAutosensCalculationFinished(cause)); + }).start(); + } finally { + mWakeLock.release(); + MainApp.bus().post(new EventIobCalculationProgress("")); + log.debug("AUTOSENSDATA thread ended: " + from); + } + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java index 9818ed17e4..d60efb5bc6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.IobCobCalculator; import android.content.Context; import android.os.PowerManager; +import android.os.SystemClock; import android.support.v4.util.LongSparseArray; import com.crashlytics.android.answers.CustomEvent; @@ -26,8 +27,8 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; import info.nightscout.androidaps.plugins.OpenAPSSMB.SMBDefaults; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; @@ -41,7 +42,7 @@ import static info.nightscout.utils.DateUtil.now; */ public class IobCobThread extends Thread { - private static Logger log = LoggerFactory.getLogger(IobCobThread.class); + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); private final Event cause; private IobCobCalculatorPlugin iobCobCalculatorPlugin; @@ -68,6 +69,7 @@ public class IobCobThread extends Thread { public final void run() { mWakeLock.acquire(); try { + log.debug("AUTOSENSDATA thread started: " + from); if (MainApp.getConfigBuilder() == null) { log.debug("Aborting calculation thread (ConfigBuilder not ready): " + from); return; // app still initializing @@ -145,6 +147,7 @@ public class IobCobThread extends Thread { log.error("! value < 39"); continue; } + autosensData.bg = bg; delta = (bg - bucketed_data.get(i + 1).value); avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; @@ -245,35 +248,41 @@ public class IobCobThread extends Thread { // calculate autosens only without COB if (autosensData.cob <= 0) { if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { - autosensData.pastSensitivity += "="; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "="; + autosensData.validDeviation = true; } else if (deviation > 0) { - autosensData.pastSensitivity += "+"; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "+"; + autosensData.validDeviation = true; } else { - autosensData.pastSensitivity += "-"; - autosensData.nonEqualDeviation = true; + autosensData.pastSensitivity = "-"; + autosensData.validDeviation = true; } - autosensData.nonCarbsDeviation = true; } else { - autosensData.pastSensitivity += "C"; + autosensData.pastSensitivity = "C"; } //log.debug("TIME: " + new Date(bgTime).toString() + " BG: " + bg + " SENS: " + sens + " DELTA: " + delta + " AVGDELTA: " + avgDelta + " IOB: " + iob.iob + " ACTIVITY: " + iob.activity + " BGI: " + bgi + " DEVIATION: " + deviation); previous = autosensData; - autosensDataTable.put(bgTime, autosensData); + if (bgTime < now()) + autosensDataTable.put(bgTime, autosensData); if (Config.logAutosensData) - log.debug("Running detectSensitivity from: " + DateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + DateUtil.dateAndTimeString(bgTime)); - autosensData.autosensRatio = iobCobCalculatorPlugin.detectSensitivity(oldestTimeWithData, bgTime).ratio; + log.debug("Running detectSensitivity from: " + DateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + DateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); + AutosensResult sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime); + if (Config.logAutosensData) + log.debug("Sensitivity result: " + sensitivity.toString()); + autosensData.autosensResult = sensitivity; if (Config.logAutosensData) log.debug(autosensData.toString()); } } - MainApp.bus().post(new EventAutosensCalculationFinished(cause)); - log.debug("Finishing calculation thread: " + from); + new Thread(() -> { + SystemClock.sleep(1000); + MainApp.bus().post(new EventAutosensCalculationFinished(cause)); + }).start(); } finally { mWakeLock.release(); MainApp.bus().post(new EventIobCalculationProgress("")); + log.debug("AUTOSENSDATA thread ended: " + from); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 87e55c7dc6..ce63ad67e5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -10,7 +10,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Date; import java.util.List; import info.nightscout.androidaps.MainApp; @@ -28,7 +27,7 @@ import info.nightscout.utils.DecimalFormatter; public class APSResult { private static Logger log = LoggerFactory.getLogger(APSResult.class); - public Date date; + public long date = 0; public String reason; public double rate; public int duration; @@ -133,8 +132,8 @@ public class APSResult { public List getPredictions() { List array = new ArrayList<>(); try { - long startTime = date.getTime(); - if (json.has("predBGs")) { + long startTime = date; + if (json != null && json.has("predBGs")) { JSONObject predBGs = json.getJSONObject("predBGs"); if (predBGs.has("IOB")) { JSONArray iob = predBGs.getJSONArray("IOB"); @@ -196,8 +195,8 @@ public class APSResult { public long getLatestPredictionsTime() { long latest = 0; try { - long startTime = date != null ? date.getTime() : 0; - if (json.has("predBGs")) { + long startTime = date; + if (json != null && json.has("predBGs")) { JSONObject predBGs = json.getJSONObject("predBGs"); if (predBGs.has("IOB")) { JSONArray iob = predBGs.getJSONArray("IOB"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java index 4ad35c0d02..7fa810f8c0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopFragment.java @@ -80,7 +80,7 @@ public class LoopFragment extends SubscriberFragment { clearGUI(); final Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(() -> lastRunView.setText(ev.text)); + activity.runOnUiThread(() -> { synchronized (LoopFragment.this) { if (lastRunView != null) lastRunView.setText(ev.text); } }); } @@ -89,26 +89,29 @@ public class LoopFragment extends SubscriberFragment { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - LoopPlugin.LastRun lastRun = LoopPlugin.lastRun; - if (lastRun != null) { - requestView.setText(lastRun.request != null ? lastRun.request.toSpanned() : ""); - constraintsProcessedView.setText(lastRun.constraintsProcessed != null ? lastRun.constraintsProcessed.toSpanned() : ""); - sourceView.setText(lastRun.source != null ? lastRun.source : ""); - lastRunView.setText(lastRun.lastAPSRun != null && lastRun.lastAPSRun.getTime() != 0 ? lastRun.lastAPSRun.toLocaleString() : ""); - lastEnactView.setText(lastRun.lastEnact != null && lastRun.lastEnact.getTime() != 0 ? lastRun.lastEnact.toLocaleString() : ""); - tbrSetByPumpView.setText(lastRun.tbrSetByPump != null ? Html.fromHtml(lastRun.tbrSetByPump.toHtml()) : ""); - smbSetByPumpView.setText(lastRun.smbSetByPump != null ? Html.fromHtml(lastRun.smbSetByPump.toHtml()) : ""); + synchronized (LoopFragment.this) { + if (!isBound()) return; + LoopPlugin.LastRun lastRun = LoopPlugin.lastRun; + if (lastRun != null) { + requestView.setText(lastRun.request != null ? lastRun.request.toSpanned() : ""); + constraintsProcessedView.setText(lastRun.constraintsProcessed != null ? lastRun.constraintsProcessed.toSpanned() : ""); + sourceView.setText(lastRun.source != null ? lastRun.source : ""); + lastRunView.setText(lastRun.lastAPSRun != null && lastRun.lastAPSRun.getTime() != 0 ? lastRun.lastAPSRun.toLocaleString() : ""); + lastEnactView.setText(lastRun.lastEnact != null && lastRun.lastEnact.getTime() != 0 ? lastRun.lastEnact.toLocaleString() : ""); + tbrSetByPumpView.setText(lastRun.tbrSetByPump != null ? Html.fromHtml(lastRun.tbrSetByPump.toHtml()) : ""); + smbSetByPumpView.setText(lastRun.smbSetByPump != null ? Html.fromHtml(lastRun.smbSetByPump.toHtml()) : ""); - String constraints = ""; - if (lastRun.constraintsProcessed != null) { - Constraint allConstraints = new Constraint<>(0d); - if (lastRun.constraintsProcessed.rateConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.rateConstraint); - if (lastRun.constraintsProcessed.smbConstraint != null) - allConstraints.copyReasons(lastRun.constraintsProcessed.smbConstraint); - constraints = allConstraints.getMostLimitedReasons(); + String constraints = ""; + if (lastRun.constraintsProcessed != null) { + Constraint allConstraints = new Constraint<>(0d); + if (lastRun.constraintsProcessed.rateConstraint != null) + allConstraints.copyReasons(lastRun.constraintsProcessed.rateConstraint); + if (lastRun.constraintsProcessed.smbConstraint != null) + allConstraints.copyReasons(lastRun.constraintsProcessed.smbConstraint); + constraints = allConstraints.getMostLimitedReasons(); + } + constraintsView.setText(constraints); } - constraintsView.setText(constraints); } }); } @@ -117,13 +120,29 @@ public class LoopFragment extends SubscriberFragment { Activity activity = getActivity(); if (activity != null) activity.runOnUiThread(() -> { - requestView.setText(""); - constraintsProcessedView.setText(""); - sourceView.setText(""); - lastRunView.setText(""); - lastEnactView.setText(""); - tbrSetByPumpView.setText(""); - smbSetByPumpView.setText(""); + synchronized (LoopFragment.this) { + if (isBound()) { + requestView.setText(""); + constraintsProcessedView.setText(""); + sourceView.setText(""); + lastRunView.setText(""); + lastEnactView.setText(""); + tbrSetByPumpView.setText(""); + smbSetByPumpView.setText(""); + } + } }); } + + boolean isBound() { + return requestView != null + && constraintsProcessedView != null + && sourceView != null + && lastRunView != null + && lastEnactView != null + && tbrSetByPumpView != null + && smbSetByPumpView != null + && constraintsView != null + && runNowButton != null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index f6ec0ff7ab..480646ccfc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -46,6 +46,7 @@ import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -94,6 +95,7 @@ public class LoopPlugin extends PluginBase { .pluginName(R.string.loop) .shortName(R.string.loop_shortname) .preferencesId(R.xml.pref_closedmode) + .description(R.string.description_loop) ); loopSuspendedTill = SP.getLong("loopSuspendedTill", 0L); isSuperBolus = SP.getBoolean("isSuperBolus", false); @@ -331,7 +333,9 @@ public class LoopPlugin extends PluginBase { Constraint closedLoopEnabled = MainApp.getConstraintChecker().isClosedLoopAllowed(); if (closedLoopEnabled.value()) { - if (result.isChangeRequested()) { + if (result.isChangeRequested() + && !ConfigBuilderPlugin.getCommandQueue().bolusInQueue() + && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BOLUS)) { final PumpEnactResult waiting = new PumpEnactResult(); waiting.queued = true; if (resultAfterConstraints.tempBasalRequested) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientPlugin.java index c43297ccf6..6949cdc8b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientPlugin.java @@ -69,6 +69,7 @@ public class NSClientPlugin extends PluginBase { .pluginName(R.string.nsclientinternal) .shortName(R.string.nsclientinternal_shortname) .preferencesId(R.xml.pref_nsclientinternal) + .description(R.string.description_ns_client) ); if (Config.NSCLIENT || Config.G5UPLOADER) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java index 04b587ca24..ba6ec2c80b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NsClientReceiverDelegate.java @@ -122,7 +122,8 @@ class NsClientReceiverDelegate { boolean newAllowedState = true; if (ev.wifiConnected) { - if (!allowedSSIDs.trim().isEmpty() && !allowedSSIDs.contains(ev.ssid)) { + if (!allowedSSIDs.trim().isEmpty() && + (!allowedSSIDs.contains(ev.getSsid()) && !allowedSSIDs.contains(ev.ssid))) { newAllowedState = false; } } else { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java index 4ee2427af8..a2b6e64aec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java @@ -26,14 +26,24 @@ public class BroadcastStatus { private static Logger log = LoggerFactory.getLogger(BroadcastStatus.class); public static void handleNewStatus(NSSettingsStatus status, Context context, boolean isDelta) { + LocalBroadcastManager.getInstance(MainApp.instance()) + .sendBroadcast(createIntent(status, isDelta)); + if(SP.getBoolean(R.string.key_nsclient_localbroadcasts, true)) { + context.sendBroadcast(createIntent(status, isDelta)); + } + } + + private static Intent createIntent(NSSettingsStatus status, boolean isDelta) { Bundle bundle = new Bundle(); + try { bundle.putString("nsclientversionname", MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionName); bundle.putInt("nsclientversioncode", MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionCode); } catch (PackageManager.NameNotFoundException e) { log.error("Unhandled exception", e); } + bundle.putString("nightscoutversionname", NSClientService.nightscoutVersionName); bundle.putInt("nightscoutversioncode", NSClientService.nightscoutVersionCode); bundle.putString("status", status.getData().toString()); @@ -41,24 +51,7 @@ public class BroadcastStatus { Intent intent = new Intent(Intents.ACTION_NEW_STATUS); intent.putExtras(bundle); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - LocalBroadcastManager.getInstance(MainApp.instance()).sendBroadcast(intent); - if(SP.getBoolean(R.string.key_nsclient_localbroadcasts, true)) { - bundle = new Bundle(); - try { - bundle.putString("nsclientversionname", MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionName); - bundle.putInt("nsclientversioncode", MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionCode); - } catch (PackageManager.NameNotFoundException e) { - log.error("Unhandled exception", e); - } - bundle.putString("nightscoutversionname", NSClientService.nightscoutVersionName); - bundle.putInt("nightscoutversioncode", NSClientService.nightscoutVersionCode); - bundle.putString("status", status.getData().toString()); - bundle.putBoolean("delta", isDelta); - intent = new Intent(Intents.ACTION_NEW_STATUS); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - context.sendBroadcast(intent); - } + return intent; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java index d1755dd4c1..29d1b33467 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSDeviceStatus.java @@ -13,6 +13,7 @@ import java.util.Iterator; import java.util.Map; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.utils.DateUtil; import info.nightscout.utils.Round; import info.nightscout.utils.SP; @@ -374,7 +375,7 @@ public class NSDeviceStatus { public String getUploaderStatus() { Iterator iter = uploaders.entrySet().iterator(); int minBattery = 100; - while(iter.hasNext()) { + while (iter.hasNext()) { Map.Entry pair = (Map.Entry) iter.next(); Uploader uploader = (Uploader) pair.getValue(); if (minBattery > uploader.battery) @@ -388,7 +389,7 @@ public class NSDeviceStatus { StringBuilder string = new StringBuilder(); Iterator iter = uploaders.entrySet().iterator(); - while(iter.hasNext()) { + while (iter.hasNext()) { Map.Entry pair = (Map.Entry) iter.next(); Uploader uploader = (Uploader) pair.getValue(); String device = (String) pair.getKey(); @@ -398,4 +399,11 @@ public class NSDeviceStatus { return Html.fromHtml(string.toString()); } + public static APSResult getAPSResult() { + APSResult result = new APSResult(); + result.json = deviceStatusOpenAPSData.suggested; + result.date = deviceStatusOpenAPSData.clockSuggested; + return result; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java index cb2b4a0091..ac7a7c2b49 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -6,19 +6,18 @@ import org.mozilla.javascript.NativeObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.utils.DateUtil; public class DetermineBasalResultAMA extends APSResult { private static Logger log = LoggerFactory.getLogger(DetermineBasalResultAMA.class); - public double eventualBG; - public double snoozeBG; + private double eventualBG; + private double snoozeBG; - public DetermineBasalResultAMA(NativeObject result, JSONObject j) { + DetermineBasalResultAMA(NativeObject result, JSONObject j) { this(); - date = new Date(); + date = DateUtil.now(); json = j; if (result.containsKey("error")) { reason = result.get("error").toString(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java index 143456f6fc..74d15cd2a4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.OpenAPSAMA; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +63,7 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { .pluginName(R.string.openapsama) .shortName(R.string.oaps_shortname) .preferencesId(R.xml.pref_openapsama) + .description(R.string.description_ama) ); } @@ -172,9 +174,14 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { startPart = new Date(); if (MainApp.getConstraintChecker().isAutosensModeEnabled().value()) { - lastAutosensResult = IobCobCalculatorPlugin.getPlugin().detectSensitivityWithLock(IobCobCalculatorPlugin.getPlugin().oldestDataAvailable(), System.currentTimeMillis()); + AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensDataSynchronized("OpenAPSPlugin"); + if (autosensData == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); + return; + } } else { lastAutosensResult = new AutosensResult(); + lastAutosensResult.sensResult = "autosens disabled"; } Profiler.log(log, "detectSensitivityandCarbAbsorption()", startPart); Profiler.log(log, "AMA data gathering", start); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java index 14dac65625..39d05f8ffb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java @@ -62,6 +62,7 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { .pluginName(R.string.openapsma) .shortName(R.string.oaps_shortname) .preferencesId(R.xml.pref_openapsma) + .description(R.string.description_ma) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index da96241e61..48a4c16ccd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -234,8 +234,8 @@ public class DetermineBasalAdapterSMBJS { 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("high_temptarget_raises_sensitivity", SMBDefaults.high_temptarget_raises_sensitivity); - mProfile.put("low_temptarget_lowers_sensitivity", SMBDefaults.low_temptarget_lowers_sensitivity); + mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); + mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); mProfile.put("sensitivity_raises_target", SMBDefaults.sensitivity_raises_target); mProfile.put("resistance_lowers_target", SMBDefaults.resistance_lowers_target); mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments); @@ -250,7 +250,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)); } mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); - mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)&& advancedFiltering); + mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_enableSMB_with_COB, false)); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_enableSMB_with_temptarget, false)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java index 59c1dd95e3..d5f108bf4f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -5,22 +5,20 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; - import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.utils.DateUtil; public class DetermineBasalResultSMB extends APSResult { private static final Logger log = LoggerFactory.getLogger(DetermineBasalResultSMB.class); - public double eventualBG; - public double snoozeBG; - public double insulinReq; - public double carbsReq; + private double eventualBG; + private double snoozeBG; + //public double insulinReq; + //public double carbsReq; - public DetermineBasalResultSMB(JSONObject result) { + DetermineBasalResultSMB(JSONObject result) { this(); - date = new Date(); + date = DateUtil.now(); json = result; try { if (result.has("error")) { @@ -31,8 +29,8 @@ public class DetermineBasalResultSMB extends APSResult { reason = result.getString("reason"); if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG"); if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); - if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); - if (result.has("carbsReq")) carbsReq = result.getDouble("carbsReq"); + //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); + //if (result.has("carbsReq")) carbsReq = result.getDouble("carbsReq"); if (result.has("rate") && result.has("duration")) { tempBasalRequested = true; @@ -64,7 +62,7 @@ public class DetermineBasalResultSMB extends APSResult { } } - public DetermineBasalResultSMB() { + private DetermineBasalResultSMB() { hasPredictions = true; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java index 724bfcf86c..20ccd46b40 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBFragment.java @@ -87,34 +87,37 @@ public class OpenAPSSMBFragment extends SubscriberFragment { activity.runOnUiThread(new Runnable() { @Override public void run() { - OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); - DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; - if (lastAPSResult != null) { - resultView.setText(JSONFormatter.format(lastAPSResult.json)); - requestView.setText(lastAPSResult.toSpanned()); - } - DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; - if (determineBasalAdapterSMBJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()).toString().trim()); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()).toString().trim()); - try { - JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); - iobDataView.setText((String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))).trim()); - } catch (JSONException e) { - log.error("Unhandled exception", e); - iobDataView.setText("JSONException see log for details"); + synchronized (OpenAPSSMBFragment.this) { + if (!isBound()) return; + OpenAPSSMBPlugin plugin = OpenAPSSMBPlugin.getPlugin(); + DetermineBasalResultSMB lastAPSResult = plugin.lastAPSResult; + if (lastAPSResult != null) { + resultView.setText(JSONFormatter.format(lastAPSResult.json)); + requestView.setText(lastAPSResult.toSpanned()); + } + DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS = plugin.lastDetermineBasalAdapterSMBJS; + if (determineBasalAdapterSMBJS != null) { + glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getGlucoseStatusParam()).toString().trim()); + currentTempView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getCurrentTempParam()).toString().trim()); + try { + JSONArray iobArray = new JSONArray(determineBasalAdapterSMBJS.getIobDataParam()); + iobDataView.setText((String.format(MainApp.gs(R.string.array_of_elements), iobArray.length()) + "\n" + JSONFormatter.format(iobArray.getString(0))).trim()); + } catch (JSONException e) { + log.error("Unhandled exception", e); + iobDataView.setText("JSONException see log for details"); + } + profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()).toString().trim()); + mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()).toString().trim()); + scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug().trim()); + if (lastAPSResult != null && lastAPSResult.inputConstraints != null) + constraintsView.setText(lastAPSResult.inputConstraints.getReasons().trim()); + } + if (plugin.lastAPSRun != null) { + lastRunView.setText(plugin.lastAPSRun.toLocaleString().trim()); + } + if (plugin.lastAutosensResult != null) { + autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json()).toString().trim()); } - profileView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getProfileParam()).toString().trim()); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterSMBJS.getMealDataParam()).toString().trim()); - scriptdebugView.setText(determineBasalAdapterSMBJS.getScriptDebug().trim()); - if (lastAPSResult != null && lastAPSResult.inputConstraints != null) - constraintsView.setText(lastAPSResult.inputConstraints.getReasons().trim()); - } - if (plugin.lastAPSRun != null) { - lastRunView.setText(plugin.lastAPSRun.toLocaleString().trim()); - } - if (plugin.lastAutosensResult != null) { - autosensDataView.setText(JSONFormatter.format(plugin.lastAutosensResult.json()).toString().trim()); } } }); @@ -126,17 +129,36 @@ public class OpenAPSSMBFragment extends SubscriberFragment { activity.runOnUiThread(new Runnable() { @Override public void run() { - resultView.setText(text); - glucoseStatusView.setText(""); - currentTempView.setText(""); - iobDataView.setText(""); - profileView.setText(""); - mealDataView.setText(""); - autosensDataView.setText(""); - scriptdebugView.setText(""); - requestView.setText(""); - lastRunView.setText(""); + synchronized (OpenAPSSMBFragment.this) { + if (isBound()) { + resultView.setText(text); + glucoseStatusView.setText(""); + currentTempView.setText(""); + iobDataView.setText(""); + profileView.setText(""); + mealDataView.setText(""); + autosensDataView.setText(""); + scriptdebugView.setText(""); + requestView.setText(""); + lastRunView.setText(""); + } + } } }); } + + private boolean isBound() { + return run != null + && lastRunView != null + && constraintsView != null + && glucoseStatusView != null + && currentTempView != null + && iobDataView != null + && profileView != null + && mealDataView != null + && autosensDataView != null + && resultView != null + && scriptdebugView != null + && requestView != null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java index 49a81ea612..570b76502f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.plugins.OpenAPSSMB; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import org.json.JSONException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +66,7 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface { .pluginName(R.string.openapssmb) .shortName(R.string.smb_shortname) .preferencesId(R.xml.pref_openapssmb) + .description(R.string.description_smb) ); } @@ -179,9 +181,15 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface { startPart = new Date(); if (MainApp.getConstraintChecker().isAutosensModeEnabled().value()) { - lastAutosensResult = IobCobCalculatorPlugin.getPlugin().detectSensitivityWithLock(IobCobCalculatorPlugin.getPlugin().oldestDataAvailable(), System.currentTimeMillis()); + AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensDataSynchronized("OpenAPSPlugin"); + if (autosensData == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.gs(R.string.openaps_noasdata))); + return; + } + lastAutosensResult = autosensData.autosensResult; } else { lastAutosensResult = new AutosensResult(); + lastAutosensResult.sensResult = "autosens disabled"; } Constraint smbAllowed = new Constraint<>(!tempBasalFallback); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/BolusProgressDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/BolusProgressDialog.java index 8ef6ec2fc6..9974471528 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/BolusProgressDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/BolusProgressDialog.java @@ -112,7 +112,7 @@ public class BolusProgressDialog extends DialogFragment implements View.OnClickL stopPressed = true; stopPressedView.setVisibility(View.VISIBLE); stopButton.setVisibility(View.INVISIBLE); - ConfigBuilderPlugin.getActivePump().stopBolusDelivering(); + ConfigBuilderPlugin.getCommandQueue().cancelAllBoluses(); break; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java index 4360e2ed34..26f46081ea 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewCarbsDialog.java @@ -21,6 +21,7 @@ import android.widget.RadioButton; import com.google.common.base.Joiner; +import info.nightscout.utils.NSUpload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +33,9 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; +import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.interfaces.Constraint; @@ -124,7 +127,6 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C startEatingSoonTTCheckbox = view.findViewById(R.id.newcarbs_eating_soon_tt); startEatingSoonTTCheckbox.setOnCheckedChangeListener(this); startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt); - startHypoTTCheckbox.setOnCheckedChangeListener(this); editTime = view.findViewById(R.id.newcarbs_time); editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, textWatcher); @@ -153,6 +155,13 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C notesLayout.setVisibility(SP.getBoolean(R.string.key_show_notes_entry_dialogs, false) ? View.VISIBLE : View.GONE); notesEdit = view.findViewById(R.id.newcarbs_notes); + BgReading bgReading = DatabaseHelper.actualBg(); + if (bgReading != null && bgReading.value < 72) { + startHypoTTCheckbox.setOnCheckedChangeListener(null); + startHypoTTCheckbox.setChecked(true); + } + startHypoTTCheckbox.setOnClickListener(this); + setCancelable(true); getDialog().setCanceledOnTouchOutside(false); return view; @@ -221,13 +230,13 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - // Logic to disable a selected radio when pressed. When a checked radio - // is pressed, no CheckChanged event is trigger, so register a Click event + // Logic to disable a selected radio when pressed: when a checked radio + // is pressed, no CheckChanged event is triggered, so register a Click event // when checking a radio. Since Click events come after CheckChanged events, - // the Click event is triggered immediately after this. Thus, set toggingTT + // the Click event is triggered immediately after this. Thus, set togglingTT // var to true, so that the first Click event fired after this is ignored. // Radios remove themselves from Click events once unchecked. - // Since radios are not in a group, manually update their state. + // Since radios are not in a group, their state is manually updated here. switch (buttonView.getId()) { case R.id.newcarbs_activity_tt: togglingTT = true; @@ -303,21 +312,21 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C if (currentProfile.getUnits().equals(Constants.MMOL)) { unitLabel = "mmol/l"; } - - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " min)"); - + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + activityTTDuration + " min)"); } if (startEatingSoonTTCheckbox.isChecked()) { if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); - } else - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); + } else { + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); + } } if (startHypoTTCheckbox.isChecked()) { if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(hypoTT) + " mmol/l (" + hypoTTDuration + " min)"); - } else - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(hypoTT) + " mg/dl (" + hypoTTDuration + " min)"); + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(hypoTT) + " mmol/l (" + hypoTTDuration + " min)"); + } else { + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(hypoTT) + " mg/dl (" + hypoTTDuration + " min)"); + } } int timeOffset = editTime.getValue().intValue(); @@ -394,6 +403,7 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C CarbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes); } else { CarbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes); + NSUpload.uploadEvent(CareportalEvent.NOTE, now() - 2000, MainApp.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java index 875d207352..2889c8266b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewInsulinDialog.java @@ -148,6 +148,11 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener setCancelable(true); getDialog().setCanceledOnTouchOutside(false); + if (savedInstanceState != null) { +// log.debug("savedInstanceState in onCreate is:" + savedInstanceState.toString()); + editInsulin.setValue(savedInstanceState.getDouble("editInsulin")); + editTime.setValue(savedInstanceState.getDouble("editTime")); + } return view; } @@ -156,6 +161,17 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener return value > 0 ? "+" + formatted : formatted; } + @Override + public void onSaveInstanceState(Bundle insulinDialogState) { + insulinDialogState.putBoolean("startEatingSoonTTCheckbox", startEatingSoonTTCheckbox.isChecked()); + insulinDialogState.putBoolean("recordOnlyCheckbox", recordOnlyCheckbox.isChecked()); + insulinDialogState.putDouble("editTime", editTime.getValue()); + insulinDialogState.putDouble("editInsulin", editInsulin.getValue()); + insulinDialogState.putString("notesEdit",notesEdit.getText().toString()); + log.debug("Instance state saved:"+insulinDialogState.toString()); + super.onSaveInstanceState(insulinDialogState); + } + @Override public synchronized void onClick(View view) { switch (view.getId()) { @@ -217,9 +233,9 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener if (startEatingSoonTTCheckbox.isChecked()) { if (currentProfile.getUnits().equals(Constants.MMOL)) { - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to1Decimal(eatingSoonTT) + " mmol/l (" + eatingSoonTTDuration + " min)"); } else - actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); + actions.add(MainApp.gs(R.string.temptargetshort) + ": " + "" + DecimalFormatter.to0Decimal(eatingSoonTT) + " mg/dl (" + eatingSoonTTDuration + " min)"); } int timeOffset = editTime.getValue().intValue(); @@ -270,7 +286,7 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener detailedBolusInfo.notes = notes; if (recordOnlyCheckbox.isChecked()) { detailedBolusInfo.date = time; - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); } else { detailedBolusInfo.date = now(); ConfigBuilderPlugin.getCommandQueue().bolus(detailedBolusInfo, new Callback() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java index a315310dc8..5fb698d1c5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/NewTreatmentDialog.java @@ -25,7 +25,6 @@ import org.slf4j.LoggerFactory; import java.text.DecimalFormat; import java.util.Objects; -import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.DetailedBolusInfo; @@ -187,7 +186,7 @@ public class NewTreatmentDialog extends DialogFragment implements OnClickListene } }); } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); } FabricPrivacy.getInstance().logCustom(new CustomEvent("Bolus")); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java index c38f41b825..032104e103 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java @@ -315,6 +315,7 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com confirmMessage += "
" + MainApp.gs(R.string.carbs) + ": " + "" + carbsAfterConstraints + "g" + ""; if (insulinAfterConstraints - calculatedTotalInsulin != 0 || !carbsAfterConstraints.equals(calculatedCarbs)) { + okClicked = false; AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.gs(R.string.treatmentdeliveryerror)); builder.setMessage(MainApp.gs(R.string.constraints_violation) + "\n" + MainApp.gs(R.string.changeyourinput)); @@ -388,7 +389,7 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com } }); } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); } FabricPrivacy.getInstance().logCustom(new CustomEvent("Wizard")); } @@ -408,9 +409,9 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com private void initDialog() { Profile profile = MainApp.getConfigBuilder().getProfile(); - ProfileStore profileStore = MainApp.getConfigBuilder().getActiveProfileInterface().getProfile(); + ProfileStore profileStore = MainApp.getConfigBuilder().getActiveProfileInterface() != null ? MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() : null; - if (profile == null) { + if (profile == null || profileStore == null) { ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.noprofile)); dismiss(); return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index f43a78d64f..2b4ab247b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -24,14 +24,11 @@ import android.text.style.ForegroundColorSpan; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.ContextMenu; -import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; @@ -88,10 +85,12 @@ import info.nightscout.androidaps.plugins.Careportal.Dialogs.NewNSTreatmentDialo import info.nightscout.androidaps.plugins.Careportal.OptionsToShow; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.CobInfo; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventIobCalculationProgress; +import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; @@ -102,7 +101,6 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.NewInsulinDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; import info.nightscout.androidaps.plugins.Overview.activities.QuickWizardListActivity; -import info.nightscout.androidaps.plugins.Overview.events.EventSetWakeLock; import info.nightscout.androidaps.plugins.Overview.graphData.GraphData; import info.nightscout.androidaps.plugins.Overview.notifications.NotificationRecyclerViewAdapter; import info.nightscout.androidaps.plugins.Overview.notifications.NotificationStore; @@ -114,22 +112,29 @@ import info.nightscout.androidaps.queue.Callback; import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; +import info.nightscout.utils.DefaultValueHelper; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.OKDialog; import info.nightscout.utils.Profiler; import info.nightscout.utils.SP; import info.nightscout.utils.SingleClickButton; +import info.nightscout.utils.T; import info.nightscout.utils.ToastUtils; +import static info.nightscout.utils.DateUtil.now; + public class OverviewFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { private static Logger log = LoggerFactory.getLogger(OverviewFragment.class); TextView timeView; TextView bgView; TextView arrowView; + TextView sensitivityView; TextView timeAgoView; + TextView timeAgoShortView; TextView deltaView; + TextView deltaShortView; TextView avgdeltaView; TextView baseBasalView; TextView extendedBolusView; @@ -168,8 +173,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, SingleClickButton cgmButton; SingleClickButton quickWizardButton; - CheckBox lockScreen; - boolean smallWidth; boolean smallHeight; @@ -226,8 +229,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (smallWidth) { arrowView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 35); } + sensitivityView = (TextView) view.findViewById(R.id.overview_sensitivity); timeAgoView = (TextView) view.findViewById(R.id.overview_timeago); + timeAgoShortView = (TextView) view.findViewById(R.id.overview_timeagoshort); deltaView = (TextView) view.findViewById(R.id.overview_delta); + deltaShortView = (TextView) view.findViewById(R.id.overview_deltashort); avgdeltaView = (TextView) view.findViewById(R.id.overview_avgdelta); baseBasalView = (TextView) view.findViewById(R.id.overview_basebasal); extendedBolusView = (TextView) view.findViewById(R.id.overview_extendedbolus); @@ -281,7 +287,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, acceptTempLayout = (LinearLayout) view.findViewById(R.id.overview_accepttemplayout); notificationsView = (RecyclerView) view.findViewById(R.id.overview_notifications); - notificationsView.setHasFixedSize(true); + notificationsView.setHasFixedSize(false); llm = new LinearLayoutManager(view.getContext()); notificationsView.setLayoutManager(llm); @@ -307,7 +313,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, iobGraph.getGridLabelRenderer().setHorizontalLabelsVisible(false); bgGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); iobGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); - iobGraph.getGridLabelRenderer().setNumVerticalLabels(5); + iobGraph.getGridLabelRenderer().setNumVerticalLabels(3); rangeToDisplay = SP.getInt(R.string.key_rangetodisplay, 6); @@ -324,18 +330,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, setupChartMenu(view); - lockScreen = (CheckBox) view.findViewById(R.id.overview_lockscreen); - if (lockScreen != null) { - lockScreen.setChecked(SP.getBoolean("lockscreen", false)); - lockScreen.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - SP.putBoolean("lockscreen", isChecked); - MainApp.bus().post(new EventSetWakeLock(isChecked)); - } - }); - } - return view; } catch (Exception e) { FabricPrivacy.logException(e); @@ -351,7 +345,13 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, @Override public void onClick(View v) { final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; - final boolean predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + boolean predictionsAvailable; + if (Config.APS) + predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + else if (Config.NSCLIENT) + predictionsAvailable = true; + else + predictionsAvailable = false; MenuItem item; CharSequence title; @@ -485,9 +485,18 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else if (v == activeProfileView) { menu.setHeaderTitle(MainApp.gs(R.string.profile)); menu.add(MainApp.gs(R.string.danar_viewprofile)); - if (MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() != null) { + if (MainApp.getConfigBuilder().getActiveProfileInterface() != null && MainApp.getConfigBuilder().getActiveProfileInterface().getProfile() != null) { menu.add(MainApp.gs(R.string.careportal_profileswitch)); } + } else if (v == tempTargetView) { + menu.setHeaderTitle(MainApp.gs(R.string.careportal_temporarytarget)); + menu.add(MainApp.gs(R.string.custom)); + menu.add(MainApp.gs(R.string.eatingsoon)); + menu.add(MainApp.gs(R.string.activity)); + menu.add(MainApp.gs(R.string.hypo)); + if (TreatmentsPlugin.getPlugin().getTempTargetFromHistory() != null) { + menu.add(MainApp.gs(R.string.cancel)); + } } } @@ -578,6 +587,53 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, ProfileViewerDialog pvd = ProfileViewerDialog.newInstance(System.currentTimeMillis()); FragmentManager manager = getFragmentManager(); pvd.show(manager, "ProfileViewDialog"); + } else if (item.getTitle().equals(MainApp.gs(R.string.eatingsoon))) { + DefaultValueHelper defHelper = new DefaultValueHelper(); + double target = defHelper.determineEatingSoonTT(profile.getUnits()); + TempTarget tempTarget = new TempTarget() + .date(System.currentTimeMillis()) + .duration(defHelper.determineEatingSoonTTDuration()) + .reason(MainApp.gs(R.string.eatingsoon)) + .source(Source.USER) + .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()); + TempTarget tempTarget = new TempTarget() + .date(now()) + .duration(defHelper.determineActivityTTDuration()) + .reason(MainApp.gs(R.string.activity)) + .source(Source.USER) + .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()); + TempTarget tempTarget = new TempTarget() + .date(now()) + .duration(defHelper.determineHypoTTDuration()) + .reason(MainApp.gs(R.string.activity)) + .source(Source.USER) + .low(target) + .high(target); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); + } else if (item.getTitle().equals(MainApp.gs(R.string.custom))) { + NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog(); + final OptionsToShow temptarget = CareportalFragment.TEMPTARGET; + temptarget.executeTempTarget = true; + newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget); + newTTDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); + } else if (item.getTitle().equals(MainApp.gs(R.string.cancel))) { + TempTarget tempTarget = new TempTarget() + .source(Source.USER) + .date(now()) + .duration(0) + .low(0) + .high(0); + TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget); } return super.onContextItemSelected(item); @@ -590,6 +646,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, String units = MainApp.getConfigBuilder().getProfileUnits(); FragmentManager manager = getFragmentManager(); + // try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days + // https://stackoverflow.com/questions/14860239/checking-if-state-is-saved-before-committing-a-fragmenttransaction + if (manager.isStateSaved()) + return; switch (v.getId()) { case R.id.overview_accepttempbutton: onClickAcceptTemp(); @@ -672,12 +732,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, private void onClickAcceptTemp() { Profile profile = MainApp.getConfigBuilder().getProfile(); + Context context = getContext(); + + if (context == null) return; if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && profile != null) { LoopPlugin.getPlugin().invoke("Accept temp button", false); final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; if (finalLastRun != null && finalLastRun.lastAPSRun != null && finalLastRun.constraintsProcessed.isChangeRequested()) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(MainApp.gs(R.string.setbasalquestion) + "\n" + finalLastRun.constraintsProcessed); builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { @@ -777,7 +840,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (wizard.superBolus) { final LoopPlugin loopPlugin = LoopPlugin.getPlugin(); if (loopPlugin.isEnabled(PluginType.LOOP)) { - loopPlugin.superBolusTo(System.currentTimeMillis() + 2 * 60L * 60 * 1000); + loopPlugin.superBolusTo(System.currentTimeMillis() + T.hours(2).msecs()); MainApp.bus().post(new EventRefreshOverview("WizardDialog")); } ConfigBuilderPlugin.getCommandQueue().tempBasalPercent(0, 120, true, profile, new Callback() { @@ -816,7 +879,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } }); } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); } FabricPrivacy.getInstance().logCustom(new CustomEvent("QuickWizard")); } @@ -836,6 +899,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, sLoopHandler.removeCallbacksAndMessages(null); unregisterForContextMenu(apsModeView); unregisterForContextMenu(activeProfileView); + unregisterForContextMenu(tempTargetView); } @Override @@ -849,6 +913,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); registerForContextMenu(apsModeView); registerForContextMenu(activeProfileView); + registerForContextMenu(tempTargetView); updateGUI("onResume"); } @@ -981,16 +1046,19 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (timeView != null) { //must not exists timeView.setText(DateUtil.timeString(new Date())); } + + updateNotifications(); + + pumpStatusLayout.setVisibility(View.GONE); + loopStatusLayout.setVisibility(View.GONE); + if (!MainApp.getConfigBuilder().isProfileValid("Overview")) { pumpStatusView.setText(R.string.noprofileset); pumpStatusLayout.setVisibility(View.VISIBLE); - loopStatusLayout.setVisibility(View.GONE); return; } - pumpStatusLayout.setVisibility(View.GONE); loopStatusLayout.setVisibility(View.VISIBLE); - updateNotifications(); CareportalFragment.updateAge(getActivity(), sage, iage, cage, pbage); BgReading actualBG = DatabaseHelper.actualBg(); BgReading lastBG = DatabaseHelper.lastBg(); @@ -1017,12 +1085,18 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, arrowView.setTextColor(color); GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); if (glucoseStatus != null) { - deltaView.setText("Δ " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units); + if (deltaView != null) + deltaView.setText("Δ " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units); + if (deltaShortView != null) + deltaShortView.setText(Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); if (avgdeltaView != null) avgdeltaView.setText("øΔ15m: " + Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units) + " øΔ40m: " + Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)); } else { - deltaView.setText("Δ " + MainApp.gs(R.string.notavailable)); + if (deltaView != null) + deltaView.setText("Δ " + MainApp.gs(R.string.notavailable)); + if (deltaShortView != null) + deltaShortView.setText("---"); if (avgdeltaView != null) avgdeltaView.setText(""); } @@ -1126,15 +1200,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else { basalText = DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h"; } - baseBasalView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String fullText = MainApp.gs(R.string.pump_basebasalrate_label) + ": " + DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h\n"; - if (activeTemp != null) { - fullText += MainApp.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull(); - } - OKDialog.show(getActivity(), MainApp.gs(R.string.basal), fullText, null); + baseBasalView.setOnClickListener(v -> { + String fullText = MainApp.gs(R.string.pump_basebasalrate_label) + ": " + DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h\n"; + if (activeTemp != null) { + fullText += MainApp.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull(); } + OKDialog.show(getActivity(), MainApp.gs(R.string.basal), fullText, null); }); } else { @@ -1163,22 +1234,17 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) { extendedBolusText = DecimalFormatter.to2Decimal(extendedBolus.absoluteRate()) + "U/h"; } - extendedBolusView.setText(extendedBolusText); - extendedBolusView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - OKDialog.show(getActivity(), MainApp.gs(R.string.extendedbolus), extendedBolus.toString(), null); - } - }); - } else { if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) { extendedBolusText = extendedBolus.toString(); } - extendedBolusView.setText(extendedBolusText); + } + extendedBolusView.setText(extendedBolusText); + if (Config.NSCLIENT || Config.G5UPLOADER) { + extendedBolusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.extendedbolus), extendedBolus.toString(), null)); } if (extendedBolusText.equals("")) - extendedBolusView.setVisibility(View.GONE); + extendedBolusView.setVisibility(Config.NSCLIENT || Config.G5UPLOADER ? View.INVISIBLE : View.GONE); else extendedBolusView.setVisibility(View.VISIBLE); } @@ -1186,17 +1252,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, activeProfileView.setText(MainApp.getConfigBuilder().getProfileName()); activeProfileView.setBackgroundColor(Color.GRAY); - tempTargetView.setOnLongClickListener(view -> { - view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - NewNSTreatmentDialog newTTDialog = new NewNSTreatmentDialog(); - final OptionsToShow temptarget = CareportalFragment.TEMPTARGET; - temptarget.executeTempTarget = true; - newTTDialog.setOptions(temptarget, R.string.careportal_temporarytarget); - newTTDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - return true; - }); - tempTargetView.setLongClickable(true); - // QuickWizard button QuickWizardEntry quickWizardEntry = OverviewPlugin.getPlugin().quickWizard.getActive(); if (quickWizardEntry != null && lastBG != null && pump.isInitialized() && !pump.isSuspended()) { @@ -1256,7 +1311,10 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, flag &= ~Paint.STRIKE_THRU_TEXT_FLAG; bgView.setPaintFlags(flag); - timeAgoView.setText(DateUtil.minAgo(lastBG.date)); + if (timeAgoView != null) + timeAgoView.setText(DateUtil.minAgo(lastBG.date)); + if (timeAgoShortView != null) + timeAgoShortView.setText("(" + DateUtil.minAgoShort(lastBG.date) + ")"); // iob TreatmentsPlugin.getPlugin().updateTotalIOBTreatments(); @@ -1297,7 +1355,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, cobView.setText(cobText); } - final boolean predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + boolean predictionsAvailable; + if (Config.APS) + predictionsAvailable = finalLastRun != null && finalLastRun.request.hasPredictions; + else if (Config.NSCLIENT) + predictionsAvailable = true; + else + predictionsAvailable = false; + final boolean finalPredictionsAvailable = predictionsAvailable; + // pump status from ns if (pumpDeviceStatusView != null) { pumpDeviceStatusView.setText(NSDeviceStatus.getInstance().getPumpStatus()); @@ -1316,6 +1382,15 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, uploaderDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), MainApp.gs(R.string.uploader), NSDeviceStatus.getInstance().getExtendedUploaderStatus(), null)); } + // Sensitivity + if (sensitivityView != null) { + AutosensResult lastAutosensResult = IobCobCalculatorPlugin.getPlugin().detectSensitivityWithLock(IobCobCalculatorPlugin.getPlugin().oldestDataAvailable(), System.currentTimeMillis()); + if (lastAutosensResult != null) + sensitivityView.setText(String.format("%.0f%%", lastAutosensResult.ratio * 100)); + else + sensitivityView.setText(""); + } + // ****** GRAPH ******* new Thread(() -> { @@ -1331,18 +1406,25 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, final long toTime; final long fromTime; final long endTime; - if (predictionsAvailable && SP.getBoolean("showprediction", false)) { - int predHours = (int) (Math.ceil(finalLastRun.constraintsProcessed.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); + + APSResult apsResult = null; + + if (finalPredictionsAvailable && SP.getBoolean("showprediction", false)) { + if (Config.APS) + apsResult = finalLastRun.constraintsProcessed; + else + apsResult = NSDeviceStatus.getAPSResult(); + int predHours = (int) (Math.ceil(apsResult.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); predHours = Math.min(2, predHours); predHours = Math.max(0, predHours); hoursToFetch = rangeToDisplay - predHours; toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding - Graphview specific - fromTime = toTime - hoursToFetch * 60 * 60 * 1000L; - endTime = toTime + predHours * 60 * 60 * 1000L; + fromTime = toTime - T.hours(hoursToFetch).msecs(); + endTime = toTime + T.hours(predHours).msecs(); } else { hoursToFetch = rangeToDisplay; toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding - Graphview specific - fromTime = toTime - hoursToFetch * 60 * 60 * 1000L; + fromTime = toTime - T.hours(hoursToFetch).msecs(); endTime = toTime; } @@ -1358,9 +1440,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, graphData.addInRangeArea(fromTime, endTime, lowLine, highLine); // **** BG **** - if (predictionsAvailable && SP.getBoolean("showprediction", false)) + if (finalPredictionsAvailable && SP.getBoolean("showprediction", false)) graphData.addBgReadings(fromTime, toTime, lowLine, highLine, - finalLastRun.constraintsProcessed.getPredictions()); + apsResult.getPredictions()); else graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null); @@ -1412,7 +1494,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, secondGraphData.addDeviations(fromTime, now, useDevForScale, 1d); if (SP.getBoolean("showratios", false)) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1d); - if (SP.getBoolean("showdevslope", false)) + if (SP.getBoolean("showdevslope", false) && MainApp.devBranch) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1d); // **** NOW line **** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewPlugin.java index 67e494a76d..3b787fd768 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewPlugin.java @@ -50,6 +50,7 @@ public class OverviewPlugin extends PluginBase { .pluginName(R.string.overview) .shortName(R.string.overview_shortname) .preferencesId(R.xml.pref_overview) + .description(R.string.description_overview) ); String storedData = SP.getString("QuickWizard", "[]"); try { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java deleted file mode 100644 index 49ccf9fbfb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/events/EventSetWakeLock.java +++ /dev/null @@ -1,15 +0,0 @@ -package info.nightscout.androidaps.plugins.Overview.events; - -import info.nightscout.androidaps.events.Event; - -/** - * Created by mike on 02.07.2017. - */ - -public class EventSetWakeLock extends Event { - public boolean lock = false; - - public EventSetWakeLock(boolean val) { - lock = val; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java index 1c8b27c113..20feb76124 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphData/GraphData.java @@ -49,7 +49,8 @@ import info.nightscout.utils.Round; public class GraphData { private GraphView graph; - public double maxY = 0; + public double maxY = Double.MIN_VALUE; + public double minY = Double.MAX_VALUE; private List bgReadingsArray; private String units; private List series = new ArrayList<>(); @@ -63,7 +64,7 @@ public class GraphData { } public void addBgReadings(long fromTime, long toTime, double lowLine, double highLine, List predictions) { - double maxBgValue = 0d; + double maxBgValue = Double.MIN_VALUE; bgReadingsArray = MainApp.getDbHelper().getBgreadingsDataFromTime(fromTime, true); List bgListArray = new ArrayList<>(); @@ -93,10 +94,8 @@ public class GraphData { maxY = maxBgValue; + minY = 0; // set manual y bounds to have nice steps - graph.getViewport().setMaxY(maxY); - graph.getViewport().setMinY(0); - graph.getViewport().setYAxisBoundsManual(true); graph.getGridLabelRenderer().setNumVerticalLabels(numOfVertLines); addSeries(new PointsWithLabelGraphSeries<>(bg)); @@ -136,7 +135,9 @@ public class GraphData { double lastBaseBasal = 0; double lastTempBasal = 0; for (long time = fromTime; time < toTime; time += 60 * 1000L) { - BasalData basalData = IobCobCalculatorPlugin.getPlugin().getBasalData(time); + Profile profile = MainApp.getConfigBuilder().getProfile(time); + if (profile == null) continue; + BasalData basalData = IobCobCalculatorPlugin.getPlugin().getBasalData(profile, time); double baseBasalValue = basalData.basal; double absoluteLineValue = baseBasalValue; double tempBasalValue = 0; @@ -333,7 +334,7 @@ public class GraphData { public void addIob(long fromTime, long toTime, boolean useForScale, double scale) { FixedLineGraphSeries iobSeries; List iobArray = new ArrayList<>(); - Double maxIobValueFound = 0d; + Double maxIobValueFound = Double.MIN_VALUE; double lastIob = 0; Scale iobScale = new Scale(); @@ -359,8 +360,10 @@ public class GraphData { iobSeries.setColor(MainApp.gc(R.color.iob)); iobSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxIobValueFound; + minY = -maxIobValueFound; + } iobScale.setMultiplier(maxY * scale / maxIobValueFound); @@ -404,8 +407,10 @@ public class GraphData { cobSeries.setColor(MainApp.gc(R.color.cob)); cobSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxCobValueFound; + minY = 0; + } cobScale.setMultiplier(maxY * scale / maxCobValueFound); @@ -436,9 +441,18 @@ public class GraphData { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getAutosensData(time); if (autosensData != null) { int color = MainApp.gc(R.color.deviationblack); // "=" - if (autosensData.pastSensitivity.equals("C")) color = MainApp.gc(R.color.deviationgrey); - if (autosensData.pastSensitivity.equals("+")) color = MainApp.gc(R.color.deviationgreen); - if (autosensData.pastSensitivity.equals("-")) color = MainApp.gc(R.color.deviationred); + if (autosensData.type.equals("") || autosensData.type.equals("non-meal")) { + if (autosensData.pastSensitivity.equals("C")) + color = MainApp.gc(R.color.deviationgrey); + if (autosensData.pastSensitivity.equals("+")) + color = MainApp.gc(R.color.deviationgreen); + if (autosensData.pastSensitivity.equals("-")) + color = MainApp.gc(R.color.deviationred); + } else if (autosensData.type.equals("uam")) { + color = MainApp.gc(R.color.uam); + } else if (autosensData.type.equals("csf")) { + color = MainApp.gc(R.color.deviationgrey); + } devArray.add(new DeviationDataPoint(time, autosensData.deviation, color, devScale)); maxDevValueFound = Math.max(maxDevValueFound, Math.abs(autosensData.deviation)); } @@ -455,8 +469,10 @@ public class GraphData { } }); - if (useForScale) + if (useForScale) { maxY = maxDevValueFound; + minY = -maxY; + } devScale.setMultiplier(maxY * scale / maxDevValueFound); @@ -467,14 +483,16 @@ public class GraphData { public void addRatio(long fromTime, long toTime, boolean useForScale, double scale) { LineGraphSeries ratioSeries; List ratioArray = new ArrayList<>(); - Double maxRatioValueFound = 0d; - Scale ratioScale = new Scale(-1d); + Double maxRatioValueFound = Double.MIN_VALUE; + Double minRatioValueFound = Double.MAX_VALUE; + Scale ratioScale = new Scale(); for (long time = fromTime; time <= toTime; time += 5 * 60 * 1000L) { AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getAutosensData(time); if (autosensData != null) { - ratioArray.add(new ScaledDataPoint(time, autosensData.autosensRatio, ratioScale)); - maxRatioValueFound = Math.max(maxRatioValueFound, Math.abs(autosensData.autosensRatio)); + ratioArray.add(new ScaledDataPoint(time, autosensData.autosensResult.ratio - 1, ratioScale)); + maxRatioValueFound = Math.max(maxRatioValueFound, autosensData.autosensResult.ratio - 1); + minRatioValueFound = Math.min(minRatioValueFound, autosensData.autosensResult.ratio - 1); } } @@ -485,8 +503,10 @@ public class GraphData { ratioSeries.setColor(MainApp.gc(R.color.ratio)); ratioSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = maxRatioValueFound; + minY = minRatioValueFound; + } ratioScale.setMultiplier(maxY * scale / maxRatioValueFound); @@ -527,8 +547,10 @@ public class GraphData { dsMinSeries.setColor(MainApp.gc(R.color.devslopeneg)); dsMinSeries.setThickness(3); - if (useForScale) + if (useForScale) { maxY = Math.max(maxFromMaxValueFound, maxFromMinValueFound); + minY = -maxY; + } dsMaxScale.setMultiplier(maxY * scale / maxFromMaxValueFound); dsMinScale.setMultiplier(maxY * scale / maxFromMinValueFound); @@ -582,6 +604,10 @@ public class GraphData { } } + graph.getViewport().setMaxY(Round.ceilTo(maxY, 1d)); + graph.getViewport().setMinY(Round.floorTo(minY, 1d)); + graph.getViewport().setYAxisBoundsManual(true); + // draw it graph.onDataChanged(false, false); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java index 5f39cedafe..4a87a8508e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/graphExtensions/PointsWithLabelGraphSeries.java @@ -312,20 +312,20 @@ public class PointsWithLabelGraphSeries e mPaint.setStrokeWidth(5); canvas.drawRect(px - 3, bounds.top + py - 3, xpluslength + 3, bounds.bottom + py + 3, mPaint); } - } else if (value.getShape() == Shape.OPENAPSOFFLINE) { + } else if (value.getShape() == Shape.OPENAPSOFFLINE && value.getDuration() != 0) { mPaint.setStrokeWidth(0); if (value.getLabel() != null) { - mPaint.setStrokeWidth(0); - mPaint.setTextSize(scaledTextSize); - mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + //mPaint.setStrokeWidth(0); + //mPaint.setTextSize(scaledTextSize); + //mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); Rect bounds = new Rect(); - mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds); - mPaint.setStyle(Paint.Style.STROKE); + //mPaint.getTextBounds(value.getLabel(), 0, value.getLabel().length(), bounds); + mPaint.setStyle(Paint.Style.FILL_AND_STROKE); float px = endX; float py = graphTop + 50; - canvas.drawText(value.getLabel(), px, py, mPaint); + //canvas.drawText(value.getLabel(), px, py, mPaint); mPaint.setStrokeWidth(5); - canvas.drawRect(px - 3, bounds.top + py - 3, xpluslength + 3, bounds.bottom + py + 3, mPaint); + canvas.drawRect(px - 3, graphTop, xpluslength + 3, graphTop + graphHeight, mPaint); } } else if (value.getShape() == Shape.GENERALWITHDURATION) { mPaint.setStrokeWidth(0); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/Notification.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/Notification.java index 66725c23b8..9952865761 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/Notification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/Notification.java @@ -42,6 +42,8 @@ public class Notification { public static final int APPROACHING_DAILY_LIMIT = 11; public static final int NSCLIENT_NO_WRITE_PERMISSION = 12; public static final int MISSING_SMS_PERMISSION = 13; + public static final int PUMPERROR = 14; + public static final int WRONGSERIALNUMBER = 15; public static final int NSANNOUNCEMENT = 18; public static final int NSALARM = 19; @@ -59,7 +61,13 @@ public class Notification { public static final int ZERO_VALUE_IN_PROFILE = 31; public static final int PROFILE_SWITCH_MISSING = 32; public static final int NOT_ENG_MODE_OR_RELEASE = 33; - public static final int MEDTRONIC_PUMP_ALARM = 34; + public static final int WRONG_PUMP_PASSWORD = 34; + public static final int PERMISSION_STORAGE = 35; + public static final int PERMISSION_LOCATION = 36; + public static final int PERMISSION_BATTERY = 37; + public static final int PERMISSION_SMS = 38; + public static final int MAXIMUM_BASAL_VALUE_REPLACED = 39; + public static final int MEDTRONIC_PUMP_ALARM = 40; public int id; public Date date; @@ -69,7 +77,6 @@ public class Notification { public NSAlarm nsAlarm = null; public Integer soundId = null; - public Notification() { } @@ -183,10 +190,10 @@ public class Notification { return false; } - public static boolean isAlarmForStaleData() { + public static boolean isAlarmForStaleData(){ long snoozedTo = SP.getLong("snoozedTo", 0L); - if (snoozedTo != 0L) { - if (System.currentTimeMillis() < SP.getLong("snoozedTo", 0L)) { + if(snoozedTo != 0L){ + if(System.currentTimeMillis() < SP.getLong("snoozedTo", 0L)) { //log.debug("Alarm is snoozed for next "+(SP.getLong("snoozedTo", 0L)-System.currentTimeMillis())/1000+" seconds"); return false; } @@ -202,12 +209,12 @@ public class Notification { //log.debug("bgReadingAgoMin value is:"+bgReadingAgoMin); //log.debug("Stale alarm snoozed to: "+(System.currentTimeMillis() - snoozedTo)/60000L); Double threshold = NSSettingsStatus.getInstance().getThreshold("alarmTimeagoWarnMins"); - //log.debug("OpenAPS Alerts enabled: "+openAPSEnabledAlerts); - // if no thresshold from Ns get it loccally - if (threshold == null) threshold = SP.getDouble(R.string.key_nsalarm_staledatavalue, 15D); - // No threshold of OpenAPS Alarm so using the one for BG - // Added OpenAPSEnabledAlerts to alarm check - if ((bgReadingAgoMin > threshold && SP.getBoolean(R.string.key_nsalarm_staledata, false)) || (bgReadingAgoMin > threshold && openAPSEnabledAlerts)) { + //log.debug("OpenAPS Alerts enabled: "+openAPSEnabledAlerts); + // if no thresshold from Ns get it loccally + if(threshold == null) threshold = SP.getDouble(R.string.key_nsalarm_staledatavalue,15D); + // No threshold of OpenAPS Alarm so using the one for BG + // Added OpenAPSEnabledAlerts to alarm check + if((bgReadingAgoMin > threshold && SP.getBoolean(R.string.key_nsalarm_staledata, false))||(bgReadingAgoMin > threshold && openAPSEnabledAlerts)){ return true; } //snoozing for threshold diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/NotificationRecyclerViewAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/NotificationRecyclerViewAdapter.java index 1b1487bbaa..895b99a43e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/NotificationRecyclerViewAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/notifications/NotificationRecyclerViewAdapter.java @@ -42,9 +42,12 @@ public class NotificationRecyclerViewAdapter extends RecyclerView.Adapter= Build.VERSION_CODES.O) + .description(R.string.description_persistent_notification) ); this.ctx = ctx; } @@ -65,7 +75,7 @@ public class PersistentNotificationPlugin extends PluginBase { protected void onStart() { MainApp.bus().register(this); createNotificationChannel(); - updateNotification(); + triggerNotificationUpdate(); super.onStart(); } @@ -84,20 +94,22 @@ public class PersistentNotificationPlugin extends PluginBase { @Override protected void onStop() { MainApp.bus().unregister(this); - NotificationManager mNotificationManager = - (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancel(ONGOING_NOTIFICATION_ID); + MainApp.instance().stopService(new Intent(MainApp.instance(), DummyService.class)); } - private void updateNotification() { + private void triggerNotificationUpdate() { + MainApp.instance().startService(new Intent(MainApp.instance(), DummyService.class)); + } + + Notification updateNotification() { if (!isEnabled(PluginType.GENERAL)) { - return; + return null; } String line1 = ""; if (MainApp.getConfigBuilder().getActiveProfileInterface() == null || !MainApp.getConfigBuilder().isProfileValid("Notificiation")) - return; + return null; String units = MainApp.getConfigBuilder().getProfileUnits(); @@ -142,9 +154,15 @@ public class PersistentNotificationPlugin extends PluginBase { builder.setOngoing(true); builder.setOnlyAlertOnce(true); builder.setCategory(NotificationCompat.CATEGORY_STATUS); - builder.setSmallIcon(R.drawable.ic_notification); - Bitmap largeIcon = BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.blueowl); - builder.setLargeIcon(largeIcon); + if (Config.NSCLIENT || Config.G5UPLOADER){ + builder.setSmallIcon(R.drawable.nsclient_smallicon); + Bitmap largeIcon = BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.yellowowl); + builder.setLargeIcon(largeIcon); + } else { + builder.setSmallIcon(R.drawable.ic_notification); + Bitmap largeIcon = BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.blueowl); + builder.setLargeIcon(largeIcon); + } builder.setContentTitle(line1); builder.setContentText(line2); builder.setSubText(line3); @@ -165,7 +183,7 @@ public class PersistentNotificationPlugin extends PluginBase { android.app.Notification notification = builder.build(); mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification); - + return notification; } private String deltastring(double deltaMGDL, double deltaMMOL, String units) { @@ -187,42 +205,42 @@ public class PersistentNotificationPlugin extends PluginBase { @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventExtendedBolusChange ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventNewBG ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventInitializationChanged ev) { - updateNotification(); + triggerNotificationUpdate(); } @Subscribe public void onStatusEvent(final EventRefreshOverview ev) { - updateNotification(); + triggerNotificationUpdate(); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java index 6c22a2e9ce..18060ba1ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfileFragment.java @@ -4,7 +4,6 @@ package info.nightscout.androidaps.plugins.ProfileLocal; import android.app.Activity; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v4.app.FragmentTransaction; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; @@ -23,7 +22,6 @@ import java.text.DecimalFormat; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -109,41 +107,32 @@ public class LocalProfileFragment extends SubscriberFragment { mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - mgdlView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - LocalProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); - LocalProfilePlugin.getPlugin().mmol = !LocalProfilePlugin.getPlugin().mgdl; - mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - doEdit(); - } + mgdlView.setOnClickListener(v -> { + LocalProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); + LocalProfilePlugin.getPlugin().mmol = !LocalProfilePlugin.getPlugin().mgdl; + mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); + doEdit(); }); - mmolView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - LocalProfilePlugin.getPlugin().mmol = mmolView.isChecked(); - LocalProfilePlugin.getPlugin().mgdl = !LocalProfilePlugin.getPlugin().mmol; - mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); - doEdit(); - } + mmolView.setOnClickListener(v -> { + LocalProfilePlugin.getPlugin().mmol = mmolView.isChecked(); + LocalProfilePlugin.getPlugin().mgdl = !LocalProfilePlugin.getPlugin().mmol; + mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); + doEdit(); }); - profileswitchButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - } + profileswitchButton.setOnClickListener(view -> { + NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); + final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; + profileswitch.executeProfileSwitch = true; + newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); + newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); }); resetButton.setOnClickListener(view -> { LocalProfilePlugin.getPlugin().loadSettings(); mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl); mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol); - diaView.setParams(LocalProfilePlugin.getPlugin().dia, 2d, 48d, 0.1d, new DecimalFormat("0.0"), false, textWatch); + diaView.setParams(LocalProfilePlugin.getPlugin().dia, 5d, 12d, 0.1d, new DecimalFormat("0.0"), false, textWatch); icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save); isfView = new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save); basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java index c132196228..8d4e2d6a36 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java @@ -13,6 +13,7 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.events.EventProfileStoreChanged; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -63,6 +64,7 @@ public class LocalProfilePlugin extends PluginBase implements ProfileInterface { .fragmentClass(LocalProfileFragment.class.getName()) .pluginName(R.string.localprofile) .shortName(R.string.localprofile_shortname) + .description(R.string.description_profile_local) ); loadSettings(); } @@ -81,6 +83,7 @@ public class LocalProfilePlugin extends PluginBase implements ProfileInterface { edited = false; if (Config.logPrefsChange) log.debug("Storing settings: " + getRawProfile().getData().toString()); + MainApp.bus().post(new EventProfileStoreChanged()); } public synchronized void loadSettings() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java index dca9b183a3..6cc9fdf98e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfileFragment.java @@ -79,11 +79,14 @@ public class NSProfileFragment extends SubscriberFragment { public void onStatusEvent(final EventNSProfileUpdateGUI ev) { Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(() -> updateGUI()); + activity.runOnUiThread(() -> { synchronized (NSProfileFragment.this) { updateGUI(); } }); } @Override protected void updateGUI() { + if (noProfile == null || profileSpinner == null) + return; + ProfileStore profileStore = NSProfilePlugin.getPlugin().getProfile(); if (profileStore != null) { ArrayList profileList = profileStore.getProfileList(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java index dfd62cda70..404aff0608 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java @@ -16,6 +16,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.Services.Intents; import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.events.EventProfileStoreChanged; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -43,11 +44,12 @@ public class NSProfilePlugin extends PluginBase implements ProfileInterface { super(new PluginDescription() .mainType(PluginType.PROFILE) .fragmentClass(NSProfileFragment.class.getName()) - .pluginName(R.string.profileviewer) + .pluginName(R.string.nsprofile) .shortName(R.string.profileviewer_shortname) .alwaysEnabled(Config.NSCLIENT) .alwayVisible(Config.NSCLIENT) .showInList(!Config.NSCLIENT) + .description(R.string.description_profile_nightscout) ); loadNSProfile(); } @@ -68,6 +70,7 @@ public class NSProfilePlugin extends PluginBase implements ProfileInterface { profile = new ProfileStore(newProfile.getData()); storeNSProfile(); MainApp.bus().post(new EventNSProfileUpdateGUI()); + MainApp.bus().post(new EventProfileStoreChanged()); } private void storeNSProfile() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfileFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfileFragment.java index b3b2226705..1315413e38 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfileFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfileFragment.java @@ -73,34 +73,25 @@ public class SimpleProfileFragment extends SubscriberFragment { targetlowView.setText(SimpleProfilePlugin.getPlugin().targetLow.toString()); targethighView.setText(SimpleProfilePlugin.getPlugin().targetHigh.toString()); - mgdlView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - SimpleProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); - SimpleProfilePlugin.getPlugin().mmol = !SimpleProfilePlugin.getPlugin().mgdl; - mmolView.setChecked(SimpleProfilePlugin.getPlugin().mmol); - SimpleProfilePlugin.getPlugin().storeSettings(); - } + mgdlView.setOnClickListener(v -> { + SimpleProfilePlugin.getPlugin().mgdl = mgdlView.isChecked(); + SimpleProfilePlugin.getPlugin().mmol = !SimpleProfilePlugin.getPlugin().mgdl; + mmolView.setChecked(SimpleProfilePlugin.getPlugin().mmol); + SimpleProfilePlugin.getPlugin().storeSettings(); }); - mmolView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - SimpleProfilePlugin.getPlugin().mmol = mmolView.isChecked(); - SimpleProfilePlugin.getPlugin().mgdl = !SimpleProfilePlugin.getPlugin().mmol; - mgdlView.setChecked(SimpleProfilePlugin.getPlugin().mgdl); - SimpleProfilePlugin.getPlugin().storeSettings(); - } + mmolView.setOnClickListener(v -> { + SimpleProfilePlugin.getPlugin().mmol = mmolView.isChecked(); + SimpleProfilePlugin.getPlugin().mgdl = !SimpleProfilePlugin.getPlugin().mmol; + mgdlView.setChecked(SimpleProfilePlugin.getPlugin().mgdl); + SimpleProfilePlugin.getPlugin().storeSettings(); }); - profileswitchButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); - final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCH; - profileswitch.executeProfileSwitch = true; - newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); - newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); - } + profileswitchButton.setOnClickListener(view -> { + NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); + final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCH; + profileswitch.executeProfileSwitch = true; + newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); + newDialog.show(getFragmentManager(), "NewNSTreatmentDialog"); }); TextWatcher textWatch = new TextWatcher() { @@ -152,20 +143,17 @@ public class SimpleProfileFragment extends SubscriberFragment { protected void updateGUI() { Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - boolean isValid = SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid(MainApp.gs(R.string.simpleprofile)); - if (!ConfigBuilderPlugin.getActivePump().isInitialized() || ConfigBuilderPlugin.getActivePump().isSuspended() || !isValid) { - profileswitchButton.setVisibility(View.GONE); - } else { - profileswitchButton.setVisibility(View.VISIBLE); - } - if (isValid) - invalidProfile.setVisibility(View.GONE); - else - invalidProfile.setVisibility(View.VISIBLE); + activity.runOnUiThread(() -> { + boolean isValid = SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid(MainApp.gs(R.string.simpleprofile)); + if (!ConfigBuilderPlugin.getActivePump().isInitialized() || ConfigBuilderPlugin.getActivePump().isSuspended() || !isValid) { + profileswitchButton.setVisibility(View.GONE); + } else { + profileswitchButton.setVisibility(View.VISIBLE); } + if (isValid) + invalidProfile.setVisibility(View.GONE); + else + invalidProfile.setVisibility(View.VISIBLE); }); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java index adff378819..bb669289ee 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java @@ -14,6 +14,7 @@ import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.events.EventProfileStoreChanged; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; @@ -51,6 +52,7 @@ public class SimpleProfilePlugin extends PluginBase implements ProfileInterface .fragmentClass(SimpleProfileFragment.class.getName()) .pluginName(R.string.simpleprofile) .shortName(R.string.simpleprofile_shortname) + .description(R.string.description_profile_simple) ); loadSettings(); } @@ -73,6 +75,7 @@ public class SimpleProfilePlugin extends PluginBase implements ProfileInterface createConvertedProfile(); if (Config.logPrefsChange) log.debug("Storing settings: " + getRawProfile().getData().toString()); + MainApp.bus().post(new EventProfileStoreChanged()); } private void loadSettings() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java index 3cf4557e54..a16ffbda05 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ComboPlugin.java @@ -1,8 +1,11 @@ package info.nightscout.androidaps.plugins.PumpCombo; +import android.content.DialogInterface; import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.crashlytics.android.answers.CustomEvent; @@ -28,6 +31,7 @@ import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TDD; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventRefreshOverview; @@ -57,8 +61,6 @@ import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.PumpHi import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.PumpHistoryRequest; import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.Tdd; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.queue.CommandQueue; import info.nightscout.utils.DateUtil; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.SP; @@ -166,6 +168,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint .fragmentClass(ComboFragment.class.getName()) .pluginName(R.string.combopump) .shortName(R.string.combopump_shortname) + .description(R.string.description_pump_combo) ); ruffyScripter = new RuffyScripter(MainApp.instance().getApplicationContext()); } @@ -192,6 +195,32 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint return MainApp.gs(R.string.combo_pump_state_running); } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + + @Override public boolean isInitialized() { return pump.initialized; @@ -352,6 +381,11 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint // trigger a connect, which will update state and check history CommandResult stateResult = runCommand(null, 1, ruffyScripter::readPumpState); + if (stateResult.invalidSetup) { + MainApp.bus().post(new EventNewNotification( + new Notification(Notification.COMBO_PUMP_ALARM, MainApp.gs(R.string.combo_invalid_setup), Notification.URGENT))); + return; + } if (!stateResult.success) { return; } @@ -421,9 +455,6 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint return pump.basalProfile.hourlyRates[currentHour]; } - private static BolusProgressReporter nullBolusProgressReporter = (state, percent, delivered) -> { - }; - private static BolusProgressReporter bolusProgressReporter = (state, percent, delivered) -> { EventOverviewBolusProgress event = EventOverviewBolusProgress.getInstance(); switch (state) { @@ -464,7 +495,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint return deliverBolus(detailedBolusInfo); } else { // no bolus required, carb only treatment - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); bolusingEvent.t = new Treatment(); @@ -549,12 +580,14 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint return new PumpEnactResult().success(true).enacted(false); } - BolusProgressReporter progressReporter = detailedBolusInfo.isSMB ? nullBolusProgressReporter : bolusProgressReporter; + Treatment treatment = new Treatment(); + treatment.isSMB = detailedBolusInfo.isSMB; + EventOverviewBolusProgress.getInstance().t = treatment; // start bolus delivery scripterIsBolusing = true; runCommand(null, 0, - () -> ruffyScripter.deliverBolus(detailedBolusInfo.insulin, progressReporter)); + () -> ruffyScripter.deliverBolus(detailedBolusInfo.insulin, bolusProgressReporter)); scripterIsBolusing = false; // Note that the result of the issued bolus command is not checked. If there was @@ -653,7 +686,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint dbi.source = Source.PUMP; dbi.insulin = lastPumpBolus.amount; try { - boolean treatmentCreated = TreatmentsPlugin.getPlugin().addToHistoryTreatment(dbi); + boolean treatmentCreated = TreatmentsPlugin.getPlugin().addToHistoryTreatment(dbi, false); if (!treatmentCreated) { log.error("Adding treatment record overrode an existing record: " + dbi); if (dbi.isSMB) { @@ -1152,7 +1185,8 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint dbi.source = Source.PUMP; dbi.insulin = pumpBolus.amount; dbi.eventType = CareportalEvent.CORRECTIONBOLUS; - if (TreatmentsPlugin.getPlugin().addToHistoryTreatment(dbi)) { + if (TreatmentsPlugin.getPlugin().getService().getPumpRecordById(dbi.pumpId) == null) { + TreatmentsPlugin.getPlugin().addToHistoryTreatment(dbi, false); updated = true; } } @@ -1356,7 +1390,7 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint private boolean validBasalRateProfileSelectedOnPump = true; @Override - public Constraint isLoopInvokationAllowed(Constraint value) { + public Constraint isLoopInvocationAllowed(Constraint value) { if (!validBasalRateProfileSelectedOnPump) value.set(false, MainApp.gs(R.string.novalidbasalrate), this); return value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java index 9da5d4d6fa..b595d13d28 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/CommandResult.java @@ -5,10 +5,11 @@ import android.support.annotation.Nullable; import java.util.LinkedList; import java.util.List; -import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.Bolus; import info.nightscout.androidaps.plugins.PumpCombo.ruffyscripter.history.PumpHistory; public class CommandResult { + /** True if a condition indicating a broken pump setup/configuration is detected */ + public boolean invalidSetup; /** Whether the command was executed successfully. */ public boolean success; /** State of the pump *after* command execution. */ diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java index 1e7e542e5f..5b569e479e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/RuffyScripter.java @@ -281,10 +281,10 @@ public class RuffyScripter implements RuffyCommands { log.debug("Executing " + cmd + " took " + (cmdEndTime - cmdStartTime) + "ms"); } catch (CommandException e) { log.error("CommandException running command", e); - activeCmd.getResult().success = false; + cmd.getResult().success = false; } catch (Exception e) { log.error("Unexpected exception running cmd", e); - activeCmd.getResult().success = false; + cmd.getResult().success = false; } }, cmd.getClass().getSimpleName()); long executionStart = System.currentTimeMillis(); @@ -328,6 +328,7 @@ public class RuffyScripter implements RuffyCommands { if (unparsableMenuEncountered) { log.error("UnparsableMenuEncountered flagged, aborting command"); cmdThread.interrupt(); + activeCmd.getResult().invalidSetup = true; activeCmd.getResult().success = false; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java index e55fb4d010..654861fddb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCombo/ruffyscripter/commands/ReadQuickInfoCommand.java @@ -39,17 +39,19 @@ public class ReadQuickInfoCommand extends BaseCommand { // read bolus records int totalRecords = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.TOTAL_RECORD); int record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); - while (true) { - bolusHistory.add(readBolusRecord()); - if (bolusHistory.size() == numberOfBolusRecordsToRetrieve || record == totalRecords) { - break; + if (record > 0) { + while (true) { + bolusHistory.add(readBolusRecord()); + if (bolusHistory.size() == numberOfBolusRecordsToRetrieve || record == totalRecords) { + break; + } + // advance to next record + scripter.pressDownKey(); + while (record == (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD)) { + scripter.waitForScreenUpdate(); + } + record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); } - // advance to next record - scripter.pressDownKey(); - while (record == (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD)) { - scripter.waitForScreenUpdate(); - } - record = (int) scripter.getCurrentMenu().getAttribute(MenuAttribute.CURRENT_RECORD); } if (log.isDebugEnabled()) { if (!result.history.bolusHistory.isEmpty()) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java index 903be558b9..1f51979126 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/AbstractDanaRPlugin.java @@ -59,6 +59,7 @@ public abstract class AbstractDanaRPlugin extends PluginBase implements PumpInte .pluginName(R.string.danarspump) .shortName(R.string.danarpump_shortname) .preferencesId(R.xml.pref_danars) + .description(R.string.description_pump_dana_r) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java index b11cd87278..7288602d65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java @@ -11,6 +11,7 @@ import android.text.Spanned; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; @@ -29,11 +30,14 @@ import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.events.EventTempBasalChange; +import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.plugins.Common.SubscriberFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog; import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRUserOptionsActivity; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; +import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.events.EventQueueChanged; import info.nightscout.utils.DateUtil; @@ -88,6 +92,8 @@ public class DanaRFragment extends SubscriberFragment { LinearLayout pumpStatusLayout; @BindView(R.id.overview_pumpstatus) TextView pumpStatusView; + @BindView(R.id.danar_user_options) + Button danar_user_options; public DanaRFragment() { } @@ -138,6 +144,9 @@ public class DanaRFragment extends SubscriberFragment { startActivity(new Intent(getContext(), TDDStatsActivity.class)); } + @OnClick(R.id.danar_user_options) + void onUserOptionsClick() { startActivity(new Intent(getContext(), DanaRUserOptionsActivity.class)); } + @OnClick(R.id.danar_btconnection) void onBtConnectionClick() { log.debug("Clicked connect to pump"); @@ -153,19 +162,24 @@ public class DanaRFragment extends SubscriberFragment { activity.runOnUiThread( new Runnable() { @Override - public void run() { - if (c.sStatus == EventPumpStatusChanged.CONNECTING) - btConnectionView.setText("{fa-bluetooth-b spin} " + c.sSecondsElapsed + "s"); - else if (c.sStatus == EventPumpStatusChanged.CONNECTED) - btConnectionView.setText("{fa-bluetooth}"); - else if (c.sStatus == EventPumpStatusChanged.DISCONNECTED) - btConnectionView.setText("{fa-bluetooth-b}"); + public void run() { + synchronized(DanaRFragment.this){ - if (!status.equals("")) { - pumpStatusView.setText(status); - pumpStatusLayout.setVisibility(View.VISIBLE); - } else { - pumpStatusLayout.setVisibility(View.GONE); + if(btConnectionView == null || pumpStatusView == null || pumpStatusLayout == null ) return; + + if (c.sStatus == EventPumpStatusChanged.CONNECTING) + btConnectionView.setText("{fa-bluetooth-b spin} " + c.sSecondsElapsed + "s"); + else if (c.sStatus == EventPumpStatusChanged.CONNECTED) + btConnectionView.setText("{fa-bluetooth}"); + else if (c.sStatus == EventPumpStatusChanged.DISCONNECTED) + btConnectionView.setText("{fa-bluetooth-b}"); + + if (!status.equals("")) { + pumpStatusView.setText(status); + pumpStatusLayout.setVisibility(View.VISIBLE); + } else { + pumpStatusLayout.setVisibility(View.GONE); + } } } } @@ -202,69 +216,96 @@ public class DanaRFragment extends SubscriberFragment { @SuppressLint("SetTextI18n") @Override public void run() { - DanaRPump pump = DanaRPump.getInstance(); - if (pump.lastConnection != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastConnection; - int agoMin = (int) (agoMsec / 60d / 1000d); - lastConnectionView.setText(DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")"); - SetWarnColor.setColor(lastConnectionView, agoMin, 16d, 31d); - } - if (pump.lastBolusTime.getTime() != 0) { - Long agoMsec = System.currentTimeMillis() - pump.lastBolusTime.getTime(); - double agoHours = agoMsec / 60d / 60d / 1000d; - if (agoHours < 6) // max 6h back - lastBolusView.setText(DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime.getTime()) + " " + DecimalFormatter.to2Decimal(DanaRPump.getInstance().lastBolusAmount) + " U"); - else lastBolusView.setText(""); - } + synchronized(DanaRFragment.this) { + if (!isBound()) return; - dailyUnitsView.setText(DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U"); - SetWarnColor.setColor(dailyUnitsView, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75d, pump.maxDailyTotalUnits * 0.9d); - basaBasalRateView.setText("( " + (pump.activeProfile + 1) + " ) " + DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) + " U/h"); - // DanaRPlugin, DanaRKoreanPlugin - if (ConfigBuilderPlugin.getActivePump().isFakingTempsByExtendedBoluses()) { - if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) { - tempBasalView.setText(TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); - } else { - tempBasalView.setText(""); + DanaRPump pump = DanaRPump.getInstance(); + if (pump.lastConnection != 0) { + Long agoMsec = System.currentTimeMillis() - pump.lastConnection; + int agoMin = (int) (agoMsec / 60d / 1000d); + lastConnectionView.setText(DateUtil.timeString(pump.lastConnection) + " (" + String.format(MainApp.gs(R.string.minago), agoMin) + ")"); + SetWarnColor.setColor(lastConnectionView, agoMin, 16d, 31d); } - } else { - // v2 plugin - if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { - tempBasalView.setText(TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); - } else { - tempBasalView.setText(""); + if (pump.lastBolusTime.getTime() != 0) { + Long agoMsec = System.currentTimeMillis() - pump.lastBolusTime.getTime(); + double agoHours = agoMsec / 60d / 60d / 1000d; + if (agoHours < 6) // max 6h back + lastBolusView.setText(DateUtil.timeString(pump.lastBolusTime) + " " + DateUtil.sinceString(pump.lastBolusTime.getTime()) + " " + DecimalFormatter.to2Decimal(DanaRPump.getInstance().lastBolusAmount) + " U"); + else lastBolusView.setText(""); } - } - ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); - if (activeExtendedBolus != null) { - extendedBolusView.setText(activeExtendedBolus.toString()); - } else { - extendedBolusView.setText(""); - } - reservoirView.setText(DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + " / 300 U"); - SetWarnColor.setColorInverse(reservoirView, pump.reservoirRemainingUnits, 50d, 20d); - batteryView.setText("{fa-battery-" + (pump.batteryRemaining / 25) + "}"); - SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d); - iobView.setText(pump.iob + " U"); - if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { - firmwareView.setText(String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode)); - } else { - firmwareView.setText("OLD"); - } - basalStepView.setText("" + pump.basalStep); - bolusStepView.setText("" + pump.bolusStep); - serialNumberView.setText("" + pump.serialNumber); - if (queueView != null) { - Spanned status = ConfigBuilderPlugin.getCommandQueue().spannedStatus(); - if (status.toString().equals("")) { - queueView.setVisibility(View.GONE); + + dailyUnitsView.setText(DecimalFormatter.to0Decimal(pump.dailyTotalUnits) + " / " + pump.maxDailyTotalUnits + " U"); + SetWarnColor.setColor(dailyUnitsView, pump.dailyTotalUnits, pump.maxDailyTotalUnits * 0.75d, pump.maxDailyTotalUnits * 0.9d); + basaBasalRateView.setText("( " + (pump.activeProfile + 1) + " ) " + DecimalFormatter.to2Decimal(ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) + " U/h"); + // DanaRPlugin, DanaRKoreanPlugin + if (ConfigBuilderPlugin.getActivePump().isFakingTempsByExtendedBoluses()) { + if (TreatmentsPlugin.getPlugin().isInHistoryRealTempBasalInProgress()) { + tempBasalView.setText(TreatmentsPlugin.getPlugin().getRealTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); + } else { + tempBasalView.setText(""); + } } else { - queueView.setVisibility(View.VISIBLE); - queueView.setText(status); + // v2 plugin + if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { + tempBasalView.setText(TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()).toStringFull()); + } else { + tempBasalView.setText(""); + } + } + ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis()); + if (activeExtendedBolus != null) { + extendedBolusView.setText(activeExtendedBolus.toString()); + } else { + extendedBolusView.setText(""); + } + reservoirView.setText(DecimalFormatter.to0Decimal(pump.reservoirRemainingUnits) + " / 300 U"); + SetWarnColor.setColorInverse(reservoirView, pump.reservoirRemainingUnits, 50d, 20d); + batteryView.setText("{fa-battery-" + (pump.batteryRemaining / 25) + "}"); + SetWarnColor.setColorInverse(batteryView, pump.batteryRemaining, 51d, 26d); + iobView.setText(pump.iob + " U"); + if (pump.model != 0 || pump.protocol != 0 || pump.productCode != 0) { + firmwareView.setText(String.format(MainApp.gs(R.string.danar_model), pump.model, pump.protocol, pump.productCode)); + } else { + firmwareView.setText("OLD"); + } + basalStepView.setText("" + pump.basalStep); + bolusStepView.setText("" + pump.bolusStep); + serialNumberView.setText("" + pump.serialNumber); + if (queueView != null) { + Spanned status = ConfigBuilderPlugin.getCommandQueue().spannedStatus(); + if (status.toString().equals("")) { + queueView.setVisibility(View.GONE); + } else { + queueView.setVisibility(View.VISIBLE); + queueView.setText(status); + } + } + //hide user options button if not an RS pump + boolean isKorean = MainApp.getSpecificPlugin(DanaRKoreanPlugin.class) != null && MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isEnabled(PluginType.PUMP); + if (isKorean) { + danar_user_options.setVisibility(View.GONE); } } } }); } + private boolean isBound() { + return lastConnectionView != null + && lastBolusView != null + && dailyUnitsView != null + && basaBasalRateView != null + && tempBasalView != null + && extendedBolusView != null + && reservoirView != null + && batteryView != null + && iobView != null + && firmwareView != null + && basalStepView != null + && bolusStepView != null + && serialNumberView != null + && danar_user_options != null + && queueView != null; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index 279a93340f..144107779d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaR; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -18,6 +21,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStartWithSpeed; import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; @@ -78,6 +82,31 @@ public class DanaRPlugin extends AbstractDanaRPlugin { pumpDescription.needsManualTDDLoad = true; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); @@ -170,7 +199,7 @@ public class DanaRPlugin extends AbstractDanaRPlugin { log.debug("deliverTreatment: OK. Asked: " + detailedBolusInfo.insulin + " Delivered: " + result.bolusDelivered); detailedBolusInfo.insulin = t.insulin; detailedBolusInfo.date = System.currentTimeMillis(); - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); return result; } else { PumpEnactResult result = new PumpEnactResult(); @@ -379,4 +408,9 @@ public class DanaRPlugin extends AbstractDanaRPlugin { public PumpEnactResult loadEvents() { return null; // no history, not needed } + + @Override + public PumpEnactResult setUserOptions() { + return sExecutionService.setUserOptions(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java index 1e346695e3..d9bc947b84 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java @@ -161,7 +161,7 @@ public class DanaRPump { public int lowReservoirRate; public int cannulaVolume; public int refillAmount; - + public byte[] userOptionsFrompump; public double initialBolusAmount; // Bolus settings public int bolusCalculationOption; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRUserOptionsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRUserOptionsActivity.java new file mode 100644 index 0000000000..b4930a2a1a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRUserOptionsActivity.java @@ -0,0 +1,202 @@ +package info.nightscout.androidaps.plugins.PumpDanaR.activities; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.Button; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.Switch; + +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DecimalFormat; + +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventInitializationChanged; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin; +import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; +import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin; +import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin; +import info.nightscout.utils.NumberPicker; + +/** + * Created by Rumen Georgiev on 5/31/2018. + */ + +public class DanaRUserOptionsActivity extends Activity { + private static Logger log = LoggerFactory.getLogger(DanaRUserOptionsActivity.class); + + Switch timeFormat; + Switch buttonScroll; + Switch beep; + RadioGroup pumpAlarm; + RadioButton pumpAlarmSound; + RadioButton pumpAlarmVibrate; + RadioButton pumpAlarmBoth; + Switch pumpUnits; + NumberPicker screenTimeout; + NumberPicker backlightTimeout; + NumberPicker shutdown; + NumberPicker lowReservoir; + Button saveToPumpButton; + + @Override + protected void onResume() { + super.onResume(); + MainApp.bus().register(this); + } + + @Override + protected void onPause() { + super.onPause(); + MainApp.bus().unregister(this); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.danar_user_options); + + timeFormat = (Switch) findViewById(R.id.danar_timeformat); + buttonScroll = (Switch) findViewById(R.id.danar_buttonscroll); + beep = (Switch) findViewById(R.id.danar_beep); + pumpAlarm = (RadioGroup) findViewById(R.id.danar_pumpalarm); + pumpAlarmSound = (RadioButton) findViewById(R.id.danar_pumpalarm_sound); + pumpAlarmVibrate = (RadioButton) findViewById(R.id.danar_pumpalarm_vibrate); + pumpAlarmBoth = (RadioButton) findViewById(R.id.danar_pumpalarm_both); + screenTimeout = (NumberPicker) findViewById(R.id.danar_screentimeout); + backlightTimeout = (NumberPicker) findViewById(R.id.danar_backlight); + pumpUnits = (Switch) findViewById(R.id.danar_units); + shutdown = (NumberPicker) findViewById(R.id.danar_shutdown); + lowReservoir = (NumberPicker) findViewById(R.id.danar_lowreservoir); + saveToPumpButton = (Button) findViewById(R.id.save_user_options); + + saveToPumpButton.setOnClickListener(v -> onSaveClick()); + + DanaRPump pump = DanaRPump.getInstance(); + //used for debugging + log.debug("UserOptionsLoaded:" + (System.currentTimeMillis() - pump.lastConnection) / 1000 + " s ago" + + "\ntimeDisplayType:" + pump.timeDisplayType + + "\nbuttonScroll:" + pump.buttonScrollOnOff + + "\ntimeDisplayType:" + pump.timeDisplayType + + "\nlcdOnTimeSec:" + pump.lcdOnTimeSec + + "\nbacklight:" + pump.backlightOnTimeSec + + "\npumpUnits:" + pump.units + + "\nlowReservoir:" + pump.lowReservoirRate); + + screenTimeout.setParams((double) pump.lcdOnTimeSec, 5d, 240d, 5d, new DecimalFormat("1"), false); + backlightTimeout.setParams((double) pump.backlightOnTimeSec, 1d, 60d, 1d, new DecimalFormat("1"), false); + shutdown.setParams((double) pump.shutdownHour, 0d, 24d, 1d, new DecimalFormat("1"), true); + lowReservoir.setParams((double) pump.lowReservoirRate, 10d, 60d, 10d, new DecimalFormat("10"), false); + switch (pump.beepAndAlarm) { + case 0x01: + pumpAlarmSound.setChecked(true); + break; + case 0x02: + pumpAlarmVibrate.setChecked(true); + break; + case 0x11: + pumpAlarmBoth.setChecked(true); + break; + case 0x101: + pumpAlarmSound.setChecked(true); + beep.setChecked(true); + break; + case 0x110: + pumpAlarmVibrate.setChecked(true); + beep.setChecked(true); + break; + case 0x111: + pumpAlarmBoth.setChecked(true); + beep.setChecked(true); + break; + } + if (pump.lastSettingsRead == 0) + log.debug("No settings loaded from pump!"); + else + setData(); + } + + public void setData() { + DanaRPump pump = DanaRPump.getInstance(); + + timeFormat.setChecked(pump.timeDisplayType != 0); + buttonScroll.setChecked(pump.buttonScrollOnOff != 0); + beep.setChecked(pump.beepAndAlarm > 4); + screenTimeout.setValue((double) pump.lcdOnTimeSec); + backlightTimeout.setValue((double) pump.backlightOnTimeSec); + pumpUnits.setChecked(pump.getUnits() != null && pump.getUnits().equals(Constants.MMOL)); + shutdown.setValue((double) pump.shutdownHour); + lowReservoir.setValue((double) pump.lowReservoirRate); + } + + @Subscribe + public void onEventInitializationChanged(EventInitializationChanged ignored) { + runOnUiThread(this::setData); + } + + public void onSaveClick() { + boolean isRS = MainApp.getSpecificPlugin(DanaRSPlugin.class) != null && MainApp.getSpecificPlugin(DanaRSPlugin.class).isEnabled(PluginType.PUMP); + boolean isDanaR = MainApp.getSpecificPlugin(DanaRPlugin.class) != null && MainApp.getSpecificPlugin(DanaRPlugin.class).isEnabled(PluginType.PUMP); + boolean isDanaRv2 = MainApp.getSpecificPlugin(DanaRv2Plugin.class) != null && MainApp.getSpecificPlugin(DanaRv2Plugin.class).isEnabled(PluginType.PUMP); + if (!isRS && !isDanaR && !isDanaRv2) { + //exit if pump is not DanaRS, Dana!, or DanaR with upgraded firmware + return; + } + DanaRPump pump = DanaRPump.getInstance(); + + if (timeFormat.isChecked()) + pump.timeDisplayType = 1; + else + pump.timeDisplayType = 0; + if (buttonScroll.isChecked()) + pump.buttonScrollOnOff = 1; + else + pump.buttonScrollOnOff = 0; + + pump.beepAndAlarm = 1; // default + if (pumpAlarmSound.isChecked()) pump.beepAndAlarm = 1; + else if (pumpAlarmVibrate.isChecked()) pump.beepAndAlarm = 2; + else if (pumpAlarmBoth.isChecked()) pump.beepAndAlarm = 3; + if (beep.isChecked()) pump.beepAndAlarm += 4; + + + // step is 5 seconds + int screenTimeoutValue = !screenTimeout.getText().isEmpty() ? (Integer.parseInt(screenTimeout.getText().toString()) / 5) * 5: 5; + if (screenTimeoutValue > 4 && screenTimeoutValue < 241) { + pump.lcdOnTimeSec = screenTimeoutValue; + } else { + pump.lcdOnTimeSec = 5; + } + int backlightTimeoutValue = !backlightTimeout.getText().isEmpty() ? Integer.parseInt(backlightTimeout.getText().toString()): 1; + if (backlightTimeoutValue > 0 && backlightTimeoutValue < 61) { + pump.backlightOnTimeSec = backlightTimeoutValue; + } + if (pumpUnits.isChecked()) { + pump.units = 1; + } else { + pump.units = 0; + } + int shutDownValue = !shutdown.getText().isEmpty() ? Integer.parseInt(shutdown.getText().toString()) : 0; + if (shutDownValue > -1 && shutDownValue < 25) { + pump.shutdownHour = shutDownValue; + } else { + pump.shutdownHour = 0; + } + int lowReservoirValue = !lowReservoir.getText().isEmpty() ? (Integer.parseInt(lowReservoir.getText().toString()) * 10) / 10 : 10; + if (lowReservoirValue > 9 && lowReservoirValue < 51) { + pump.lowReservoirRate = lowReservoirValue; + } else + pump.lowReservoirRate = 10; + + MainApp.getConfigBuilder().getCommandQueue().setUserOptions(null); + finish(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MessageHashTable.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MessageHashTable.java index 10d2c0b091..4b90b8f02d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MessageHashTable.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MessageHashTable.java @@ -60,6 +60,7 @@ public class MessageHashTable { put(new MsgSettingProfileRatiosAll()); // 0x320D CMD_SETTING_V_CIR_CF_VALUE put(new MsgSetSingleBasalProfile()); // 0x3302 CMD_SETTING_BASAL_INS_S put(new MsgSetBasalProfile()); // 0x3306 CMD_SETTING_BASAL_PROFILE_S + put(new MsgSetUserOptions()); // 0x330B CMD_SETTING_USER_OPTIONS_S put(new MsgSetActivateBasalProfile()); // 0x330C CMD_SETTING_PROFILE_NUMBER_S put(new MsgHistoryAllDone()); // 0x41F1 CMD_HISTORY_ALL_DONE put(new MsgHistoryAll()); // 0x41F2 CMD_HISTORY_ALL diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MsgSetUserOptions.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MsgSetUserOptions.java new file mode 100644 index 0000000000..407b19da8d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/comm/MsgSetUserOptions.java @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.plugins.PumpDanaR.comm; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import android.support.v4.internal.view.SupportMenu; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; + +/** + * Created by mike on 05.07.2016. + */ +public class MsgSetUserOptions extends MessageBase { + + private static Logger log = LoggerFactory.getLogger(MsgSetUserOptions.class); + + public boolean done; + + public MsgSetUserOptions() { + SetCommand(0x330B); + DanaRPump pump = DanaRPump.getInstance(); + if (pump.userOptionsFrompump == null) { + // No options set -> Exitting + log.debug("NO USER OPTIONS LOADED EXITTING!"); + return; + } + pump.userOptionsFrompump[0] = (byte) (pump.timeDisplayType == 1 ? 0 : 1); + pump.userOptionsFrompump[1] = (byte) pump.buttonScrollOnOff; + pump.userOptionsFrompump[2] = (byte) pump.beepAndAlarm; + pump.userOptionsFrompump[3] = (byte) pump.lcdOnTimeSec; + pump.userOptionsFrompump[4] = (byte) pump.backlightOnTimeSec; + pump.userOptionsFrompump[5] = (byte) pump.selectedLanguage; + pump.userOptionsFrompump[8] = (byte) pump.units; + pump.userOptionsFrompump[9] = (byte) pump.shutdownHour; + pump.userOptionsFrompump[27] = (byte) pump.lowReservoirRate; + for(int i=0; i 24h 0 -> 12h + pump.buttonScrollOnOff = bytes[1] == (byte) 1 ? 1 : 0; // 1 -> ON, 0-> OFF + pump.beepAndAlarm = bytes[2]; // 1 -> Sound on alarm 2-> Vibrate on alarm 3-> Both on alarm 5-> Sound + beep 6-> vibrate + beep 7-> both + beep Beep adds 4 + pump.lcdOnTimeSec = bytes[3] & 255; + pump.backlightOnTimeSec = bytes[4] & 255; + pump.selectedLanguage = bytes[5]; // on DanaRv2 is that needed ? + pump.units = bytes[8]; + pump.shutdownHour = bytes[9]; + pump.lowReservoirRate = bytes[32] & 255; + /* int selectableLanguage1 = bytes[10]; + int selectableLanguage2 = bytes[11]; + int selectableLanguage3 = bytes[12]; + int selectableLanguage4 = bytes[13]; + int selectableLanguage5 = bytes[14]; + */ + if (Config.logDanaMessageDetail) { + + log.debug("timeDisplayType: " + pump.timeDisplayType); + log.debug("Button scroll: " + pump.buttonScrollOnOff); + log.debug("BeepAndAlarm: " + pump.beepAndAlarm); + log.debug("screen timeout: " + pump.lcdOnTimeSec); + log.debug("Backlight: " + pump.backlightOnTimeSec); + log.debug("Selected language: " + pump.selectedLanguage); + log.debug("Units: " + pump.getUnits()); + log.debug("Shutdown: " + pump.shutdownHour); + log.debug("Low reservoir: " + pump.lowReservoirRate); + } } + public static byte[] getDataBytes(byte[] bytes, int start, int len) { + if (bytes == null) { + return null; + } + byte[] ret = new byte[len]; + System.arraycopy(bytes, start + 6, ret, 0, len); + return ret; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/AbstractDanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/AbstractDanaRExecutionService.java index b7201c1163..e4c2933d43 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/AbstractDanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/AbstractDanaRExecutionService.java @@ -65,6 +65,11 @@ public abstract class AbstractDanaRExecutionService extends Service { protected final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); + protected long lastWrongPumpPassword = 0; + + protected long lastApproachingDailyLimit = 0; + + public abstract boolean updateBasalsInPump(final Profile profile); public abstract void connect(); @@ -87,6 +92,7 @@ public abstract class AbstractDanaRExecutionService extends Service { public abstract boolean extendedBolusStop(); + public abstract PumpEnactResult setUserOptions(); protected BroadcastReceiver receiver = new BroadcastReceiver() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java index 65a5d22d36..ea4dec25fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java @@ -18,17 +18,17 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; -import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.SerialIOThread; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MessageBase; @@ -45,6 +45,7 @@ import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetExtendedBolusStop import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStart; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStop; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTime; +import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetUserOptions; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingActiveProfile; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingBasal; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingGlucose; @@ -54,15 +55,17 @@ import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingProfileRatios import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingProfileRatiosAll; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingPumpTime; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingShippingInfo; +import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingUserOptions; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatus; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusBasic; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusBolusExtended; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusTempBasal; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; +import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; -import info.nightscout.utils.ToastUtils; public class DanaRExecutionService extends AbstractDanaRExecutionService{ @@ -97,7 +100,11 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService{ public void connect() { if (mDanaRPump.password != -1 && mDanaRPump.password != SP.getInt(R.string.key_danar_password, -1)) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.wrongpumppassword), R.raw.error); + if(System.currentTimeMillis() > lastWrongPumpPassword + 30 * 1000) { + Notification notification = new Notification(Notification.WRONG_PUMP_PASSWORD, MainApp.gs(R.string.wrongpumppassword), Notification.URGENT); + notification.soundId = R.raw.error; + lastWrongPumpPassword = System.currentTimeMillis(); + } return; } @@ -161,7 +168,19 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService{ MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); long now = System.currentTimeMillis(); - if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRPlugin.class).isInitialized()) { + mDanaRPump.lastConnection = now; + + Profile profile = MainApp.getConfigBuilder().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (profile != null && Math.abs(mDanaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + mSerialIOThread.sendMessage(new MsgSettingBasal()); + if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { + MainApp.bus().post(new EventProfileSwitchChange()); + } + } + + if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); @@ -173,6 +192,7 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService{ mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); mSerialIOThread.sendMessage(new MsgSettingProfileRatios()); mSerialIOThread.sendMessage(new MsgSettingProfileRatiosAll()); + mSerialIOThread.sendMessage(new MsgSettingUserOptions()); MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); mSerialIOThread.sendMessage(new MsgSettingPumpTime()); long timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; @@ -186,15 +206,17 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService{ mDanaRPump.lastSettingsRead = now; } - mDanaRPump.lastConnection = now; MainApp.bus().post(new EventDanaRNewStatus()); MainApp.bus().post(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (mDanaRPump.dailyTotalUnits > mDanaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { log.debug("Approaching daily limit: " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits); - Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); - NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + if(System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { + Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(reportFail)); + NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + lastApproachingDailyLimit = System.currentTimeMillis(); + } } } catch (Exception e) { log.error("Unhandled exception", e); @@ -390,4 +412,13 @@ public class DanaRExecutionService extends AbstractDanaRExecutionService{ log.debug("EventAppExit finished"); } + public PumpEnactResult setUserOptions() { + if (!isConnected()) + return new PumpEnactResult().success(false); + SystemClock.sleep(300); + MsgSetUserOptions msg = new MsgSetUserOptions(); + mSerialIOThread.sendMessage(msg); + SystemClock.sleep(200); + return new PumpEnactResult().success(!msg.failed); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 092febec61..49edb6b096 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaRKorean; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -23,6 +26,7 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.PumpDanaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStart; import info.nightscout.androidaps.plugins.PumpDanaRKorean.services.DanaRKoreanExecutionService; @@ -45,7 +49,8 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { } public DanaRKoreanPlugin() { - super(); + pluginDescription.description(R.string.description_pump_dana_r_korean); + log = LoggerFactory.getLogger(DanaRKoreanPlugin.class); useExtendedBoluses = SP.getBoolean("danar_useextended", false); @@ -79,6 +84,32 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { pumpDescription.needsManualTDDLoad = true; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + + @Override protected void onStart() { Context context = MainApp.instance().getApplicationContext(); @@ -171,7 +202,7 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { log.debug("deliverTreatment: OK. Asked: " + detailedBolusInfo.insulin + " Delivered: " + result.bolusDelivered); detailedBolusInfo.insulin = t.insulin; detailedBolusInfo.date = System.currentTimeMillis(); - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); return result; } else { PumpEnactResult result = new PumpEnactResult(); @@ -380,4 +411,9 @@ public class DanaRKoreanPlugin extends AbstractDanaRPlugin { public PumpEnactResult loadEvents() { return null; // no history, not needed } + + @Override + public PumpEnactResult setUserOptions() { + return null; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java index b9cc704aba..0f4afa4314 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java @@ -18,11 +18,12 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; @@ -38,6 +39,7 @@ import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetSingleBasalProfil import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStart; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStop; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTime; +import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingBasal; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingGlucose; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingMaxValues; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingMeal; @@ -53,9 +55,10 @@ import info.nightscout.androidaps.plugins.PumpDanaRKorean.SerialIOThread; import info.nightscout.androidaps.plugins.PumpDanaRKorean.comm.MsgCheckValue_k; import info.nightscout.androidaps.plugins.PumpDanaRKorean.comm.MsgSettingBasal_k; import info.nightscout.androidaps.plugins.PumpDanaRKorean.comm.MsgStatusBasic_k; +import info.nightscout.androidaps.plugins.Treatments.Treatment; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; -import info.nightscout.utils.ToastUtils; public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { @@ -105,7 +108,11 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { public void connect() { if (mDanaRPump.password != -1 && mDanaRPump.password != SP.getInt(R.string.key_danar_password, -1)) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.wrongpumppassword), R.raw.error); + if(System.currentTimeMillis() > lastWrongPumpPassword + 30 * 1000) { + Notification notification = new Notification(Notification.WRONG_PUMP_PASSWORD, MainApp.gs(R.string.wrongpumppassword), Notification.URGENT); + notification.soundId = R.raw.error; + lastWrongPumpPassword = System.currentTimeMillis(); + } return; } @@ -169,7 +176,19 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingbolusstatus))); long now = System.currentTimeMillis(); - if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isInitialized()) { + mDanaRPump.lastConnection = now; + + Profile profile = MainApp.getConfigBuilder().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (profile != null && Math.abs(mDanaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + mSerialIOThread.sendMessage(new MsgSettingBasal()); + if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { + MainApp.bus().post(new EventProfileSwitchChange()); + } + } + + if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingMeal()); @@ -191,15 +210,17 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { mDanaRPump.lastSettingsRead = now; } - mDanaRPump.lastConnection = now; MainApp.bus().post(new EventDanaRNewStatus()); MainApp.bus().post(new EventInitializationChanged()); NSUpload.uploadDeviceStatus(); if (mDanaRPump.dailyTotalUnits > mDanaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { log.debug("Approaching daily limit: " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits); - Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); - NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + if(System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { + Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(reportFail)); + NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + lastApproachingDailyLimit = System.currentTimeMillis(); + } } } catch (Exception e) { log.error("Unhandled exception", e); @@ -320,4 +341,9 @@ public class DanaRKoreanExecutionService extends AbstractDanaRExecutionService { return true; } + @Override + public PumpEnactResult setUserOptions() { + return null; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java index 1cbb256c1e..36edf1e94f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/DanaRSPlugin.java @@ -2,10 +2,13 @@ package info.nightscout.androidaps.plugins.PumpDanaRS; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -26,8 +29,6 @@ import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Bolus_Set_Step_Bolus_Start; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; @@ -38,6 +39,7 @@ import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; @@ -46,13 +48,16 @@ import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRFragment; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; +import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Bolus_Set_Step_Bolus_Start; import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSDeviceChange; import info.nightscout.androidaps.plugins.PumpDanaRS.services.DanaRSService; +import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.Round; import info.nightscout.utils.SP; +import info.nightscout.utils.T; /** * Created by mike on 03.09.2017. @@ -83,6 +88,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte .pluginName(R.string.danarspump) .shortName(R.string.danarspump_shortname) .preferencesId(R.xml.pref_danars) + .description(R.string.description_pump_dana_rs) ); pumpDescription.isBolusCapable = true; @@ -146,6 +152,31 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte MainApp.bus().unregister(this); } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + private ServiceConnection mConnection = new ServiceConnection() { public void onServiceDisconnected(ComponentName name) { @@ -223,6 +254,11 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte return danaRSService.loadEvents(); } + @Override + public PumpEnactResult setUserOptions() { + return danaRSService.setUserSettings(); + } + // Constraints interface @Override @@ -376,6 +412,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte double carbs = detailedBolusInfo.carbs; detailedBolusInfo.carbs = 0; int carbTime = detailedBolusInfo.carbTime; + if (carbTime == 0) carbTime--; // better set 1 min back to prevents clash with insulin detailedBolusInfo.carbTime = 0; DetailedBolusInfoStorage.add(detailedBolusInfo); // will be picked up on reading history @@ -384,7 +421,7 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte t.isSMB = detailedBolusInfo.isSMB; boolean connectionOK = false; if (detailedBolusInfo.insulin > 0 || carbs > 0) - connectionOK = danaRSService.bolus(detailedBolusInfo.insulin, (int) carbs, DateUtil.now() + carbTime * 60 * 1000, t); + connectionOK = danaRSService.bolus(detailedBolusInfo.insulin, (int) carbs, DateUtil.now() + T.mins(carbTime).msecs(), t); PumpEnactResult result = new PumpEnactResult(); result.success = connectionOK && Math.abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep; result.bolusDelivered = t.insulin; @@ -480,8 +517,12 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte // Convert duration from minutes to hours if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); - // use special APS temp basal call ... 100+/15min .... 100-/30min - result = setHighTempBasalPercent(percentRate); + if (percentRate == 0 && durationInMinutes > 30) { + result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + } else { + // use special APS temp basal call ... 100+/15min .... 100-/30min + result = setHighTempBasalPercent(percentRate); + } if (!result.success) { log.error("setTempBasalAbsolute: Failed to set hightemp basal"); return result; @@ -758,4 +799,5 @@ public class DanaRSPlugin extends PluginBase implements PumpInterface, DanaRInte public PumpEnactResult loadTDDs() { return loadHistory(RecordTypes.RECORD_TYPE_DAILY); } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/activities/BLEScanActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/activities/BLEScanActivity.java index 319a64f713..3b6bcaf099 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/activities/BLEScanActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/activities/BLEScanActivity.java @@ -62,13 +62,15 @@ public class BLEScanActivity extends AppCompatActivity { super.onResume(); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); - - if (mBluetoothLeScanner == null) { - mBluetoothAdapter.enable(); + if (mBluetoothAdapter != null) { mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + + if (mBluetoothLeScanner == null) { + mBluetoothAdapter.enable(); + mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); + } + startScan(); } - startScan(); } @Override @@ -79,11 +81,13 @@ public class BLEScanActivity extends AppCompatActivity { } private void startScan() { - mBluetoothLeScanner.startScan(mBleScanCallback); + if (mBluetoothLeScanner != null) + mBluetoothLeScanner.startScan(mBleScanCallback); } private void stopScan() { - mBluetoothLeScanner.stopScan(mBleScanCallback); + if (mBluetoothLeScanner != null) + mBluetoothLeScanner.stopScan(mBleScanCallback); } private void addBleDevice(BluetoothDevice device) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_APS_History_Events.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_APS_History_Events.java index 181584df8e..b8c8aaf2b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_APS_History_Events.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_APS_History_Events.java @@ -133,14 +133,14 @@ public class DanaRS_Packet_APS_History_Events extends DanaRS_Packet { break; case DanaRPump.BOLUS: detailedBolusInfo.insulin = param1 / 100d; - boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT BOLUS (" + recordCode + ") " + datetime.toLocaleString() + " Bolus: " + (param1 / 100d) + "U Duration: " + param2 + "min"); DetailedBolusInfoStorage.remove(detailedBolusInfo.date); status = "BOLUS " + DateUtil.timeString(datetime); break; case DanaRPump.DUALBOLUS: detailedBolusInfo.insulin = param1 / 100d; - newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT DUALBOLUS (" + recordCode + ") " + datetime.toLocaleString() + " Bolus: " + (param1 / 100d) + "U Duration: " + param2 + "min"); DetailedBolusInfoStorage.remove(detailedBolusInfo.date); status = "DUALBOLUS " + DateUtil.timeString(datetime); @@ -183,7 +183,7 @@ public class DanaRS_Packet_APS_History_Events extends DanaRS_Packet { emptyCarbsInfo.date = datetime.getTime(); emptyCarbsInfo.source = Source.PUMP; emptyCarbsInfo.pumpId = datetime.getTime(); - newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(emptyCarbsInfo); + newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(emptyCarbsInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT CARBS (" + recordCode + ") " + datetime.toLocaleString() + " Carbs: " + param1 + "g"); status = "CARBS " + DateUtil.timeString(datetime); break; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_Option_Set_User_Option.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_Option_Set_User_Option.java index e5d9374f32..40577aaa5d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_Option_Set_User_Option.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/comm/DanaRS_Packet_Option_Set_User_Option.java @@ -23,7 +23,14 @@ public class DanaRS_Packet_Option_Set_User_Option extends DanaRS_Packet { @Override public byte[] getRequestParams() { DanaRPump pump = DanaRPump.getInstance(); - + log.debug("UserOptions:"+(System.currentTimeMillis() - pump.lastConnection)/1000+" s ago" + +"\ntimeDisplayType:"+pump.timeDisplayType + +"\nbuttonScroll:"+pump.buttonScrollOnOff + +"\ntimeDisplayType:"+pump.timeDisplayType + +"\nlcdOnTimeSec:"+pump.lcdOnTimeSec + +"\nbacklight:"+pump.backlightOnTimeSec + +"\npumpUnits:"+pump.units + +"\nlowReservoir:"+pump.lowReservoirRate); byte[] request = new byte[13]; request[0] = (byte) (pump.timeDisplayType & 0xff); request[1] = (byte) (pump.buttonScrollOnOff & 0xff); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java index b552026279..9df2ebc0a1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/BLEComm.java @@ -20,13 +20,13 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaRS.DanaRSPlugin; import info.nightscout.androidaps.plugins.PumpDanaRS.activities.PairingHelperActivity; @@ -34,6 +34,7 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRSMessageHashTable import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet; import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPacket; import info.nightscout.androidaps.plugins.PumpDanaRS.events.EventDanaRSPairingSuccess; +import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; /** @@ -45,11 +46,11 @@ public class BLEComm { private static final long WRITE_DELAY_MILLIS = 50; - public static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; - public static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; + private static String UART_READ_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"; + private static String UART_WRITE_UUID = "0000fff2-0000-1000-8000-00805f9b34fb"; - private byte PACKET_START_BYTE = (byte) 0xA5; - private byte PACKET_END_BYTE = (byte) 0x5A; + private final byte PACKET_START_BYTE = (byte) 0xA5; + private final byte PACKET_END_BYTE = (byte) 0x5A; private static BLEComm instance = null; public static BLEComm getInstance(DanaRSService service) { @@ -58,16 +59,13 @@ public class BLEComm { return instance; } - private final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture scheduledDisconnection = null; private DanaRS_Packet processsedMessage = null; - private ArrayList mSendQueue = new ArrayList<>(); + private final ArrayList mSendQueue = new ArrayList<>(); private BluetoothManager mBluetoothManager = null; private BluetoothAdapter mBluetoothAdapter = null; - private BluetoothDevice mBluetoothDevice = null; - private String mBluetoothDeviceAddress = null; private String mBluetoothDeviceName = null; private BluetoothGatt mBluetoothGatt = null; @@ -79,7 +77,7 @@ public class BLEComm { private DanaRSService service; - BLEComm(DanaRSService service) { + private BLEComm(DanaRSService service) { this.service = service; initialize(); } @@ -138,15 +136,13 @@ public class BLEComm { BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { - log.debug("Device not found. Unable to connect."); + log.debug("Device not found. Unable to connect from: " + from); return false; } - log.debug("Trying to create a new connection."); + log.debug("Trying to create a new connection from: " + from); mBluetoothGatt = device.connectGatt(service.getApplicationContext(), false, mGattCallback); setCharacteristicNotification(getUARTReadBTGattChar(), true); - mBluetoothDevice = device; - mBluetoothDeviceAddress = address; mBluetoothDeviceName = device.getName(); return true; } @@ -174,7 +170,7 @@ public class BLEComm { SystemClock.sleep(2000); } - public void close() { + public synchronized void close() { log.debug("BluetoothAdapter close"); if (mBluetoothGatt == null) { return; @@ -184,15 +180,8 @@ public class BLEComm { mBluetoothGatt = null; } - public BluetoothDevice getConnectDevice() { - return mBluetoothDevice; - } - public String getConnectDeviceAddress() { - return mBluetoothDeviceAddress; - } - - public String getConnectDeviceName() { + private String getConnectDeviceName() { return mBluetoothDeviceName; } @@ -229,33 +218,25 @@ public class BLEComm { public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { log.debug("onCharacteristicChanged" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); addToReadBuffer(characteristic.getValue()); - new Thread(new Runnable() { - @Override - public void run() { - readDataParsing(); - } - }).start(); + new Thread(() -> readDataParsing()).start(); } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { log.debug("onCharacteristicWrite" + (characteristic != null ? ":" + DanaRS_Packet.toHexString(characteristic.getValue()) : "")); - new Thread(new Runnable() { - @Override - public void run() { - synchronized (mSendQueue) { - // after message sent, check if there is the rest of the message waiting and send it - if (mSendQueue.size() > 0) { - byte[] bytes = mSendQueue.get(0); - mSendQueue.remove(0); - writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); - } + new Thread(() -> { + synchronized (mSendQueue) { + // after message sent, check if there is the rest of the message waiting and send it + if (mSendQueue.size() > 0) { + byte[] bytes = mSendQueue.get(0); + mSendQueue.remove(0); + writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); } } }).start(); } }; - public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { + private synchronized void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { log.debug("setCharacteristicNotification"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -266,7 +247,7 @@ public class BLEComm { mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); } - public void readCharacteristic(BluetoothGattCharacteristic characteristic) { + public synchronized void readCharacteristic(BluetoothGattCharacteristic characteristic) { log.debug("readCharacteristic"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -277,41 +258,39 @@ public class BLEComm { mBluetoothGatt.readCharacteristic(characteristic); } - public void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { - new Thread(new Runnable() { - public void run() { - SystemClock.sleep(WRITE_DELAY_MILLIS); + private synchronized void writeCharacteristic_NO_RESPONSE(final BluetoothGattCharacteristic characteristic, final byte[] data) { + new Thread(() -> { + SystemClock.sleep(WRITE_DELAY_MILLIS); - if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { - log.debug("BluetoothAdapter not initialized_ERROR"); - isConnecting = false; - isConnected = false; - return; - } - - characteristic.setValue(data); - characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); - log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); - mBluetoothGatt.writeCharacteristic(characteristic); + if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { + log.debug("BluetoothAdapter not initialized_ERROR"); + isConnecting = false; + isConnected = false; + return; } + + characteristic.setValue(data); + characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); + log.debug("writeCharacteristic:" + DanaRS_Packet.toHexString(data)); + mBluetoothGatt.writeCharacteristic(characteristic); }).start(); } - public BluetoothGattCharacteristic getUARTReadBTGattChar() { + private BluetoothGattCharacteristic getUARTReadBTGattChar() { if (UART_Read == null) { UART_Read = new BluetoothGattCharacteristic(UUID.fromString(UART_READ_UUID), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 0); } return UART_Read; } - public BluetoothGattCharacteristic getUARTWriteBTGattChar() { + private BluetoothGattCharacteristic getUARTWriteBTGattChar() { if (UART_Write == null) { UART_Write = new BluetoothGattCharacteristic(UUID.fromString(UART_WRITE_UUID), BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 0); } return UART_Write; } - public List getSupportedGattServices() { + private List getSupportedGattServices() { log.debug("getSupportedGattServices"); if ((mBluetoothAdapter == null) || (mBluetoothGatt == null)) { log.debug("BluetoothAdapter not initialized_ERROR"); @@ -329,7 +308,7 @@ public class BLEComm { if (gattServices == null) { return; } - String uuid = null; + String uuid; for (BluetoothGattService gattService : gattServices) { List gattCharacteristics = gattService.getCharacteristics(); @@ -346,7 +325,7 @@ public class BLEComm { } } - private byte[] readBuffer = new byte[1024]; + private final byte[] readBuffer = new byte[1024]; private int bufferLength = 0; private void addToReadBuffer(byte[] buffer) { @@ -445,14 +424,25 @@ public class BLEComm { SendPairingRequest(); } + } else if (inputBuffer.length == 6 && inputBuffer[2] == 'P' && inputBuffer[3] == 'U' && inputBuffer[4] == 'M' && inputBuffer[5] == 'P') { + log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (PUMP)" + " " + DanaRS_Packet.toHexString(inputBuffer)); + mSendQueue.clear(); + MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.pumperror))); + NSUpload.uploadError(MainApp.gs(R.string.pumperror)); + Notification n = new Notification(Notification.PUMPERROR, MainApp.gs(R.string.pumperror), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(n)); } else if (inputBuffer.length == 6 && inputBuffer[2] == 'B' && inputBuffer[3] == 'U' && inputBuffer[4] == 'S' && inputBuffer[5] == 'Y') { log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (BUSY)" + " " + DanaRS_Packet.toHexString(inputBuffer)); mSendQueue.clear(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.pumpbusy))); } else { + // ERROR in response, wrong serial number log.debug("<<<<< " + "ENCRYPTION__PUMP_CHECK (ERROR)" + " " + DanaRS_Packet.toHexString(inputBuffer)); mSendQueue.clear(); MainApp.bus().post(new EventPumpStatusChanged(EventPumpStatusChanged.DISCONNECTED, MainApp.gs(R.string.connectionerror))); + SP.remove(MainApp.gs(R.string.key_danars_pairingkey) + DanaRSPlugin.mDeviceName); + Notification n = new Notification(Notification.WRONGSERIALNUMBER, MainApp.gs(R.string.wrongpassword), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(n)); } break; // 2nd packet, pairing key @@ -634,7 +624,7 @@ public class BLEComm { writeCharacteristic_NO_RESPONSE(getUARTWriteBTGattChar(), bytes); } - protected void SendPumpCheck() { + private void SendPumpCheck() { // 1st message sent to pump after connect byte[] bytes = BleCommandUtil.getInstance().getEncryptedPacket(BleCommandUtil.DANAR_PACKET__OPCODE_ENCRYPTION__PUMP_CHECK, null, getConnectDeviceName()); log.debug(">>>>> " + "ENCRYPTION__PUMP_CHECK (0x00)" + " " + DanaRS_Packet.toHexString(bytes)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java index a6afd0aeb0..fae0430adc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRS/services/DanaRSService.java @@ -19,15 +19,17 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventInitializationChanged; +import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; -import info.nightscout.androidaps.plugins.Overview.notifications.Notification; +import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; +import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; @@ -69,8 +71,12 @@ import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_History_ import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_Delivery_Complete; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Notify_Delivery_Rate_Display; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Get_Pump_Time; +import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Get_User_Option; import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Set_Pump_Time; +import info.nightscout.androidaps.plugins.PumpDanaRS.comm.DanaRS_Packet_Option_Set_User_Option; +import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -87,6 +93,7 @@ public class DanaRSService extends Service { private Treatment bolusingTreatment = null; private long lastHistoryFetched = 0; + private long lastApproachingDailyLimit = 0; public DanaRSService() { try { @@ -133,8 +140,51 @@ public class DanaRSService extends Service { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingtempbasalstatus))); bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Temporary_Basal_State()); + danaRPump.lastConnection = System.currentTimeMillis(); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (profile != null && Math.abs(danaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal + if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { + MainApp.bus().post(new EventProfileSwitchChange()); + } + } + + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + + long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + if (Math.abs(timeDiff) > 3) { + if (Math.abs(timeDiff) > 60*60*1.5) { + log.debug("Pump time difference: " + timeDiff + " seconds - large difference"); + //If time-diff is very large, warn user until we can synchronize history readings properly + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.error); + i.putExtra("status", MainApp.gs(R.string.largetimediff)); + i.putExtra("title", MainApp.gs(R.string.largetimedifftitle)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + //deinitialize pump + danaRPump.lastConnection = 0; + MainApp.bus().post(new EventDanaRNewStatus()); + MainApp.bus().post(new EventInitializationChanged()); + return; + } else { + waitForWholeMinute(); // Dana can set only whole minute + // add 10sec to be sure we are over minute (will be cutted off anyway) + bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); + timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + } + } + long now = System.currentTimeMillis(); - if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()) { + if (danaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); bleComm.sendMessage(new DanaRS_Packet_General_Get_Shipping_Information()); // serial no bleComm.sendMessage(new DanaRS_Packet_General_Get_Pump_Check()); // firmware @@ -143,18 +193,7 @@ public class DanaRSService extends Service { bleComm.sendMessage(new DanaRS_Packet_Basal_Get_Basal_Rate()); // basal profile, basalStep, maxBasal bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_Calculation_Information()); // target bleComm.sendMessage(new DanaRS_Packet_Bolus_Get_CIR_CF_Array()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); - bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); - long timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - if (Math.abs(timeDiff) > 3) { - waitForWholeMinute(); // Dana can set only whole minute - // add 10sec to be sure we are over minute (will be cutted off anyway) - bleComm.sendMessage(new DanaRS_Packet_Option_Set_Pump_Time(new Date(DateUtil.now() + T.secs(10).msecs()))); - bleComm.sendMessage(new DanaRS_Packet_Option_Get_Pump_Time()); - timeDiff = (danaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - } + bleComm.sendMessage(new DanaRS_Packet_Option_Get_User_Option()); // Getting user options danaRPump.lastSettingsRead = now; } @@ -165,9 +204,12 @@ public class DanaRSService extends Service { NSUpload.uploadDeviceStatus(); if (danaRPump.dailyTotalUnits > danaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { log.debug("Approaching daily limit: " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits); - Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); - NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); + if(System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { + Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(reportFail)); + NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + danaRPump.dailyTotalUnits + "/" + danaRPump.maxDailyTotalUnits + "U"); + lastApproachingDailyLimit = System.currentTimeMillis(); + } } } catch (Exception e) { log.error("Unhandled exception", e); @@ -176,6 +218,15 @@ public class DanaRSService extends Service { } public PumpEnactResult loadEvents() { + + if(!MainApp.getSpecificPlugin(DanaRSPlugin.class).isInitialized()){ + PumpEnactResult result = new PumpEnactResult().success(false); + result.comment = "pump not initialized"; + return result; + } + + SystemClock.sleep(1000); + DanaRS_Packet_APS_History_Events msg; if (lastHistoryFetched == 0) { msg = new DanaRS_Packet_APS_History_Events(0); @@ -189,7 +240,7 @@ public class DanaRSService extends Service { SystemClock.sleep(100); } if (DanaRS_Packet_APS_History_Events.lastEventTimeLoaded != 0) - lastHistoryFetched = DanaRS_Packet_APS_History_Events.lastEventTimeLoaded - 45 * 60 * 1000L; // always load last 45 min + lastHistoryFetched = DanaRS_Packet_APS_History_Events.lastEventTimeLoaded - T.mins(1).msecs(); else lastHistoryFetched = 0; log.debug("Events loaded"); @@ -198,6 +249,13 @@ public class DanaRSService extends Service { } + public PumpEnactResult setUserSettings() { + bleComm.sendMessage(new DanaRS_Packet_Option_Set_User_Option()); + bleComm.sendMessage(new DanaRS_Packet_Option_Get_User_Option()); + return new PumpEnactResult().success(true); + } + + public boolean bolus(final double insulin, int carbs, long carbtime, Treatment t) { if (!isConnected()) return false; if (BolusProgressDialog.stopPressed) return false; @@ -214,7 +272,7 @@ public class DanaRSService extends Service { // bleComm.sendMessage(msg); DanaRS_Packet_APS_Set_Event_History msgSetHistoryEntry_v2 = new DanaRS_Packet_APS_Set_Event_History(DanaRPump.CARBS, carbtime, carbs, 0); bleComm.sendMessage(msgSetHistoryEntry_v2); - lastHistoryFetched = carbtime - 60000; + lastHistoryFetched = Math.min(lastHistoryFetched, carbtime - T.mins(1).msecs()); } final long bolusStart = System.currentTimeMillis(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index 7c13707949..e83f9d9435 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -2,9 +2,12 @@ package info.nightscout.androidaps.plugins.PumpDanaRv2; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import com.squareup.otto.Subscribe; @@ -20,6 +23,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PumpDescription; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.DetailedBolusInfoStorage; import info.nightscout.androidaps.plugins.PumpDanaR.AbstractDanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgBolusStartWithSpeed; @@ -29,6 +33,7 @@ import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; import info.nightscout.utils.Round; import info.nightscout.utils.SP; +import info.nightscout.utils.T; /** * Created by mike on 05.08.2016. @@ -44,6 +49,8 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { } private DanaRv2Plugin() { + pluginDescription.description(R.string.description_pump_dana_r_v2); + log = LoggerFactory.getLogger(DanaRv2Plugin.class); useExtendedBoluses = false; @@ -138,6 +145,31 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { return pump.lastConnection > 0 && pump.maxBasal > 0; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + // Pump interface @Override public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) { @@ -164,6 +196,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { double carbs = detailedBolusInfo.carbs; detailedBolusInfo.carbs = 0; int carbTime = detailedBolusInfo.carbTime; + if (carbTime == 0) carbTime--; // better set 1 man back to prevent clash with insulin detailedBolusInfo.carbTime = 0; DetailedBolusInfoStorage.add(detailedBolusInfo); // will be picked up on reading history @@ -172,7 +205,7 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { t.isSMB = detailedBolusInfo.isSMB; boolean connectionOK = false; if (detailedBolusInfo.insulin > 0 || carbs > 0) - connectionOK = sExecutionService.bolus(detailedBolusInfo.insulin, (int) carbs, DateUtil.now() + carbTime * 60 * 1000, t); + connectionOK = sExecutionService.bolus(detailedBolusInfo.insulin, (int) carbs, DateUtil.now() + T.mins(carbTime).msecs(), t); PumpEnactResult result = new PumpEnactResult(); result.success = connectionOK && Math.abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep; result.bolusDelivered = t.insulin; @@ -266,8 +299,12 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { // Convert duration from minutes to hours if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Setting temp basal " + percentRate + "% for " + durationInMinutes + " mins (doLowTemp || doHighTemp)"); - // use special APS temp basal call ... 100+/15min .... 100-/30min - result = setHighTempBasalPercent(percentRate); + if (percentRate == 0 && durationInMinutes > 30) { + result = setTempBasalPercent(percentRate, durationInMinutes, profile, false); + } else { + // use special APS temp basal call ... 100+/15min .... 100-/30min + result = setHighTempBasalPercent(percentRate); + } if (!result.success) { log.error("setTempBasalAbsolute: Failed to set hightemp basal"); return result; @@ -389,4 +426,8 @@ public class DanaRv2Plugin extends AbstractDanaRPlugin { return sExecutionService.loadEvents(); } + @Override + public PumpEnactResult setUserOptions() { + return sExecutionService.setUserOptions(); + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MessageHashTable_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MessageHashTable_v2.java index cafba32239..8d1cd12be1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MessageHashTable_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MessageHashTable_v2.java @@ -66,6 +66,7 @@ public class MessageHashTable_v2 { put(new MsgSettingProfileRatiosAll()); // 0x320D CMD_SETTING_V_CIR_CF_VALUE put(new MsgSetSingleBasalProfile()); // 0x3302 CMD_SETTING_BASAL_INS_S put(new MsgSetBasalProfile()); // 0x3306 CMD_SETTING_BASAL_PROFILE_S + put(new MsgSetUserOptions()); // 0x330B CMD_SETTING_USER_OPTIONS_S put(new MsgSetActivateBasalProfile()); // 0x330C CMD_SETTING_PROFILE_NUMBER_S put(new MsgHistoryAllDone()); // 0x41F1 CMD_HISTORY_ALL_DONE put(new MsgHistoryAll()); // 0x41F2 CMD_HISTORY_ALL diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MsgHistoryEvents_v2.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MsgHistoryEvents_v2.java index 05c4087dcb..91c01ba38c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MsgHistoryEvents_v2.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/comm/MsgHistoryEvents_v2.java @@ -107,14 +107,14 @@ public class MsgHistoryEvents_v2 extends MessageBase { break; case DanaRPump.BOLUS: detailedBolusInfo.insulin = param1 / 100d; - boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + boolean newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT BOLUS (" + recordCode + ") " + datetime.toLocaleString() + " Bolus: " + (param1 / 100d) + "U Duration: " + param2 + "min"); DetailedBolusInfoStorage.remove(detailedBolusInfo.date); status = "BOLUS " + DateUtil.timeString(datetime); break; case DanaRPump.DUALBOLUS: detailedBolusInfo.insulin = param1 / 100d; - newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT DUALBOLUS (" + recordCode + ") " + datetime.toLocaleString() + " Bolus: " + (param1 / 100d) + "U Duration: " + param2 + "min"); DetailedBolusInfoStorage.remove(detailedBolusInfo.date); status = "DUALBOLUS " + DateUtil.timeString(datetime); @@ -157,7 +157,7 @@ public class MsgHistoryEvents_v2 extends MessageBase { emptyCarbsInfo.date = datetime.getTime(); emptyCarbsInfo.source = Source.PUMP; emptyCarbsInfo.pumpId = datetime.getTime(); - newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(emptyCarbsInfo); + newRecord = TreatmentsPlugin.getPlugin().addToHistoryTreatment(emptyCarbsInfo, false); log.debug((newRecord ? "**NEW** " : "") + "EVENT CARBS (" + recordCode + ") " + datetime.toLocaleString() + " Carbs: " + param1 + "g"); status = "CARBS " + DateUtil.timeString(datetime); break; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java index b9eb8ae655..1229f23f4e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java @@ -1,6 +1,7 @@ package info.nightscout.androidaps.plugins.PumpDanaRv2.services; import android.bluetooth.BluetoothDevice; +import android.content.Intent; import android.content.IntentFilter; import android.os.Binder; import android.os.SystemClock; @@ -18,13 +19,15 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventInitializationChanged; import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; +import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; @@ -42,6 +45,7 @@ import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetExtendedBolusStop import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStart; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTempBasalStop; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetTime; +import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSetUserOptions; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingActiveProfile; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingBasal; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingGlucose; @@ -51,6 +55,7 @@ import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingProfileRatios import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingProfileRatiosAll; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingPumpTime; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingShippingInfo; +import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgSettingUserOptions; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatus; import info.nightscout.androidaps.plugins.PumpDanaR.comm.MsgStatusBasic; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; @@ -63,12 +68,13 @@ import info.nightscout.androidaps.plugins.PumpDanaRv2.comm.MsgSetAPSTempBasalSta import info.nightscout.androidaps.plugins.PumpDanaRv2.comm.MsgSetHistoryEntry_v2; import info.nightscout.androidaps.plugins.PumpDanaRv2.comm.MsgStatusBolusExtended_v2; import info.nightscout.androidaps.plugins.PumpDanaRv2.comm.MsgStatusTempBasal_v2; +import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.queue.Callback; +import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; import info.nightscout.utils.T; -import info.nightscout.utils.ToastUtils; public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { @@ -120,7 +126,11 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { public void connect() { if (mDanaRPump.password != -1 && mDanaRPump.password != SP.getInt(R.string.key_danar_password, -1)) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.gs(R.string.wrongpumppassword), R.raw.error); + if(System.currentTimeMillis() > lastWrongPumpPassword + 30 * 1000) { + Notification notification = new Notification(Notification.WRONG_PUMP_PASSWORD, MainApp.gs(R.string.wrongpumppassword), Notification.URGENT); + notification.soundId = R.raw.error; + lastWrongPumpPassword = System.currentTimeMillis(); + } return; } @@ -183,8 +193,50 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingextendedbolusstatus))); mSerialIOThread.sendMessage(exStatusMsg); + mDanaRPump.lastConnection = System.currentTimeMillis(); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (profile != null && Math.abs(mDanaRPump.currentBasal - profile.getBasal()) >= pump.getPumpDescription().basalStep) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); + mSerialIOThread.sendMessage(new MsgSettingBasal()); + if (!pump.isThisProfileSet(profile) && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BASALPROFILE)) { + MainApp.bus().post(new EventProfileSwitchChange()); + } + } + + MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); + mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + long timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + if (Math.abs(timeDiff) > 3) { + if (Math.abs(timeDiff) > 60*60*1.5) { + log.debug("Pump time difference: " + timeDiff + " seconds - large difference"); + //If time-diff is very large, warn user until we can synchronize history readings properly + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.error); + i.putExtra("status", MainApp.gs(R.string.largetimediff)); + i.putExtra("title", MainApp.gs(R.string.largetimedifftitle)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + //deinitialize pump + mDanaRPump.lastConnection = 0; + MainApp.bus().post(new EventDanaRNewStatus()); + MainApp.bus().post(new EventInitializationChanged()); + return; + } else { + waitForWholeMinute(); // Dana can set only whole minute + // add 10sec to be sure we are over minute (will be cutted off anyway) + mSerialIOThread.sendMessage(new MsgSetTime(new Date(DateUtil.now() + T.secs(10).msecs()))); + mSerialIOThread.sendMessage(new MsgSettingPumpTime()); + timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; + log.debug("Pump time difference: " + timeDiff + " seconds"); + } + } + long now = System.currentTimeMillis(); - if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !MainApp.getSpecificPlugin(DanaRv2Plugin.class).isInitialized()) { + if (mDanaRPump.lastSettingsRead + 60 * 60 * 1000L < now || !pump.isInitialized()) { MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumpsettings))); mSerialIOThread.sendMessage(new MsgSettingShippingInfo()); mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); @@ -195,19 +247,8 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(new MsgSettingGlucose()); mSerialIOThread.sendMessage(new MsgSettingActiveProfile()); mSerialIOThread.sendMessage(new MsgSettingProfileRatios()); + mSerialIOThread.sendMessage(new MsgSettingUserOptions()); mSerialIOThread.sendMessage(new MsgSettingProfileRatiosAll()); - MainApp.bus().post(new EventPumpStatusChanged(MainApp.gs(R.string.gettingpumptime))); - mSerialIOThread.sendMessage(new MsgSettingPumpTime()); - long timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - if (Math.abs(timeDiff) > 3) { - waitForWholeMinute(); // Dana can set only whole minute - // add 10sec to be sure we are over minute (will be cutted off anyway) - mSerialIOThread.sendMessage(new MsgSetTime(new Date(DateUtil.now() + T.secs(10).msecs()))); - mSerialIOThread.sendMessage(new MsgSettingPumpTime()); - timeDiff = (mDanaRPump.pumpTime.getTime() - System.currentTimeMillis()) / 1000L; - log.debug("Pump time difference: " + timeDiff + " seconds"); - } mDanaRPump.lastSettingsRead = now; } @@ -218,9 +259,12 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { NSUpload.uploadDeviceStatus(); if (mDanaRPump.dailyTotalUnits > mDanaRPump.maxDailyTotalUnits * Constants.dailyLimitWarning) { log.debug("Approaching daily limit: " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits); - Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); - MainApp.bus().post(new EventNewNotification(reportFail)); - NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + if(System.currentTimeMillis() > lastApproachingDailyLimit + 30 * 60 * 1000) { + Notification reportFail = new Notification(Notification.APPROACHING_DAILY_LIMIT, MainApp.gs(R.string.approachingdailylimit), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(reportFail)); + NSUpload.uploadError(MainApp.gs(R.string.approachingdailylimit) + ": " + mDanaRPump.dailyTotalUnits + "/" + mDanaRPump.maxDailyTotalUnits + "U"); + lastApproachingDailyLimit = System.currentTimeMillis(); + } } } catch (Exception e) { log.error("Unhandled exception", e); @@ -327,7 +371,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(msg); MsgSetHistoryEntry_v2 msgSetHistoryEntry_v2 = new MsgSetHistoryEntry_v2(DanaRPump.CARBS, carbtime, carbs, 0); mSerialIOThread.sendMessage(msgSetHistoryEntry_v2); - lastHistoryFetched = carbtime - 60000; + lastHistoryFetched = Math.min(lastHistoryFetched, carbtime - T.mins(1).msecs()); } final long bolusStart = System.currentTimeMillis(); @@ -411,11 +455,19 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { mSerialIOThread.sendMessage(msg); MsgSetHistoryEntry_v2 msgSetHistoryEntry_v2 = new MsgSetHistoryEntry_v2(DanaRPump.CARBS, time, amount, 0); mSerialIOThread.sendMessage(msgSetHistoryEntry_v2); - lastHistoryFetched = time - 1; + lastHistoryFetched = Math.min(lastHistoryFetched, time - T.mins(1).msecs()); return true; } public PumpEnactResult loadEvents() { + + if(!MainApp.getSpecificPlugin(DanaRv2Plugin.class).isInitialized()){ + PumpEnactResult result = new PumpEnactResult().success(false); + result.comment = "pump not initialized"; + return result; + } + + if (!isConnected()) return new PumpEnactResult().success(false); SystemClock.sleep(300); @@ -433,7 +485,7 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { } SystemClock.sleep(200); if (MsgHistoryEvents_v2.lastEventTimeLoaded != 0) - lastHistoryFetched = MsgHistoryEvents_v2.lastEventTimeLoaded - 45 * 60 * 1000L; //always load last 45 min; + lastHistoryFetched = MsgHistoryEvents_v2.lastEventTimeLoaded - T.mins(1).msecs(); else lastHistoryFetched = 0; mDanaRPump.lastConnection = System.currentTimeMillis(); @@ -464,4 +516,15 @@ public class DanaRv2ExecutionService extends AbstractDanaRExecutionService { SystemClock.sleep(Math.min(timeToWholeMinute, 100)); } } + + public PumpEnactResult setUserOptions() { + if (!isConnected()) + return new PumpEnactResult().success(false); + SystemClock.sleep(300); + MsgSetUserOptions msg = new MsgSetUserOptions(); + mSerialIOThread.sendMessage(msg); + SystemClock.sleep(200); + return new PumpEnactResult().success(!msg.failed); + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Cstatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Cstatus.java deleted file mode 100644 index 53c0cf798f..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Cstatus.java +++ /dev/null @@ -1,20 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpInsight; - -/** - * Created by jamorham on 25/01/2018. - * - * Async command status - * - */ -enum Cstatus { - UNKNOWN, - PENDING, - SUCCESS, - FAILURE, - TIMEOUT; - - boolean success() { - return this == SUCCESS; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightAsyncAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightAsyncAdapter.java deleted file mode 100644 index 3759a3f721..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightAsyncAdapter.java +++ /dev/null @@ -1,95 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpInsight; - -import android.os.PowerManager; - -import com.squareup.otto.Subscribe; - -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightCallback; - -import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.getWakeLock; -import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.msSince; -import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.releaseWakeLock; -import static info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers.tsl; - -/** - * Created by jamorham on 25/01/2018. - * - * Asynchronous adapter - * - */ - -public class InsightAsyncAdapter { - - private final ConcurrentHashMap commandResults = new ConcurrentHashMap<>(); - - InsightAsyncAdapter() { - MainApp.bus().register(this); - } - - // just log during debugging - private static void log(String msg) { - android.util.Log.e("INSIGHTPUMPASYNC", msg); - } - - @Subscribe - public void onStatusEvent(final EventInsightCallback ev) { - log("Received callback event: " + ev.toString()); - commandResults.put(ev.request_uuid, ev); - } - - // poll command result - private Cstatus checkCommandResult(UUID uuid) { - if (uuid == null) return Cstatus.FAILURE; - if (commandResults.containsKey(uuid)) { - if (commandResults.get(uuid).success) { - return Cstatus.SUCCESS; - } else { - return Cstatus.FAILURE; - } - } else { - return Cstatus.PENDING; - } - } - - // blocking call to wait for result callback - private Cstatus busyWaitForCommandInternal(final UUID uuid, long wait_time) { - final PowerManager.WakeLock wl = getWakeLock("insight-wait-cmd", 60000); - try { - log("busy wait for command " + uuid); - if (uuid == null) return Cstatus.FAILURE; - final long start_time = tsl(); - Cstatus status = checkCommandResult(uuid); - while ((status == Cstatus.PENDING) && msSince(start_time) < wait_time) { - //log("command result waiting"); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - log("Got interrupted exception! " + e); - } - status = checkCommandResult(uuid); - } - if (status == Cstatus.PENDING) { - return Cstatus.TIMEOUT; - } else { - return status; - } - } finally { - releaseWakeLock(wl); - } - } - - // wait for and then package result, cleanup and return - Mstatus busyWaitForCommandResult(final UUID uuid, long wait_time) { - final Mstatus mstatus = new Mstatus(); - mstatus.cstatus = busyWaitForCommandInternal(uuid, wait_time); - mstatus.event = commandResults.get(uuid); - commandResults.remove(uuid); - return mstatus; - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java index 824a449f38..5fd8e25f26 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/InsightPlugin.java @@ -1,5 +1,9 @@ package info.nightscout.androidaps.plugins.PumpInsight; +import android.content.DialogInterface; +import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; + import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -9,7 +13,6 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.UUID; import info.nightscout.androidaps.BuildConfig; import info.nightscout.androidaps.Config; @@ -21,7 +24,6 @@ import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.ConstraintsInterface; import info.nightscout.androidaps.interfaces.PluginBase; @@ -29,11 +31,13 @@ import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProgress; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; -import info.nightscout.androidaps.plugins.PumpInsight.connector.CancelBolusTaskRunner; +import info.nightscout.androidaps.plugins.PumpInsight.connector.CancelBolusSilentlyTaskRunner; +import info.nightscout.androidaps.plugins.PumpInsight.connector.CancelTBRSilentlyTaskRunner; import info.nightscout.androidaps.plugins.PumpInsight.connector.Connector; import info.nightscout.androidaps.plugins.PumpInsight.connector.SetTBRTaskRunner; import info.nightscout.androidaps.plugins.PumpInsight.connector.StatusTaskRunner; @@ -44,18 +48,19 @@ import info.nightscout.androidaps.plugins.PumpInsight.history.HistoryReceiver; import info.nightscout.androidaps.plugins.PumpInsight.history.LiveHistory; import info.nightscout.androidaps.plugins.PumpInsight.utils.Helpers; import info.nightscout.androidaps.plugins.PumpInsight.utils.StatusItem; +import info.nightscout.androidaps.plugins.Treatments.Treatment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; import sugar.free.sightparser.applayer.descriptors.ActiveBolus; import sugar.free.sightparser.applayer.descriptors.ActiveBolusType; +import sugar.free.sightparser.applayer.descriptors.MessagePriority; import sugar.free.sightparser.applayer.descriptors.PumpStatus; import sugar.free.sightparser.applayer.descriptors.configuration_blocks.BRProfileBlock; import sugar.free.sightparser.applayer.messages.AppLayerMessage; import sugar.free.sightparser.applayer.messages.remote_control.BolusMessage; import sugar.free.sightparser.applayer.messages.remote_control.CancelBolusMessage; -import sugar.free.sightparser.applayer.messages.remote_control.CancelTBRMessage; import sugar.free.sightparser.applayer.messages.remote_control.ExtendedBolusMessage; import sugar.free.sightparser.applayer.messages.remote_control.StandardBolusMessage; import sugar.free.sightparser.applayer.messages.status.ActiveBolusesMessage; @@ -94,7 +99,6 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai private static boolean initialized = false; private static volatile boolean update_pending = false; private static Logger log = LoggerFactory.getLogger(InsightPlugin.class); - private final InsightAsyncAdapter async = new InsightAsyncAdapter(); private StatusTaskRunner.Result statusResult; private long statusResultTime = -1; private Date lastDataTime = new Date(0); @@ -112,13 +116,14 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai .pluginName(R.string.insightpump) .shortName(R.string.insightpump_shortname) .preferencesId(R.xml.pref_insightpump) + .description(R.string.description_pump_insight) ); log("InsightPlugin instantiated"); pumpDescription.isBolusCapable = true; - pumpDescription.bolusStep = 0.05d; // specification says 0.05U up to 2U then 0.1U @ 2-5U 0.2U @ 10-20U 0.5U 10-20U (are these just UI restrictions?) + pumpDescription.bolusStep = 0.01d; // specification says 0.05U up to 2U then 0.1U @ 2-5U 0.2U @ 10-20U 0.5U 10-20U (are these just UI restrictions? Yes, they are!) pumpDescription.isExtendedBolusCapable = true; - pumpDescription.extendedBolusStep = 0.05d; // specification probably same as above + pumpDescription.extendedBolusStep = 0.01d; // specification probably same as above pumpDescription.extendedBolusDurationStep = 15; // 15 minutes up to 24 hours pumpDescription.extendedBolusMaxDuration = 24 * 60; @@ -138,6 +143,7 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai pumpDescription.is30minBasalRatesCapable = true; pumpDescription.basalStep = 0.01d; pumpDescription.basalMinimumRate = 0.02d; + pumpDescription.basalMaximumRate = 25d; pumpDescription.isRefillingCapable = true; @@ -191,7 +197,7 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai @Override public boolean isFakingTempsByExtendedBoluses() { - return false; + return true; } @Override @@ -201,6 +207,31 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai return result; } + @Override + public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) { + boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false); + if (allowHardwarePump || context == null){ + pluginSwitcher.invoke(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setMessage(R.string.allow_hardware_pump_text) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.invoke(); + SP.putBoolean("allow_hardware_pump", true); + log.debug("First time HW pump allowed!"); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + pluginSwitcher.cancel(); + log.debug("User does not allow switching to HW pump!"); + } + }); + builder.create().show(); + } + } + @Override public boolean isInitialized() { return initialized; @@ -287,17 +318,15 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai log("getPumpStatus"); if (Connector.get().isPumpConnected()) { log("is connected.. requesting status"); - final UUID uuid = aSyncTaskRunner(new StatusTaskRunner(connector.getServiceConnector()), "Status"); - Mstatus mstatus = async.busyWaitForCommandResult(uuid, BUSY_WAIT_TIME); - if (mstatus.success()) { + try { + setStatusResult(fetchTaskRunner(new StatusTaskRunner(connector.getServiceConnector()), StatusTaskRunner.Result.class)); log("GOT STATUS RESULT!!! PARTY WOOHOO!!!"); - setStatusResult((StatusTaskRunner.Result) mstatus.getResponseObject()); statusResultTime = Helpers.tsl(); processStatusResult(); updateGui(); connector.requestHistoryReSync(); connector.requestHistorySync(); - } else { + } catch (Exception e) { log("StatusTaskRunner wasn't successful."); if (connector.getServiceConnector().isConnectedToService() && connector.getServiceConnector().getStatus() != Status.CONNECTED) { if (Helpers.ratelimit("insight-reconnect", 2)) { @@ -313,6 +342,8 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai public void setStatusResult(StatusTaskRunner.Result result) { this.statusResult = result; + this.pumpDescription.basalMinimumRate = result.minimumBasalAmount; + this.pumpDescription.basalMaximumRate = result.maximumBasalAmount; } @Override @@ -335,9 +366,8 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai profileBlocks.add(new BRProfileBlock.ProfileBlock((((nextValue != null ? nextValue.timeAsSeconds : 24 * 60 * 60) - basalValue.timeAsSeconds) / 60), Helpers.roundDouble(basalValue.value, 2))); log("setNewBasalProfile: " + basalValue.value + " for " + Integer.toString(((nextValue != null ? nextValue.timeAsSeconds : 24 * 60 * 60) - basalValue.timeAsSeconds) / 60)); } - final UUID uuid = aSyncTaskRunner(new WriteBasalProfileTaskRunner(connector.getServiceConnector(), profileBlocks), "Write basal profile"); - final Mstatus ms = async.busyWaitForCommandResult(uuid, BUSY_WAIT_TIME); - if (ms.success()) { + try { + fetchTaskRunner(new WriteBasalProfileTaskRunner(connector.getServiceConnector(), profileBlocks)); MainApp.bus().post(new EventDismissNotification(Notification.FAILED_UDPATE_PROFILE)); Notification notification = new Notification(Notification.PROFILE_SET_OK, MainApp.gs(R.string.profile_set_ok), Notification.INFO, 60); MainApp.bus().post(new EventNewNotification(notification)); @@ -345,7 +375,7 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai result.enacted = true; result.comment = "OK"; this.profileBlocks = profileBlocks; - } else { + } catch (Exception e) { Notification notification = new Notification(Notification.FAILED_UDPATE_PROFILE, MainApp.gs(R.string.failedupdatebasalprofile), Notification.URGENT); MainApp.bus().post(new EventNewNotification(notification)); result.comment = MainApp.gs(R.string.failedupdatebasalprofile); @@ -403,17 +433,13 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai // is there an insulin component to the treatment? if (detailedBolusInfo.insulin > 0) { - final UUID cmd = deliverBolus(detailedBolusInfo.insulin); // actually request delivery - if (cmd == null) { + try { + bolusId = deliverBolus(detailedBolusInfo.insulin); + result.success = true; + detailedBolusInfo.pumpId = getRecordUniqueID(bolusId); + } catch (Exception e) { return pumpEnactFailure(); } - final Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - - result.success = ms.success(); - if (ms.success()) { - detailedBolusInfo.pumpId = getRecordUniqueID(ms.getResponseID()); - bolusId = ms.getResponseID(); - } } else { result.success = true; // always true with carb only treatments } @@ -429,7 +455,7 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai bolusingEvent.bolusId = bolusId; bolusingEvent.percent = 0; MainApp.bus().post(bolusingEvent); - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); } else { log.debug("Failure to deliver treatment"); } @@ -440,20 +466,11 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai updateGui(); connector.tryToGetPumpStatusAgain(); - connector.requestHistorySync(30000); - if (result.success) while (true) { try { - Thread.sleep(200); - } catch (InterruptedException e) { - e.printStackTrace(); - break; - } - final UUID uuid = aSyncSingleCommand(new ActiveBolusesMessage(), "Active boluses"); - Mstatus mstatus = async.busyWaitForCommandResult(uuid, BUSY_WAIT_TIME); - if (mstatus.success()) { + Thread.sleep(500); final EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance(); - ActiveBolusesMessage activeBolusesMessage = (ActiveBolusesMessage) mstatus.getResponseObject(); + ActiveBolusesMessage activeBolusesMessage = fetchSingleMessage(new ActiveBolusesMessage(), ActiveBolusesMessage.class); ActiveBolus activeBolus = null; if (activeBolusesMessage.getBolus1() != null && activeBolusesMessage.getBolus1().getBolusID() == bolusingEvent.bolusId) activeBolus = activeBolusesMessage.getBolus1(); @@ -463,87 +480,60 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai activeBolus = activeBolusesMessage.getBolus3(); if (activeBolus == null) break; else { + int percentBefore = bolusingEvent.percent; bolusingEvent.percent = (int) (100D / activeBolus.getInitialAmount() * (activeBolus.getInitialAmount() - activeBolus.getLeftoverAmount())); bolusingEvent.status = String.format(MainApp.gs(R.string.bolusdelivering), activeBolus.getInitialAmount() - activeBolus.getLeftoverAmount()); - MainApp.bus().post(bolusingEvent); + if (percentBefore != bolusingEvent.percent) MainApp.bus().post(bolusingEvent); } - } else break; + } catch (Exception e) { + break; + } } + + connector.requestHistorySync(2000); return result; } @Override public void stopBolusDelivering() { CancelBolusMessage cancelBolusMessage = new CancelBolusMessage(); + cancelBolusMessage.setMessagePriority(MessagePriority.HIGHEST); cancelBolusMessage.setBolusId(EventOverviewBolusProgress.getInstance().bolusId); - final UUID cmd = aSyncSingleCommand(cancelBolusMessage, "Cancel standard bolus"); - - if (cmd == null) { - return; + try { + fetchSingleMessage(cancelBolusMessage); + } catch (Exception e) { } - - final Mstatus cs = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - log("Got command status: " + cs); } // Temporary Basals @Override public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile, boolean enforceNew) { - absoluteRate = Helpers.roundDouble(absoluteRate, 3); log("Set TBR absolute: " + absoluteRate); - final double base_basal = getBaseBasalRate(); - if (base_basal == 0) { + if (getBaseBasalRate() == 0) { log("Base basal rate appears to be zero!"); return pumpEnactFailure(); } - int percent_amount = (int) Math.round(100d / base_basal * absoluteRate); - log("Calculated requested rate: " + absoluteRate + " base rate: " + base_basal + " percentage: " + percent_amount + "%"); - percent_amount = (int) Math.round(((double) percent_amount) / 10d) * 10; - log("Calculated final rate: " + percent_amount + "%"); - - if (percent_amount == 100) { - return cancelTempBasal(false); - } - - if (percent_amount > 250) percent_amount = 250; - - - final SetTBRTaskRunner task = new SetTBRTaskRunner(connector.getServiceConnector(), percent_amount, durationInMinutes); - final UUID cmd = aSyncTaskRunner(task, "Set TBR abs: " + absoluteRate + " " + durationInMinutes + "m"); - - if (cmd == null) { + double percent = 100D / getBaseBasalRate() * absoluteRate; + log("Calculated requested rate: " + absoluteRate + " base rate: " + getBaseBasalRate() + " percentage: " + percent + "%"); + try { + if (percent > 250) { + log ("Calculated rate is above 250%, switching to emulation using extended boluses"); + cancelTempBasal(true); + if (!setExtendedBolus((absoluteRate - getBaseBasalRate()) / 60D * ((double) durationInMinutes), durationInMinutes).success) { + //Fallback to TBR if setting an extended bolus didn't work + log ("Setting an extended bolus didn't work, falling back to normal TBR"); + return setTempBasalPercent((int) percent, durationInMinutes, profile, true); + } + return new PumpEnactResult().success(true).enacted(true).absolute(absoluteRate).duration(durationInMinutes); + } else { + log ("Calculated rate is below or equal to 250%, using normal TBRs"); + cancelExtendedBolus(); + return setTempBasalPercent((int) percent, durationInMinutes, profile, true); + } + } catch (Exception e) { return pumpEnactFailure(); } - - Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - log("Got command status: " + ms); - - PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).isPercent(true).duration(durationInMinutes); - pumpEnactResult.percent = percent_amount; - pumpEnactResult.success = ms.success(); - pumpEnactResult.comment = ms.getCommandComment(); - - - if (pumpEnactResult.success) { - // create log entry - final TemporaryBasal tempBasal = new TemporaryBasal() - .date(System.currentTimeMillis()) - .percent(percent_amount) - .duration(durationInMinutes) - .source(Source.USER); - TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal); - } - - if (Config.logPumpComm) - log.debug("Setting temp basal absolute: " + pumpEnactResult.success); - - updateGui(); - - connector.requestHistorySync(5000); - connector.tryToGetPumpStatusAgain(); - - return pumpEnactResult; } @@ -555,42 +545,24 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai if (percent == 100) { // This would cause a cancel if a tbr is in progress so treat as a cancel return cancelTempBasal(false); - } + } else if (percent > 250) percent = 250; - - final UUID cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), percent, durationInMinutes), "Set TBR " + percent + "%" + " " + durationInMinutes + "m"); - - if (cmd == null) { - return pumpEnactFailure(); - } - - final Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - log("Got command status: " + ms); - - PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).isPercent(true).duration(durationInMinutes); - pumpEnactResult.percent = percent; - pumpEnactResult.success = ms.success(); - pumpEnactResult.comment = ms.getCommandComment(); - - if (pumpEnactResult.success) { - // create log entry + try { + fetchTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), percent, durationInMinutes)); final TemporaryBasal tempBasal = new TemporaryBasal() .date(System.currentTimeMillis()) .percent(percent) .duration(durationInMinutes) - .source(Source.USER); // TODO check this is correct + .source(Source.USER); TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempBasal); + updateGui(); + if (Config.logPumpComm) log.debug("Set temp basal " + percent + "% for " + durationInMinutes + "m"); + connector.requestHistorySync(5000); + connector.tryToGetPumpStatusAgain(); + return new PumpEnactResult().success(true).enacted(true).percent(percent); + } catch (Exception e) { + return pumpEnactFailure(); } - - updateGui(); - - if (Config.logPumpComm) - log.debug("Set temp basal " + percent + "% for " + durationInMinutes + "m"); - - connector.requestHistorySync(5000); - connector.tryToGetPumpStatusAgain(); - - return pumpEnactResult; } @@ -598,35 +570,24 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai public PumpEnactResult cancelTempBasal(boolean enforceNew) { log("Cancel TBR"); - - fauxTBRcancel = !SP.getBoolean("insight_real_tbr_cancel", false); - - final UUID cmd; - - if (fauxTBRcancel) { - cmd = aSyncTaskRunner(new SetTBRTaskRunner(connector.getServiceConnector(), 100, 1), "Faux Cancel TBR - setting " + "90%" + " 1m"); - } else { - cmd = aSyncSingleCommand(new CancelTBRMessage(), "Cancel Temp Basal"); - } - if (cmd == null) { + try { + cancelExtendedBolus(); + realTBRCancel(); + updateGui(); + if (Config.logPumpComm) log.debug("Canceling temp basal"); + connector.requestHistorySync(5000); + connector.tryToGetPumpStatusAgain(); + return new PumpEnactResult().success(true).enacted(true).isTempCancel(true); + } catch (Exception e) { return pumpEnactFailure(); } + } - // TODO isn't conditional on one apparently being in progress only the history change - final Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - - if (TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { + private void realTBRCancel() throws Exception { + if (fetchTaskRunner(new CancelTBRSilentlyTaskRunner(connector.getServiceConnector()), Boolean.class) && TreatmentsPlugin.getPlugin().isTempBasalInProgress()) { TemporaryBasal tempStop = new TemporaryBasal().date(System.currentTimeMillis()).source(Source.USER); TreatmentsPlugin.getPlugin().addToHistoryTempBasal(tempStop); } - updateGui(); - if (Config.logPumpComm) - log.debug("Canceling temp basal: "); // TODO get more info - - connector.requestHistorySync(5000); - connector.tryToGetPumpStatusAgain(); - - return new PumpEnactResult().success(ms.success()).enacted(true).isTempCancel(true); } @@ -635,88 +596,60 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai @Override public PumpEnactResult setExtendedBolus(Double insulin, Integer durationInMinutes) { log("Set Extended bolus " + insulin + " " + durationInMinutes); - ExtendedBolusMessage extendedBolusMessage = new ExtendedBolusMessage(); - extendedBolusMessage.setAmount(insulin); - extendedBolusMessage.setDuration(durationInMinutes); - final UUID cmd = aSyncSingleCommand(extendedBolusMessage, "Extended bolus U" + insulin + " mins:" + durationInMinutes); - if (cmd == null) { - return pumpEnactFailure(); - } - - final Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - log("Got command status: " + ms); - - PumpEnactResult pumpEnactResult = new PumpEnactResult().enacted(true).bolusDelivered(insulin).duration(durationInMinutes); - pumpEnactResult.success = ms.success(); - pumpEnactResult.comment = ms.getCommandComment(); - - if (pumpEnactResult.success) { - // create log entry + try { + ExtendedBolusMessage extendedBolusMessage = new ExtendedBolusMessage(); + extendedBolusMessage.setAmount(insulin); + extendedBolusMessage.setDuration(durationInMinutes); + BolusMessage bolusMessage = fetchSingleMessage(extendedBolusMessage, BolusMessage.class); final ExtendedBolus extendedBolus = new ExtendedBolus(); extendedBolus.date = System.currentTimeMillis(); extendedBolus.insulin = insulin; extendedBolus.durationInMinutes = durationInMinutes; extendedBolus.source = Source.USER; - extendedBolus.pumpId = getRecordUniqueID(ms.getResponseID()); + extendedBolus.pumpId = getRecordUniqueID(bolusMessage.getBolusId()); TreatmentsPlugin.getPlugin().addToHistoryExtendedBolus(extendedBolus); + updateGui(); + connector.requestHistorySync(30000); + connector.tryToGetPumpStatusAgain(); + if (Config.logPumpComm) + log.debug("Setting extended bolus: " + insulin + " mins:" + durationInMinutes); + return new PumpEnactResult().success(true).enacted(true).duration(durationInMinutes).bolusDelivered(insulin); + } catch (Exception e) { + return pumpEnactFailure(); } - - if (Config.logPumpComm) - log.debug("Setting extended bolus: " + insulin + " mins:" + durationInMinutes + " " + pumpEnactResult.comment); - - updateGui(); - - connector.requestHistorySync(30000); - connector.tryToGetPumpStatusAgain(); - - return pumpEnactResult; } @Override public PumpEnactResult cancelExtendedBolus() { - log("Cancel Extended bolus"); - // TODO note always sends cancel to pump but only changes history if present + Integer bolusId = null; - final UUID cmd = aSyncTaskRunner(new CancelBolusTaskRunner(connector.getServiceConnector(), ActiveBolusType.EXTENDED), "Cancel extended bolus"); - - if (cmd == null) { + try { + bolusId = fetchTaskRunner(new CancelBolusSilentlyTaskRunner(connector.getServiceConnector(), ActiveBolusType.EXTENDED), Integer.class); + if (TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { + ExtendedBolus exStop = new ExtendedBolus(System.currentTimeMillis()); + exStop.source = Source.USER; + TreatmentsPlugin.getPlugin().addToHistoryExtendedBolus(exStop); + } + if (Config.logPumpComm) log.debug("Cancel extended bolus:"); + if (bolusId != null) connector.requestHistorySync(5000); + connector.tryToGetPumpStatusAgain(); + updateGui(); + return new PumpEnactResult().success(true).enacted(bolusId != null); + } catch (Exception e) { return pumpEnactFailure(); } - - final Mstatus ms = async.busyWaitForCommandResult(cmd, BUSY_WAIT_TIME); - - if (TreatmentsPlugin.getPlugin().isInHistoryExtendedBoluslInProgress()) { - ExtendedBolus exStop = new ExtendedBolus(System.currentTimeMillis()); - exStop.source = Source.USER; - TreatmentsPlugin.getPlugin().addToHistoryExtendedBolus(exStop); - } - - if (Config.logPumpComm) - log.debug("Cancel extended bolus:"); - - updateGui(); - - connector.requestHistorySync(5000); - connector.tryToGetPumpStatusAgain(); - - return new PumpEnactResult().success(ms.success()).enacted(true); } - private synchronized UUID deliverBolus(double bolusValue) { + private int deliverBolus(double bolusValue) throws Exception { log("DeliverBolus: " + bolusValue); - if (bolusValue == 0) return null; - if (bolusValue < 0) return null; - - // TODO check limits here or they already occur via a previous constraint interface? - final StandardBolusMessage message = new StandardBolusMessage(); message.setAmount(bolusValue); - return aSyncSingleCommand(message, "Deliver Bolus " + bolusValue); + return fetchSingleMessage(message, BolusMessage.class).getBolusId(); } @Override @@ -931,82 +864,34 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai } } - // Utility - - private synchronized UUID aSyncSingleCommand(final AppLayerMessage msg, final String name) { - // if (!isConnected()) return false; - //if (isBusy()) return false; - log("asyncSinglecommand called: " + name); - final EventInsightCallback event = new EventInsightCallback(); - new Thread() { - @Override - public void run() { - log("asyncSingleCommand thread"); - final SingleMessageTaskRunner singleMessageTaskRunner = new SingleMessageTaskRunner(connector.getServiceConnector(), msg); - try { - singleMessageTaskRunner.fetch(new TaskRunner.ResultCallback() { - @Override - public void onResult(Object o) { - lastDataTime = new Date(); - log(name + " success"); - event.response_object = o; - if (o instanceof BolusMessage) { - event.response_id = ((BolusMessage) o).getBolusId(); - } - event.success = true; - pushCallbackEvent(event); - } - - @Override - public void onError(Exception e) { - log(name + " error"); - event.message = e.getMessage(); - pushCallbackEvent(event); - } - }); - - } catch (Exception e) { - log("EXCEPTION" + e.toString()); - } - } - }.start(); - return event.request_uuid; + private void fetchTaskRunner(TaskRunner taskRunner) throws Exception { + fetchTaskRunner(taskRunner, Object.class); } - private synchronized UUID aSyncTaskRunner(final TaskRunner task, final String name) { - // if (!isConnected()) return false; - //if (isBusy()) return false; - log("asyncTaskRunner called: " + name); - final EventInsightCallback event = new EventInsightCallback(); - new Thread() { - @Override - public void run() { - log("asyncTaskRunner thread"); - try { - task.fetch(new TaskRunner.ResultCallback() { - @Override - public void onResult(Object o) { - lastDataTime = new Date(); - log(name + " success"); - event.response_object = o; - event.success = true; - pushCallbackEvent(event); - } + private void fetchSingleMessage(AppLayerMessage message) throws Exception { + fetchSingleMessage(message, AppLayerMessage.class); + } - @Override - public void onError(Exception e) { - log(name + " error"); - event.message = e.getMessage(); - pushCallbackEvent(event); - } - }); + private T fetchTaskRunner(TaskRunner taskRunner, Class resultType) throws Exception { + try { + T result = (T) taskRunner.fetchAndWaitUsingLatch(BUSY_WAIT_TIME); + lastDataTime = new Date(); + return result; + } catch (Exception e) { + log("Error while fetching " + taskRunner.getClass().getSimpleName() + ": " + e.getClass().getSimpleName()); + throw e; + } + } - } catch (Exception e) { - log("EXCEPTION" + e.toString()); - } - } - }.start(); - return event.request_uuid; + private T fetchSingleMessage(AppLayerMessage message, Class resultType) throws Exception { + try { + T result = (T) new SingleMessageTaskRunner(connector.getServiceConnector(), message).fetchAndWaitUsingLatch(BUSY_WAIT_TIME); + lastDataTime = new Date(); + return result; + } catch (Exception e) { + log("Error while fetching " + message.getClass().getSimpleName() + ": " + e.getClass().getSimpleName()); + throw e; + } } @@ -1016,14 +901,6 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai // Constraints - @Override - public Constraint applyBasalConstraints(Constraint absoluteRate, Profile profile) { - if (statusResult != null) { - absoluteRate.setIfSmaller(statusResult.maximumBasalAmount, String.format(MainApp.gs(R.string.limitingbasalratio), statusResult.maximumBasalAmount, MainApp.gs(R.string.pumplimit)), this); - } - return absoluteRate; - } - @Override public Constraint applyBasalPercentConstraints(Constraint percentRate, Profile profile) { percentRate.setIfGreater(0, String.format(MainApp.gs(R.string.limitingpercentrate), 0, MainApp.gs(R.string.itmustbepositivevalue)), this); @@ -1034,8 +911,17 @@ public class InsightPlugin extends PluginBase implements PumpInterface, Constrai @Override public Constraint applyBolusConstraints(Constraint insulin) { - if (statusResult != null) + if (statusResult != null) { insulin.setIfSmaller(statusResult.maximumBolusAmount, String.format(MainApp.gs(R.string.limitingbolus), statusResult.maximumBolusAmount, MainApp.gs(R.string.pumplimit)), this); + if (insulin.value() < statusResult.minimumBolusAmount) { + + //TODO: Add function to Constraints or use different approach + // This only works if the interface of the InsightPlugin is called last. + // If not, another contraint could theoretically set the value between 0 and minimumBolusAmount + + insulin.set(0d, String.format(MainApp.gs(R.string.limitingbolus), statusResult.minimumBolusAmount, MainApp.gs(R.string.pumplimit)), this); + } + } return insulin; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Mstatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Mstatus.java deleted file mode 100644 index 8797325f7c..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/Mstatus.java +++ /dev/null @@ -1,50 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpInsight; - -import info.nightscout.androidaps.plugins.PumpInsight.events.EventInsightCallback; - -/** - * Created by jamorham on 01/02/2018. - * - * Encapsulates results from commands - */ - -class Mstatus { - - Cstatus cstatus = Cstatus.UNKNOWN; - EventInsightCallback event; - - // comment field preparation for results - String getCommandComment() { - if (success()) { - return "OK"; - } else { - return (event == null) ? "EVENT DATA IS NULL - ERROR OR FIREWALL ENABLED?" : event.message; - } - } - - boolean success() { - return cstatus.success(); - } - - int getResponseID() { - if (success()) { - return event.response_id; - } else { - return -2; // invalid - } - } - - Object getResponseObject() { - if (success()) { - return event.response_object; - } else { - return null; - } - } - - @Override - public String toString() { - return cstatus + " " + event; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusSilentlyTaskRunner.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusSilentlyTaskRunner.java new file mode 100644 index 0000000000..7b3108602e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusSilentlyTaskRunner.java @@ -0,0 +1,66 @@ +package info.nightscout.androidaps.plugins.PumpInsight.connector; + +import sugar.free.sightparser.applayer.descriptors.ActiveBolusType; +import sugar.free.sightparser.applayer.descriptors.MessagePriority; +import sugar.free.sightparser.applayer.descriptors.alerts.Warning38BolusCancelled; +import sugar.free.sightparser.applayer.messages.AppLayerMessage; +import sugar.free.sightparser.applayer.messages.remote_control.CancelBolusMessage; +import sugar.free.sightparser.applayer.messages.remote_control.DismissAlertMessage; +import sugar.free.sightparser.applayer.messages.status.ActiveAlertMessage; +import sugar.free.sightparser.applayer.messages.status.ActiveBolusesMessage; +import sugar.free.sightparser.handling.SightServiceConnector; +import sugar.free.sightparser.handling.TaskRunner; + +// by Tebbe Ubben + +public class CancelBolusSilentlyTaskRunner extends TaskRunner { + + private ActiveBolusType bolusType; + private long cancelledAt; + private int bolusId; + + public CancelBolusSilentlyTaskRunner(SightServiceConnector serviceConnector, ActiveBolusType bolusType) { + super(serviceConnector); + this.bolusType = bolusType; + } + + @Override + protected AppLayerMessage run(AppLayerMessage message) throws Exception { + if (message == null) return new ActiveBolusesMessage(); + else if (message instanceof ActiveBolusesMessage) { + ActiveBolusesMessage bolusesMessage = (ActiveBolusesMessage) message; + CancelBolusMessage cancelBolusMessage = new CancelBolusMessage(); + if (bolusesMessage.getBolus1().getBolusType() == bolusType) + bolusId = bolusesMessage.getBolus1().getBolusID(); + else if (bolusesMessage.getBolus2().getBolusType() == bolusType) + bolusId = bolusesMessage.getBolus2().getBolusID(); + else if (bolusesMessage.getBolus3().getBolusType() == bolusType) + bolusId = bolusesMessage.getBolus3().getBolusID(); + else finish(null); + cancelBolusMessage.setBolusId(bolusId); + return cancelBolusMessage; + } else if (message instanceof CancelBolusMessage) { + cancelledAt = System.currentTimeMillis(); + ActiveAlertMessage activeAlertMessage = new ActiveAlertMessage(); + activeAlertMessage.setMessagePriority(MessagePriority.HIGHER); + return activeAlertMessage; + } else if (message instanceof ActiveAlertMessage) { + ActiveAlertMessage activeAlertMessage = (ActiveAlertMessage) message; + if (activeAlertMessage.getAlert() == null) { + if (System.currentTimeMillis() - cancelledAt >= 10000) finish(bolusId); + else { + ActiveAlertMessage activeAlertMessage2 = new ActiveAlertMessage(); + activeAlertMessage2.setMessagePriority(MessagePriority.HIGHER); + return activeAlertMessage2; + } + } else if (!(activeAlertMessage.getAlert() instanceof Warning38BolusCancelled)) finish(bolusId); + else { + DismissAlertMessage dismissAlertMessage = new DismissAlertMessage(); + dismissAlertMessage.setAlertID(activeAlertMessage.getAlertID()); + dismissAlertMessage.setMessagePriority(MessagePriority.HIGHER); + return dismissAlertMessage; + } + } else if (message instanceof DismissAlertMessage) finish(bolusId); + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusTaskRunner.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusTaskRunner.java deleted file mode 100644 index f350b80851..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelBolusTaskRunner.java +++ /dev/null @@ -1,38 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpInsight.connector; - -import sugar.free.sightparser.applayer.messages.AppLayerMessage; -import sugar.free.sightparser.applayer.descriptors.ActiveBolusType; -import sugar.free.sightparser.applayer.messages.remote_control.CancelBolusMessage; -import sugar.free.sightparser.applayer.messages.status.ActiveBolusesMessage; -import sugar.free.sightparser.handling.SightServiceConnector; -import sugar.free.sightparser.handling.TaskRunner; - -// by Tebbe Ubben - -public class CancelBolusTaskRunner extends TaskRunner { - - private ActiveBolusType bolusType; - - public CancelBolusTaskRunner(SightServiceConnector serviceConnector, ActiveBolusType bolusType) { - super(serviceConnector); - this.bolusType = bolusType; - } - - @Override - protected AppLayerMessage run(AppLayerMessage message) throws Exception { - if (message == null) return new ActiveBolusesMessage(); - else if (message instanceof ActiveBolusesMessage) { - ActiveBolusesMessage bolusesMessage = (ActiveBolusesMessage) message; - CancelBolusMessage cancelBolusMessage = new CancelBolusMessage(); - if (bolusesMessage.getBolus1().getBolusType() == bolusType) - cancelBolusMessage.setBolusId(bolusesMessage.getBolus1().getBolusID()); - else if (bolusesMessage.getBolus2().getBolusType() == bolusType) - cancelBolusMessage.setBolusId(bolusesMessage.getBolus2().getBolusID()); - else if (bolusesMessage.getBolus3().getBolusType() == bolusType) - cancelBolusMessage.setBolusId(bolusesMessage.getBolus3().getBolusID()); - else finish(null); - return cancelBolusMessage; - } else if (message instanceof CancelBolusMessage) finish(null); - return null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelTBRSilentlyTaskRunner.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelTBRSilentlyTaskRunner.java new file mode 100644 index 0000000000..9a3068aef9 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/CancelTBRSilentlyTaskRunner.java @@ -0,0 +1,50 @@ +package info.nightscout.androidaps.plugins.PumpInsight.connector; + +import sugar.free.sightparser.applayer.descriptors.MessagePriority; +import sugar.free.sightparser.applayer.descriptors.alerts.Warning36TBRCancelled; +import sugar.free.sightparser.applayer.messages.AppLayerMessage; +import sugar.free.sightparser.applayer.messages.remote_control.CancelTBRMessage; +import sugar.free.sightparser.applayer.messages.remote_control.DismissAlertMessage; +import sugar.free.sightparser.applayer.messages.status.ActiveAlertMessage; +import sugar.free.sightparser.applayer.messages.status.CurrentTBRMessage; +import sugar.free.sightparser.handling.SightServiceConnector; +import sugar.free.sightparser.handling.TaskRunner; + +public class CancelTBRSilentlyTaskRunner extends TaskRunner { + + private long cancelledAt; + + public CancelTBRSilentlyTaskRunner(SightServiceConnector serviceConnector) { + super(serviceConnector); + } + + @Override + protected AppLayerMessage run(AppLayerMessage message) throws Exception { + if (message == null) return new CurrentTBRMessage(); + else if (message instanceof CurrentTBRMessage) { + if (((CurrentTBRMessage) message).getPercentage() == 100) finish(false); + else return new CancelTBRMessage(); + } else if (message instanceof CancelTBRMessage) { + ActiveAlertMessage activeAlertMessage = new ActiveAlertMessage(); + activeAlertMessage.setMessagePriority(MessagePriority.HIGHER); + return activeAlertMessage; + } else if (message instanceof ActiveAlertMessage) { + ActiveAlertMessage activeAlertMessage = (ActiveAlertMessage) message; + if (activeAlertMessage.getAlert() == null) { + if (System.currentTimeMillis() - cancelledAt >= 10000) finish(true); + else { + ActiveAlertMessage activeAlertMessage2 = new ActiveAlertMessage(); + activeAlertMessage2.setMessagePriority(MessagePriority.HIGHER); + return activeAlertMessage2; + } + } else if (!(activeAlertMessage.getAlert() instanceof Warning36TBRCancelled)) finish(true); + else { + DismissAlertMessage dismissAlertMessage = new DismissAlertMessage(); + dismissAlertMessage.setAlertID(activeAlertMessage.getAlertID()); + dismissAlertMessage.setMessagePriority(MessagePriority.HIGHER); + return dismissAlertMessage; + } + } else if (message instanceof DismissAlertMessage) finish(true); + return null; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/SetTBRTaskRunner.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/SetTBRTaskRunner.java index 1506ecb4a9..f18bb0d487 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/SetTBRTaskRunner.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/SetTBRTaskRunner.java @@ -25,25 +25,15 @@ public class SetTBRTaskRunner extends TaskRunner { if (message == null) return new CurrentTBRMessage(); else if (message instanceof CurrentTBRMessage) { if (((CurrentTBRMessage) message).getPercentage() == 100) { - if (amount == 100) finish(amount); - else { - SetTBRMessage setTBRMessage = new SetTBRMessage(); - setTBRMessage.setDuration(duration); - setTBRMessage.setAmount(amount); - return setTBRMessage; - } + SetTBRMessage setTBRMessage = new SetTBRMessage(); + setTBRMessage.setDuration(duration); + setTBRMessage.setAmount(amount); + return setTBRMessage; } else { - if (amount == 100) { - ChangeTBRMessage changeTBRMessage = new ChangeTBRMessage(); - changeTBRMessage.setDuration(1); - changeTBRMessage.setAmount(90); - return changeTBRMessage; - } else { - ChangeTBRMessage changeTBRMessage = new ChangeTBRMessage(); - changeTBRMessage.setDuration(duration); - changeTBRMessage.setAmount(amount); - return changeTBRMessage; - } + ChangeTBRMessage changeTBRMessage = new ChangeTBRMessage(); + changeTBRMessage.setDuration(duration); + changeTBRMessage.setAmount(amount); + return changeTBRMessage; } } else if (message instanceof SetTBRMessage) finish(amount); return null; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/StatusTaskRunner.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/StatusTaskRunner.java index 86e01f98f8..1df56be468 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/StatusTaskRunner.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/connector/StatusTaskRunner.java @@ -12,6 +12,8 @@ import sugar.free.sightparser.applayer.descriptors.configuration_blocks.BRProfil import sugar.free.sightparser.applayer.descriptors.configuration_blocks.BRProfile5Block; import sugar.free.sightparser.applayer.descriptors.configuration_blocks.BRProfileBlock; import sugar.free.sightparser.applayer.descriptors.configuration_blocks.ConfigurationBlock; +import sugar.free.sightparser.applayer.descriptors.configuration_blocks.FactoryMinBRAmountBlock; +import sugar.free.sightparser.applayer.descriptors.configuration_blocks.FactoryMinBolusAmountBlock; import sugar.free.sightparser.applayer.descriptors.configuration_blocks.MaxBRAmountBlock; import sugar.free.sightparser.applayer.descriptors.configuration_blocks.MaxBolusAmountBlock; import sugar.free.sightparser.applayer.messages.AppLayerMessage; @@ -102,6 +104,16 @@ public class StatusTaskRunner extends TaskRunner { return readMessage; } else if (configurationBlock instanceof MaxBRAmountBlock) { result.maximumBasalAmount = ((MaxBRAmountBlock) configurationBlock).getMaximumAmount(); + ReadConfigurationBlockMessage readMessage = new ReadConfigurationBlockMessage(); + readMessage.setConfigurationBlockID(FactoryMinBRAmountBlock.ID); + return readMessage; + } else if (configurationBlock instanceof FactoryMinBRAmountBlock) { + result.minimumBasalAmount = ((FactoryMinBRAmountBlock) configurationBlock).getMinimumAmount(); + ReadConfigurationBlockMessage readMessage = new ReadConfigurationBlockMessage(); + readMessage.setConfigurationBlockID(FactoryMinBolusAmountBlock.ID); + return readMessage; + } else if (configurationBlock instanceof FactoryMinBolusAmountBlock) { + result.minimumBolusAmount = ((FactoryMinBolusAmountBlock) configurationBlock).getMinimumAmount(); finish(result); } } @@ -122,5 +134,7 @@ public class StatusTaskRunner extends TaskRunner { public List basalProfile; public double maximumBolusAmount; public double maximumBasalAmount; + public double minimumBolusAmount; + public double minimumBasalAmount; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java index e7784287b7..2c2235bf47 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryIntentAdapter.java @@ -58,13 +58,8 @@ class HistoryIntentAdapter { final long record_unique_id = getRecordUniqueID(pump_serial_number, pump_record_id); - // other sanity checks - if ((pump_tbr_percent == 90) && (pump_tbr_duration <= 1)) { - log("Not creating TBR record for faux cancel"); - } else { - log("Creating TBR record: " + pump_tbr_percent + "% " + pump_tbr_duration + "m" + " id:" + record_unique_id); - logAdapter.createTBRrecord(start_time, pump_tbr_percent, pump_tbr_duration, record_unique_id); - } + log("Creating TBR record: " + pump_tbr_percent + "% " + pump_tbr_duration + "m" + " id:" + record_unique_id); + logAdapter.createTBRrecord(start_time, pump_tbr_percent, pump_tbr_duration, record_unique_id); } void processDeliveredBolusIntent(Intent intent) { @@ -160,8 +155,10 @@ class HistoryIntentAdapter { if (SP.getBoolean("insight_automatic_careportal_events", false)) { Date date = getDateExtra(intent, HistoryBroadcast.EXTRA_EVENT_TIME); String alertType = intent.getStringExtra(HistoryBroadcast.EXTRA_ALERT_TYPE); + int alertText = getAlertText(alertType); + if (alertText == 0) return; if (MainApp.getDbHelper().getCareportalEventFromTimestamp(date.getTime()) != null) return; - logNote(date, MainApp.gs(getAlertText(alertType))); + logNote(date, MainApp.gs(alertText)); } } @@ -231,8 +228,8 @@ class HistoryIntentAdapter { if (type.equals("Warning32BatteryLow")) return R.string.alert_w32; if (type.equals("Warning33InvalidDateTime")) return R.string.alert_w33; if (type.equals("Warning34EndOfWarranty")) return R.string.alert_w34; - if (type.equals("Warning36TBRCancelled")) return R.string.alert_w36; - if (type.equals("Warning38BolusCancelled")) return R.string.alert_w38; + if (type.equals("Warning36TBRCancelled")) return 0; + if (type.equals("Warning38BolusCancelled")) return 0; if (type.equals("Warning39LoantimeWarning")) return R.string.alert_w39; return 0; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryLogAdapter.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryLogAdapter.java index 689eb9e981..bff37c9fdd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryLogAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpInsight/history/HistoryLogAdapter.java @@ -83,6 +83,6 @@ class HistoryLogAdapter { detailedBolusInfo.source = Source.PUMP; detailedBolusInfo.pumpId = record_id; detailedBolusInfo.insulin = insulin; - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpMDI/MDIPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpMDI/MDIPlugin.java index fed0c01c64..fb41a72e88 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpMDI/MDIPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpMDI/MDIPlugin.java @@ -42,6 +42,7 @@ public class MDIPlugin extends PluginBase implements PumpInterface { super(new PluginDescription() .mainType(PluginType.PUMP) .pluginName(R.string.mdi) + .description(R.string.description_pump_mdi) ); pumpDescription.isBolusCapable = true; pumpDescription.bolusStep = 0.5d; @@ -135,7 +136,7 @@ public class MDIPlugin extends PluginBase implements PumpInterface { result.bolusDelivered = detailedBolusInfo.insulin; result.carbsDelivered = detailedBolusInfo.carbs; result.comment = MainApp.gs(R.string.virtualpump_resultok); - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); return result; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java index 7a3f1f3cd2..9d94cc6070 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java @@ -82,6 +82,8 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { .pluginName(R.string.virtualpump) .shortName(R.string.virtualpump_shortname) .preferencesId(R.xml.pref_virtualpump) + .neverVisible(BuildConfig.NSCLIENTOLNY || BuildConfig.G5UPLOADER) + .description(R.string.description_pump_virtual) ); pumpDescription.isBolusCapable = true; pumpDescription.bolusStep = 0.1d; @@ -229,7 +231,7 @@ public class VirtualPumpPlugin extends PluginBase implements PumpInterface { log.debug("Delivering treatment insulin: " + detailedBolusInfo.insulin + "U carbs: " + detailedBolusInfo.carbs + "g " + result); MainApp.bus().post(new EventVirtualPumpUpdateGui()); lastDataTime = new Date(); - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); return result; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java new file mode 100644 index 0000000000..1d96eeff6b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/AbstractSensitivityPlugin.java @@ -0,0 +1,68 @@ +package info.nightscout.androidaps.plugins.Sensitivity; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.SensitivityInterface; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.utils.Round; +import info.nightscout.utils.SP; +import info.nightscout.utils.SafeParse; + +public abstract class AbstractSensitivityPlugin extends PluginBase implements SensitivityInterface { + + private static final Logger log = LoggerFactory.getLogger("AUTOSENS"); + + public AbstractSensitivityPlugin(PluginDescription pluginDescription) { + super(pluginDescription); + } + + @Override + public abstract AutosensResult detectSensitivity(long fromTime, long toTime); + + public AutosensResult fillResult(double ratio, double carbsAbsorbed, String pastSensitivity, + String ratioLimit, String sensResult, int deviationsArraySize) { + return this.fillResult(ratio, carbsAbsorbed, pastSensitivity, ratioLimit, sensResult, + deviationsArraySize, + SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7")), + SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); + } + + public AutosensResult fillResult(double ratio, double carbsAbsorbed, String pastSensitivity, + String ratioLimit, String sensResult, int deviationsArraySize, + double ratioMin, double ratioMax) { + double rawRatio = ratio; + ratio = Math.max(ratio, ratioMin); + ratio = Math.min(ratio, ratioMax); + + //If not-excluded data <= MIN_HOURS -> don't do Autosens + //If not-excluded data >= MIN_HOURS_FULL_AUTOSENS -> full Autosens + //Between MIN_HOURS and MIN_HOURS_FULL_AUTOSENS: gradually increase autosens + double autosensContrib = (Math.min(Math.max(MIN_HOURS, deviationsArraySize / 12d), + MIN_HOURS_FULL_AUTOSENS) - MIN_HOURS) / (MIN_HOURS_FULL_AUTOSENS - MIN_HOURS); + ratio = autosensContrib * (ratio - 1) + 1; + + if (autosensContrib != 1d) { + ratioLimit += "(" + deviationsArraySize + " of " + MIN_HOURS_FULL_AUTOSENS * 12 + " values) "; + } + + if (ratio != rawRatio) { + ratioLimit += "Ratio limited from " + rawRatio + " to " + ratio; + log.debug(ratioLimit); + } + + AutosensResult output = new AutosensResult(); + output.ratio = Round.roundTo(ratio, 0.01); + output.carbsAbsorbed = Round.roundTo(carbsAbsorbed, 0.01); + output.pastSensitivity = pastSensitivity; + output.ratioLimit = ratioLimit; + output.sensResult = sensResult; + return output; + } + + + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java similarity index 72% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java index 75b1ea9675..e5ca522a51 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityAAPS/SensitivityAAPSPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityAAPSPlugin.java @@ -1,11 +1,10 @@ -package info.nightscout.androidaps.plugins.SensitivityAAPS; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -15,23 +14,21 @@ 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.interfaces.PluginBase; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.SensitivityInterface; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.utils.Round; +import info.nightscout.utils.DateUtil; import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; /** * Created by mike on 24.06.2017. */ -public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInterface { - private static Logger log = LoggerFactory.getLogger(SensitivityAAPSPlugin.class); +public class SensitivityAAPSPlugin extends AbstractSensitivityPlugin { + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); static SensitivityAAPSPlugin plugin = null; @@ -47,6 +44,7 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte .pluginName(R.string.sensitivityaaps) .shortName(R.string.sensitivity_shortname) .preferencesId(R.xml.pref_absorption_aaps) + .description(R.string.description_sensitivity_aaps) ); } @@ -69,17 +67,19 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte } if (autosensDataTable == null || autosensDataTable.size() < 4) { - log.debug("No autosens data available"); + log.debug("No autosens data available. lastDataTime=" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } AutosensData current = IobCobCalculatorPlugin.getPlugin().getAutosensData(toTime); // this is running inside lock already if (current == null) { - log.debug("No autosens data available"); + log.debug("No autosens data available. toTime: " + DateUtil.dateAndTimeString(toTime) + " lastDataTime: " + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + List deviationsArray = new ArrayList<>(); String pastSensitivity = ""; int index = 0; @@ -96,8 +96,21 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte continue; } - if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) - deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(deviation); if (deviationsArray.size() > hoursForDetection * 60 / 5) deviationsArray.remove(0); @@ -138,26 +151,16 @@ public class SensitivityAAPSPlugin extends PluginBase implements SensitivityInte if (Config.logAutosensData) log.debug(sensResult); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); if (Config.logAutosensData) { - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " percentile: " + percentile + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {}, percentile: {} ratio: {} mealCOB: ", + new Date(toTime).toLocaleString(), + percentile, output.ratio, ratio, current.cob); log.debug("Sensitivity to: deviations " + Arrays.toString(deviations)); } - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java similarity index 69% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java index 15be72880d..c9086dbfbd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityOref0/SensitivityOref0Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref0Plugin.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.SensitivityOref0; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; @@ -14,23 +14,20 @@ 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.interfaces.PluginBase; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.SensitivityInterface; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.utils.Round; -import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; +import info.nightscout.utils.DateUtil; /** * Created by mike on 24.06.2017. */ -public class SensitivityOref0Plugin extends PluginBase implements SensitivityInterface { - private static Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); +public class SensitivityOref0Plugin extends AbstractSensitivityPlugin { + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); static SensitivityOref0Plugin plugin = null; @@ -46,6 +43,7 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt .pluginName(R.string.sensitivityoref0) .shortName(R.string.sensitivity_shortname) .preferencesId(R.xml.pref_absorption_oref0) + .description(R.string.description_sensitivity_oref0) ); } @@ -53,12 +51,7 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt public AutosensResult detectSensitivity(long fromTime, long toTime) { LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); - String age = SP.getString(R.string.key_age, ""); - int defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_adult))) defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_teenage))) defaultHours = 24; - if (age.equals(MainApp.gs(R.string.key_child))) defaultHours = 24; - int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); + int hoursForDetection = 24; long now = System.currentTimeMillis(); Profile profile = MainApp.getConfigBuilder().getProfile(); @@ -69,17 +62,19 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt } if (autosensDataTable == null || autosensDataTable.size() < 4) { - log.debug("No autosens data available"); + log.debug("No autosens data available. lastDataTime=" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } AutosensData current = IobCobCalculatorPlugin.getPlugin().getAutosensData(toTime); // this is running inside lock already if (current == null) { - log.debug("No current autosens data available"); + log.debug("No autosens data available. toTime: " + DateUtil.dateAndTimeString(toTime) + " lastDataTime: " + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + List deviationsArray = new ArrayList<>(); String pastSensitivity = ""; int index = 0; @@ -96,8 +91,21 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt continue; } - if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) - deviationsArray.add(autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) + deviationsArray.add(deviation); if (deviationsArray.size() > hoursForDetection * 60 / 5) deviationsArray.remove(0); @@ -149,24 +157,13 @@ public class SensitivityOref0Plugin extends PluginBase implements SensitivityInt ratio = 1 + (basalOff / profile.getMaxDailyBasal()); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); if (Config.logAutosensData) - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {} ratio: {} mealCOB: {}", + new Date(toTime).toLocaleString(), output.ratio, current.cob); - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java new file mode 100644 index 0000000000..214c82db9b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityOref1Plugin.java @@ -0,0 +1,188 @@ +package info.nightscout.androidaps.plugins.Sensitivity; + +import android.support.v4.util.LongSparseArray; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +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.db.CareportalEvent; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; +import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.utils.DateUtil; + +/** + * Created by mike on 19.06.2018. + */ + +public class SensitivityOref1Plugin extends AbstractSensitivityPlugin { + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); + + static SensitivityOref1Plugin plugin = null; + + public static SensitivityOref1Plugin getPlugin() { + if (plugin == null) + plugin = new SensitivityOref1Plugin(); + return plugin; + } + + public SensitivityOref1Plugin() { + super(new PluginDescription() + .mainType(PluginType.SENSITIVITY) + .pluginName(R.string.sensitivityoref1) + .shortName(R.string.sensitivity_shortname) + .preferencesId(R.xml.pref_absorption_oref1) + .description(R.string.description_sensitivity_oref1) + ); + } + + @Override + public AutosensResult detectSensitivity(long fromTime, long toTime) { + // todo this method is called from the IobCobCalculatorPlugin, which leads to a circular + // dependency, this should be avoided + LongSparseArray autosensDataTable = IobCobCalculatorPlugin.getPlugin().getAutosensDataTable(); + + Profile profile = MainApp.getConfigBuilder().getProfile(); + + if (profile == null) { + log.debug("No profile"); + return new AutosensResult(); + } + + if (autosensDataTable == null || autosensDataTable.size() < 4) { + log.debug("No autosens data available. lastDataTime=" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); + return new AutosensResult(); + } + + // the current + AutosensData current = IobCobCalculatorPlugin.getPlugin().getAutosensData(toTime); // this is running inside lock already + if (current == null) { + log.debug("No autosens data available. toTime: " + DateUtil.dateAndTimeString(toTime) + " lastDataTime: " + IobCobCalculatorPlugin.getPlugin().lastDataTime()); + return new AutosensResult(); + } + + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + + List deviationsArray = new ArrayList<>(); + String pastSensitivity = ""; + int index = 0; + while (index < autosensDataTable.size()) { + AutosensData autosensData = autosensDataTable.valueAt(index); + + if (autosensData.time < fromTime) { + index++; + continue; + } + + if (autosensData.time > toTime) { + index++; + continue; + } + + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + deviationsArray.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + + if (autosensData.validDeviation) + deviationsArray.add(deviation); + + for (int i = 0; i < autosensData.extraDeviation.size(); i++) + deviationsArray.add(autosensData.extraDeviation.get(i)); + if (deviationsArray.size() > 96) + deviationsArray.remove(0); + + pastSensitivity += autosensData.pastSensitivity; + int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time); + if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) { + pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")"; + } + index++; + } + + // when we have less than 8h worth of deviation data, add up to 90m of zero deviations + // this dampens any large sensitivity changes detected based on too little data, without ignoring them completely + if (Config.logAutosensData) + log.debug("Using most recent " + deviationsArray.size() + " deviations"); + if (deviationsArray.size() < 96) { + int pad = Math.round((1 - deviationsArray.size() / 96) * 18); + if (Config.logAutosensData) + log.debug("Adding " + pad + " more zero deviations"); + for (int d = 0; d < pad; d++) { + //process.stderr.write("."); + deviationsArray.add(0d); + } + } + + Double[] deviations = new Double[deviationsArray.size()]; + deviations = deviationsArray.toArray(deviations); + + double sens = profile.getIsf(); + + double ratio = 1; + String ratioLimit = ""; + String sensResult = ""; + + if (Config.logAutosensData) + log.debug("Records: " + index + " " + pastSensitivity); + + Arrays.sort(deviations); + + for (double i = 0.9; i > 0.1; i = i - 0.01) { + if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) >= 0 && IobCobCalculatorPlugin.percentile(deviations, i) < 0) { + if (Config.logAutosensData) + log.debug(Math.round(100 * i) + "% of non-meal deviations negative (>50% = sensitivity)"); + } + if (IobCobCalculatorPlugin.percentile(deviations, (i + 0.01)) > 0 && IobCobCalculatorPlugin.percentile(deviations, i) <= 0) { + if (Config.logAutosensData) + log.debug(Math.round(100 * i) + "% of non-meal deviations negative (>50% = resistance)"); + } + } + double pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50); + double pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.50); + + double basalOff = 0; + + if (pSensitive < 0) { // sensitive + basalOff = pSensitive * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin sensitivity detected"; + } else if (pResistant > 0) { // resistant + basalOff = pResistant * (60 / 5) / Profile.toMgdl(sens, profile.getUnits()); + sensResult = "Excess insulin resistance detected"; + } else { + sensResult = "Sensitivity normal"; + } + + if (Config.logAutosensData) + log.debug(sensResult); + + ratio = 1 + (basalOff / profile.getMaxDailyBasal()); + + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, deviationsArray.size()); + + if (Config.logAutosensData) + log.debug("Sensitivity to: {} ratio: {} mealCOB: {}", + new Date(toTime).toLocaleString(), output.ratio, current.cob); + + return output; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java similarity index 71% rename from app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java rename to app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java index 58f59701d9..0200838d39 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SensitivityWeightedAverage/SensitivityWeightedAveragePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Sensitivity/SensitivityWeightedAveragePlugin.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.SensitivityWeightedAverage; +package info.nightscout.androidaps.plugins.Sensitivity; import android.support.v4.util.LongSparseArray; @@ -6,28 +6,27 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; +import java.util.List; 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.interfaces.PluginBase; +import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.SensitivityInterface; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.utils.Round; +import info.nightscout.utils.DateUtil; import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; /** * Created by mike on 24.06.2017. */ -public class SensitivityWeightedAveragePlugin extends PluginBase implements SensitivityInterface { - private static Logger log = LoggerFactory.getLogger(SensitivityWeightedAveragePlugin.class); +public class SensitivityWeightedAveragePlugin extends AbstractSensitivityPlugin { + private static Logger log = LoggerFactory.getLogger("AUTOSENS"); private static SensitivityWeightedAveragePlugin plugin = null; @@ -43,6 +42,7 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens .pluginName(R.string.sensitivityweightedaverage) .shortName(R.string.sensitivity_shortname) .preferencesId(R.xml.pref_absorption_aaps) + .description(R.string.description_sensitivity_weighted_average) ); } @@ -58,15 +58,14 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens int hoursForDetection = SP.getInt(R.string.key_openapsama_autosens_period, defaultHours); if (autosensDataTable == null || autosensDataTable.size() < 4) { - if (Config.logAutosensData) - log.debug("No autosens data available"); + log.debug("No autosens data available. lastDataTime=" + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } AutosensData current = IobCobCalculatorPlugin.getPlugin().getAutosensData(toTime); // this is running inside lock already if (current == null) { if (Config.logAutosensData) - log.debug("No autosens data available"); + log.debug("No autosens data available. toTime: " + DateUtil.dateAndTimeString(toTime) + " lastDataTime: " + IobCobCalculatorPlugin.getPlugin().lastDataTime()); return new AutosensResult(); } @@ -78,6 +77,8 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens return new AutosensResult(); } + List siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true); + String pastSensitivity = ""; int index = 0; LongSparseArray data = new LongSparseArray<>(); @@ -100,11 +101,24 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens continue; } + // reset deviations after site change + if (CareportalEvent.isEvent5minBack(siteChanges, autosensData.time)) { + data.clear(); + pastSensitivity += "(SITECHANGE)"; + } + + double deviation = autosensData.deviation; + + //set positive deviations to zero if bg < 80 + if (autosensData.bg < 80 && deviation > 0) + deviation = 0; + //data.append(autosensData.time); long reverseWeight = (toTime - autosensData.time) / (5 * 60 * 1000L); - data.append(reverseWeight, autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + if (autosensData.validDeviation) + data.append(reverseWeight, deviation); //weights += reverseWeight; - //weightedsum += reverseWeight * (autosensData.nonEqualDeviation ? autosensData.deviation : 0d); + //weightedsum += reverseWeight * (autosensData.validDeviation ? autosensData.deviation : 0d); pastSensitivity += autosensData.pastSensitivity; @@ -116,7 +130,12 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens } if (data.size() == 0) { + if (Config.logAutosensData) + log.debug("Data size: " + data.size() + " fromTime: " + DateUtil.dateAndTimeString(fromTime) + " toTime: " + DateUtil.dateAndTimeString(toTime)); return new AutosensResult(); + } else { + if (Config.logAutosensData) + log.debug("Data size: " + data.size() + " fromTime: " + DateUtil.dateAndTimeString(fromTime) + " toTime: " + DateUtil.dateAndTimeString(toTime)); } double weightedsum = 0; @@ -158,25 +177,13 @@ public class SensitivityWeightedAveragePlugin extends PluginBase implements Sens if (Config.logAutosensData) log.debug(sensResult); - double rawRatio = ratio; - ratio = Math.max(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_min, "0.7"))); - ratio = Math.min(ratio, SafeParse.stringToDouble(SP.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (ratio != rawRatio) { - ratioLimit = "Ratio limited from " + rawRatio + " to " + ratio; - if (Config.logAutosensData) - log.debug(ratioLimit); - } + AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit, + sensResult, data.size()); if (Config.logAutosensData) - log.debug("Sensitivity to: " + new Date(toTime).toLocaleString() + " weightedaverage: " + average + " ratio: " + ratio + " mealCOB: " + current.cob); + log.debug("Sensitivity to: {} weightedaverage: {} ratio: {} mealCOB: {}", new Date(toTime).toLocaleString(), + average, output.ratio, current.cob); - AutosensResult output = new AutosensResult(); - output.ratio = Round.roundTo(ratio, 0.01); - output.carbsAbsorbed = Round.roundTo(current.cob, 0.01); - output.pastSensitivity = pastSensitivity; - output.ratioLimit = ratioLimit; - output.sensResult = sensResult; return output; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java index 9de6cc6ad6..847ead4482 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.SmsCommunicator; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.os.SystemClock; import android.telephony.SmsManager; import android.telephony.SmsMessage; @@ -47,6 +48,7 @@ import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; import info.nightscout.utils.SafeParse; +import info.nightscout.utils.T; import info.nightscout.utils.XdripCalibrations; /** @@ -124,6 +126,7 @@ public class SmsCommunicatorPlugin extends PluginBase { .pluginName(R.string.smscommunicator) .shortName(R.string.smscommunicator_shortname) .preferencesId(R.xml.pref_smscommunicator) + .description(R.string.description_sms_communicator) ); processSettings(null); } @@ -449,12 +452,14 @@ public class SmsCommunicatorPlugin extends PluginBase { public void run() { PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); if (result.success) { + SystemClock.sleep(T.secs(15).msecs()); // wait some time to get history String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), result.bolusDelivered); if (pump != null) reply += "\n" + pump.shortStatus(true); lastRemoteBolusTime = new Date(); sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply, new Date())); } else { + SystemClock.sleep(T.secs(60).msecs()); // wait some time to get history String reply = MainApp.gs(R.string.smscommunicator_bolusfailed); if (pump != null) reply += "\n" + pump.shortStatus(true); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceDexcomG5Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceDexcomG5Plugin.java index d526a12971..af040c8c90 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceDexcomG5Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceDexcomG5Plugin.java @@ -29,6 +29,7 @@ public class SourceDexcomG5Plugin extends PluginBase implements BgSourceInterfac .shortName(R.string.dexcomG5_shortname) .showInList(!Config.NSCLIENT) .preferencesId(R.xml.pref_dexcomg5) + .description(R.string.description_source_dexcom_g5) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceGlimpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceGlimpPlugin.java index 0846885df7..38b891ff65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceGlimpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceGlimpPlugin.java @@ -24,6 +24,7 @@ public class SourceGlimpPlugin extends PluginBase implements BgSourceInterface { .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment.class.getName()) .pluginName(R.string.Glimp) + .description(R.string.description_source_glimp) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceMM640gPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceMM640gPlugin.java index 8df63df1e6..041b084efd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceMM640gPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceMM640gPlugin.java @@ -23,6 +23,7 @@ public class SourceMM640gPlugin extends PluginBase implements BgSourceInterface .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment.class.getName()) .pluginName(R.string.MM640g) + .description(R.string.description_source_mm640g) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java index 3ba85e97a6..c99fbcb9bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceNSClientPlugin.java @@ -20,6 +20,9 @@ public class SourceNSClientPlugin extends PluginBase implements BgSourceInterfac return plugin; } + private long lastBGTimeStamp = 0; + private boolean isAdvancedFilteringEnabled = false; + private SourceNSClientPlugin() { super(new PluginDescription() .mainType(PluginType.BGSOURCE) @@ -27,11 +30,22 @@ public class SourceNSClientPlugin extends PluginBase implements BgSourceInterfac .pluginName(R.string.nsclientbg) .showInList(!Config.NSCLIENT) .alwaysEnabled(Config.NSCLIENT) + .description(R.string.description_source_ns_client) ); } @Override public boolean advancedFilteringSupported() { - return true; + return isAdvancedFilteringEnabled; + } + + public void detectSource(String source, long timeStamp) { + if (timeStamp > lastBGTimeStamp) { + if (source.contains("G5 Native") || source.contains("AndroidAPS-DexcomG5")) + isAdvancedFilteringEnabled = true; + else + isAdvancedFilteringEnabled = false; + lastBGTimeStamp = timeStamp; + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourcePoctechPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourcePoctechPlugin.java new file mode 100644 index 0000000000..13cb99d392 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourcePoctechPlugin.java @@ -0,0 +1,39 @@ +package info.nightscout.androidaps.plugins.Source; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.BgSourceInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginDescription; +import info.nightscout.androidaps.interfaces.PluginType; + +/** + * Created by mike on 05.08.2016. + */ +public class SourcePoctechPlugin extends PluginBase implements BgSourceInterface { + + private static SourcePoctechPlugin plugin = null; + + public static SourcePoctechPlugin getPlugin() { + if (plugin == null) + plugin = new SourcePoctechPlugin(); + return plugin; + } + + private SourcePoctechPlugin() { + super(new PluginDescription() + .mainType(PluginType.BGSOURCE) + .fragmentClass(BGSourceFragment.class.getName()) + .pluginName(R.string.poctech) + .showInList(!Config.NSCLIENT) + .preferencesId(R.xml.pref_poctech) + .description(R.string.description_source_poctech) + ); + } + + @Override + public boolean advancedFilteringSupported() { + return false; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java index bd3d96162e..fa61c9ea61 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Source/SourceXdripPlugin.java @@ -26,6 +26,7 @@ public class SourceXdripPlugin extends PluginBase implements BgSourceInterface { .mainType(PluginType.BGSOURCE) .fragmentClass(BGSourceFragment.class.getName()) .pluginName(R.string.xdrip) + .description(R.string.description_source_xdrip) ); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/CarbsGenerator.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/CarbsGenerator.java index 99168a2776..01eeee3662 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/CarbsGenerator.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/CarbsGenerator.java @@ -50,7 +50,7 @@ public class CarbsGenerator { } }); } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(carbInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(carbInfo, false); } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/Treatment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/Treatment.java index 997a55b5a4..73423c0eab 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/Treatment.java @@ -57,7 +57,7 @@ public class Treatment implements DataPointWithLabelInterface { public boolean isSMB = false; @DatabaseField - public int insulinInterfaceID = InsulinInterface.FASTACTINGINSULIN; // currently unused, will be used in the future + public int insulinInterfaceID = InsulinInterface.OREF_RAPID_ACTING; // currently unused, will be used in the future @DatabaseField public double dia = Constants.defaultDIA; // currently unused, will be used in the future @@ -132,6 +132,24 @@ public class Treatment implements DataPointWithLabelInterface { return true; } + + /* + * mealBolus, _id and isSMB cannot be known coming from pump. Only compare rest + * TODO: remove debug toasts + */ + public boolean equalsRePumpHistory(Treatment other) { + if (date != other.date) { + return false; + } + if (insulin != other.insulin) { + return false; + } + if (carbs != other.carbs) { + return false; + } + return true; + } + public void copyFrom(Treatment t) { date = t.date; _id = t._id; @@ -142,6 +160,14 @@ public class Treatment implements DataPointWithLabelInterface { isSMB = t.isSMB; } + public void copyBasics(Treatment t) { + date = t.date; + insulin = t.insulin; + carbs = t.carbs; + pumpId = t.pumpId; + source = t.source; + } + // ----------------- DataPointInterface -------------------- @Override public double getX() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentService.java index 2a5b798018..e701986e89 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentService.java @@ -241,28 +241,63 @@ public class TreatmentService extends OrmLiteBaseService { } // return true if new record is created - public boolean createOrUpdate(Treatment treatment) { + public UpdateReturn createOrUpdate(Treatment treatment) { try { Treatment old; treatment.date = DatabaseHelper.roundDateToSec(treatment.date); if (treatment.source == Source.PUMP) { // check for changed from pump change in NS - QueryBuilder queryBuilder = getDao().queryBuilder(); - Where where = queryBuilder.where(); - where.eq("pumpId", treatment.pumpId); - PreparedQuery preparedQuery = queryBuilder.prepare(); - List trList = getDao().query(preparedQuery); - if (trList.size() > 0) { - // do nothing, pump history record cannot be changed - log.debug("TREATMENT: Pump record already found in database: " + treatment.toString()); - return false; + Treatment existingTreatment = getPumpRecordById(treatment.pumpId); + if (existingTreatment != null) { + boolean equalRePumpHistory = existingTreatment.equalsRePumpHistory(treatment); + boolean sameSource = existingTreatment.source == treatment.source; + if(!equalRePumpHistory) { + // another treatment exists. Update it with the treatment coming from the pump + log.debug("TREATMENT: Pump record already found in database: " + existingTreatment.toString() + " wanting to add " + treatment.toString()); + long oldDate = existingTreatment.date; + + //preserve carbs + if(existingTreatment.isValid && existingTreatment.carbs > 0 && treatment.carbs == 0){ + treatment.carbs = existingTreatment.carbs; + } + + getDao().delete(existingTreatment); // need to delete/create because date may change too + existingTreatment.copyBasics(treatment); + getDao().create(existingTreatment); + DatabaseHelper.updateEarliestDataChange(oldDate); + DatabaseHelper.updateEarliestDataChange(existingTreatment.date); + scheduleTreatmentChange(treatment); + return new UpdateReturn(sameSource, false); //updating a pump treatment with another one from the pump is not counted as clash + } + return new UpdateReturn(equalRePumpHistory, false); + } + existingTreatment = getDao().queryForId(treatment.date); + if (existingTreatment != null) { + // another treatment exists with different pumpID. Update it with the treatment coming from the pump + boolean equalRePumpHistory = existingTreatment.equalsRePumpHistory(treatment); + boolean sameSource = existingTreatment.source == treatment.source; + long oldDate = existingTreatment.date; + log.debug("TREATMENT: Pump record already found in database: " + existingTreatment.toString() + " wanting to add " + treatment.toString()); + + //preserve carbs + if(existingTreatment.isValid && existingTreatment.carbs > 0 && treatment.carbs == 0){ + treatment.carbs = existingTreatment.carbs; + } + + getDao().delete(existingTreatment); // need to delete/create because date may change too + existingTreatment.copyFrom(treatment); + getDao().create(existingTreatment); + DatabaseHelper.updateEarliestDataChange(oldDate); + DatabaseHelper.updateEarliestDataChange(existingTreatment.date); + scheduleTreatmentChange(treatment); + return new UpdateReturn(equalRePumpHistory || sameSource, false); } getDao().create(treatment); log.debug("TREATMENT: New record from: " + Source.getString(treatment.source) + " " + treatment.toString()); DatabaseHelper.updateEarliestDataChange(treatment.date); scheduleTreatmentChange(treatment); - return true; + return new UpdateReturn(true, true); } if (treatment.source == Source.NIGHTSCOUT) { old = getDao().queryForId(treatment.date); @@ -279,9 +314,9 @@ public class TreatmentService extends OrmLiteBaseService { DatabaseHelper.updateEarliestDataChange(old.date); } scheduleTreatmentChange(treatment); - return true; + return new UpdateReturn(true, true); } - return false; + return new UpdateReturn(true, false); } // find by NS _id if (treatment._id != null) { @@ -299,7 +334,7 @@ public class TreatmentService extends OrmLiteBaseService { DatabaseHelper.updateEarliestDataChange(old.date); } scheduleTreatmentChange(treatment); - return true; + return new UpdateReturn(true, true); } } } @@ -307,19 +342,39 @@ public class TreatmentService extends OrmLiteBaseService { log.debug("TREATMENT: New record from: " + Source.getString(treatment.source) + " " + treatment.toString()); DatabaseHelper.updateEarliestDataChange(treatment.date); scheduleTreatmentChange(treatment); - return true; + return new UpdateReturn(true, true); } if (treatment.source == Source.USER) { getDao().create(treatment); log.debug("TREATMENT: New record from: " + Source.getString(treatment.source) + " " + treatment.toString()); DatabaseHelper.updateEarliestDataChange(treatment.date); scheduleTreatmentChange(treatment); - return true; + return new UpdateReturn(true, true); } } catch (SQLException e) { log.error("Unhandled exception", e); } - return false; + return new UpdateReturn(false, false); + } + + /** Returns the record for the given id, null if none, throws RuntimeException + * if multiple records with the same pump id exist. */ + @Nullable + public Treatment getPumpRecordById(long pumpId) { + try { + QueryBuilder queryBuilder = getDao().queryBuilder(); + Where where = queryBuilder.where(); + where.eq("pumpId", pumpId); + PreparedQuery preparedQuery = queryBuilder.prepare(); + List result = getDao().query(preparedQuery); + switch (result.size()) { + case 0: return null; + case 1: return result.get(0); + default: throw new RuntimeException("Multiple records with the same pump id found: " + result.toString()); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } } public void deleteNS(JSONObject json) { @@ -423,4 +478,14 @@ public class TreatmentService extends OrmLiteBaseService { public IBinder onBind(Intent intent) { return null; } + + public class UpdateReturn { + public UpdateReturn(boolean success, boolean newRecord){ + this.success = success; + this.newRecord = newRecord; + } + boolean newRecord; + boolean success; + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 8ae2e6d7b1..b701f66afc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -1,7 +1,9 @@ package info.nightscout.androidaps.plugins.Treatments; +import android.content.Intent; import android.support.annotation.Nullable; +import com.crashlytics.android.answers.CustomEvent; import com.squareup.otto.Subscribe; import org.slf4j.Logger; @@ -38,11 +40,13 @@ import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.Overview.Dialogs.ErrorHelperActivity; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; -import info.nightscout.androidaps.plugins.SensitivityAAPS.SensitivityAAPSPlugin; -import info.nightscout.androidaps.plugins.SensitivityWeightedAverage.SensitivityWeightedAveragePlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityAAPSPlugin; +import info.nightscout.androidaps.plugins.Sensitivity.SensitivityWeightedAveragePlugin; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; import info.nightscout.utils.T; @@ -78,8 +82,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface .fragmentClass(TreatmentsFragment.class.getName()) .pluginName(R.string.treatments) .shortName(R.string.treatments_shortname) - .preferencesId(R.xml.pref_absorption_oref0) .alwaysEnabled(true) + .description(R.string.description_treatments) ); this.service = new TreatmentService(); } @@ -465,7 +469,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface // return true if new record is created @Override - public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo) { + public boolean addToHistoryTreatment(DetailedBolusInfo detailedBolusInfo, boolean allowUpdate) { Treatment treatment = new Treatment(); treatment.date = detailedBolusInfo.date; treatment.source = detailedBolusInfo.source; @@ -477,7 +481,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface treatment.carbs = detailedBolusInfo.carbs; treatment.source = detailedBolusInfo.source; treatment.mealBolus = treatment.carbs > 0; - boolean newRecordCreated = getService().createOrUpdate(treatment); + TreatmentService.UpdateReturn creatOrUpdateResult = getService().createOrUpdate(treatment); + boolean newRecordCreated = creatOrUpdateResult.newRecord; //log.debug("Adding new Treatment record" + treatment.toString()); if (detailedBolusInfo.carbTime != 0) { Treatment carbsTreatment = new Treatment(); @@ -491,6 +496,24 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface } if (newRecordCreated && detailedBolusInfo.isValid) NSUpload.uploadTreatmentRecord(detailedBolusInfo); + + if (!allowUpdate && !creatOrUpdateResult.success) { + log.error("Treatment could not be added to DB", new Exception()); + + String status = String.format(MainApp.gs(R.string.error_adding_treatment_message), treatment.insulin, (int) treatment.carbs, DateUtil.dateAndTimeString(treatment.date)); + + Intent i = new Intent(MainApp.instance(), ErrorHelperActivity.class); + i.putExtra("soundid", R.raw.error); + i.putExtra("title", MainApp.gs(R.string.error_adding_treatment_title)); + i.putExtra("status", status); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + MainApp.instance().startActivity(i); + + CustomEvent customEvent = new CustomEvent("TreatmentClash"); + customEvent.putCustomAttribute("status", status); + FabricPrivacy.getInstance().logCustom(customEvent); + } + return newRecordCreated; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java index 57ad6833a9..0faceb2382 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsBolusFragment.java @@ -43,6 +43,8 @@ import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; +import static info.nightscout.utils.DateUtil.now; + public class TreatmentsBolusFragment extends SubscriberFragment implements View.OnClickListener { private static Logger log = LoggerFactory.getLogger(TreatmentsBolusFragment.class); @@ -52,6 +54,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. TextView iobTotal; TextView activityTotal; Button refreshFromNS; + Button deleteFutureTreatments; Context context; @@ -89,7 +92,7 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. holder.iob.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorActive)); else holder.iob.setTextColor(holder.carbs.getCurrentTextColor()); - if (t.date > DateUtil.now()) + if (t.date > now()) holder.date.setTextColor(ContextCompat.getColor(MainApp.instance(), R.color.colorScheduled)); else holder.date.setTextColor(holder.carbs.getCurrentTextColor()); @@ -189,6 +192,9 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. refreshFromNS = (Button) view.findViewById(R.id.treatments_reshreshfromnightscout); refreshFromNS.setOnClickListener(this); + deleteFutureTreatments = (Button) view.findViewById(R.id.treatments_delete_future_treatments); + deleteFutureTreatments.setOnClickListener(this); + boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); if (nsUploadOnly) refreshFromNS.setVisibility(View.GONE); @@ -201,17 +207,37 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. @Override public void onClick(View view) { + AlertDialog.Builder builder; switch (view.getId()) { case R.id.treatments_reshreshfromnightscout: - AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext()); + builder = new AlertDialog.Builder(this.getContext()); builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(MainApp.gs(R.string.refresheventsfromnightscout) + "?"); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - TreatmentsPlugin.getPlugin().getService().resetTreatments(); - Intent restartNSClient = new Intent(Intents.ACTION_RESTART); - MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + TreatmentsPlugin.getPlugin().getService().resetTreatments(); + Intent restartNSClient = new Intent(Intents.ACTION_RESTART); + MainApp.instance().getApplicationContext().sendBroadcast(restartNSClient); + }); + builder.setNegativeButton(MainApp.gs(R.string.cancel), null); + builder.show(); + break; + case R.id.treatments_delete_future_treatments: + builder = new AlertDialog.Builder(this.getContext()); + builder.setTitle(MainApp.gs(R.string.confirmation)); + builder.setMessage(MainApp.gs(R.string.deletefuturetreatments) + "?"); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + final List futureTreatments = TreatmentsPlugin.getPlugin().getService() + .getTreatmentDataFromTime(now() + 1000, true); + for (Treatment treatment : futureTreatments) { + final String _id = treatment._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + TreatmentsPlugin.getPlugin().getService().delete(treatment); } + updateGUI(); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -233,14 +259,16 @@ public class TreatmentsBolusFragment extends SubscriberFragment implements View. protected void updateGUI() { Activity activity = getActivity(); if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); - if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { - iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " U"); - activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " U"); - } + activity.runOnUiThread(() -> { + recyclerView.swapAdapter(new RecyclerViewAdapter(TreatmentsPlugin.getPlugin().getTreatmentsFromHistory()), false); + if (TreatmentsPlugin.getPlugin().getLastCalculationTreatments() != null) { + iobTotal.setText(DecimalFormatter.to2Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().iob) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + activityTotal.setText(DecimalFormatter.to3Decimal(TreatmentsPlugin.getPlugin().getLastCalculationTreatments().activity) + " " + MainApp.gs(R.string.insulin_unit_shortname)); + } + if (!TreatmentsPlugin.getPlugin().getService().getTreatmentDataFromTime(now() + 1000, true).isEmpty()) { + deleteFutureTreatments.setVisibility(View.VISIBLE); + } else { + deleteFutureTreatments.setVisibility(View.GONE); } }); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java index 97c3a05274..7e69883a3d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsCareportalFragment.java @@ -107,16 +107,14 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(MainApp.gs(R.string.confirmation)); builder.setMessage(MainApp.gs(R.string.removerecord) + "\n" + DateUtil.dateAndTimeString(careportalEvent.date)); - builder.setPositiveButton(MainApp.gs(R.string.ok), new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - final String _id = careportalEvent._id; - if (NSUpload.isIdValid(_id)) { - NSUpload.removeCareportalEntryFromNS(_id); - } else { - UploadQueue.removeID("dbAdd", _id); - } - MainApp.getDbHelper().delete(careportalEvent); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); } + MainApp.getDbHelper().delete(careportalEvent); }); builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); @@ -136,12 +134,14 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements llm = new LinearLayoutManager(view.getContext()); recyclerView.setLayoutManager(llm); - RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)); + RecyclerViewAdapter adapter = new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)); recyclerView.setAdapter(adapter); refreshFromNS = (Button) view.findViewById(R.id.careportal_refreshfromnightscout); refreshFromNS.setOnClickListener(this); + view.findViewById(R.id.careportal_removeandroidapsstartedevents).setOnClickListener(this); + context = getContext(); boolean nsUploadOnly = SP.getBoolean(R.string.key_ns_upload_only, false); @@ -169,6 +169,16 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements builder.setNegativeButton(MainApp.gs(R.string.cancel), null); builder.show(); break; + case R.id.careportal_removeandroidapsstartedevents: + builder = new AlertDialog.Builder(context); + builder.setTitle(MainApp.gs(R.string.confirmation)); + builder.setMessage(MainApp.gs(R.string.careportal_removestartedevents)); + builder.setPositiveButton(MainApp.gs(R.string.ok), (dialog, id) -> { + removeAndroidAPSStatedEvents(); + }); + builder.setNegativeButton(MainApp.gs(R.string.cancel), null); + builder.show(); + break; } } @@ -185,8 +195,24 @@ public class TreatmentsCareportalFragment extends SubscriberFragment implements activity.runOnUiThread(new Runnable() { @Override public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEventsFromTime(false)), false); + recyclerView.swapAdapter(new RecyclerViewAdapter(MainApp.getDbHelper().getCareportalEvents(false)), false); } }); } + + private void removeAndroidAPSStatedEvents() { + List events = MainApp.getDbHelper().getCareportalEvents(false); + for (int i = 0; i < events.size(); i++) { + CareportalEvent careportalEvent = events.get(i); + if (careportalEvent.json.contains(MainApp.gs(R.string.androidaps_start))) { + final String _id = careportalEvent._id; + if (NSUpload.isIdValid(_id)) { + NSUpload.removeCareportalEntryFromNS(_id); + } else { + UploadQueue.removeID("dbAdd", _id); + } + MainApp.getDbHelper().delete(careportalEvent); + } + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java index 5ec2723fa4..fc4728c53c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/ActionStringHandler.java @@ -196,6 +196,7 @@ public class ActionStringHandler { } boolean useBG = SP.getBoolean(R.string.key_wearwizard_bg, true); + boolean useTT = SP.getBoolean(R.string.key_wearwizard_tt, false); boolean useBolusIOB = SP.getBoolean(R.string.key_wearwizard_bolusiob, true); boolean useBasalIOB = SP.getBoolean(R.string.key_wearwizard_basaliob, true); boolean useCOB = SP.getBoolean(R.string.key_wearwizard_cob, true); @@ -223,7 +224,9 @@ public class ActionStringHandler { DecimalFormat format = new DecimalFormat("0.00"); DecimalFormat formatInt = new DecimalFormat("0"); BolusWizard bolusWizard = new BolusWizard(); - bolusWizard.doCalc(profile, null, carbsAfterConstraints, useCOB?cobInfo.displayCob:0d, useBG ? bgReading.valueToUnits(profile.getUnits()) : 0d, 0d, percentage, useBolusIOB, useBasalIOB, false, useTrend); + bolusWizard.doCalc(profile, useTT ? TreatmentsPlugin.getPlugin().getTempTargetFromHistory() : null, + carbsAfterConstraints, useCOB?cobInfo.displayCob:0d, useBG ? bgReading.valueToUnits(profile.getUnits()) : 0d, + 0d, percentage, useBolusIOB, useBasalIOB, false, useTrend); Double insulinAfterConstraints = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolusWizard.calculatedTotalInsulin)).value(); if (insulinAfterConstraints - bolusWizard.calculatedTotalInsulin != 0) { @@ -716,7 +719,7 @@ public class ActionStringHandler { } }); } else { - TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo); + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, false); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java index 7282ac0763..6cb47e1a3a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/WearPlugin.java @@ -56,6 +56,7 @@ public class WearPlugin extends PluginBase { .pluginName(R.string.wear) .shortName(R.string.wear_shortname) .preferencesId(R.xml.pref_wear) + .description(R.string.description_wear) ); this.ctx = ctx; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java index 696b8b0b57..b5d30ad071 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java @@ -738,11 +738,14 @@ public class WatchUpdaterService extends WearableListenerService implements public static int getBatteryLevel(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); - int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); - if (level == -1 || scale == -1) { - return 50; + if (batteryIntent != null) { + int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); + if (level == -1 || scale == -1) { + return 50; + } + return (int) (((float) level / (float) scale) * 100.0f); } - return (int) (((float) level / (float) scale) * 100.0f); + return 50; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java index 03c6c48a1a..d5ac60a626 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/XDripStatusline/StatuslinePlugin.java @@ -70,6 +70,7 @@ public class StatuslinePlugin extends PluginBase { .shortName(R.string.xdripstatus_shortname) .neverVisible(true) .preferencesId(R.xml.pref_xdripstatus) + .description(R.string.description_xdrip_status_line) ); this.ctx = ctx; this.mPrefs = PreferenceManager.getDefaultSharedPreferences(ctx); diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java index d2fd2a5e24..47fe4acd6a 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueue.java @@ -26,6 +26,7 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressHelperAc import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.Overview.notifications.Notification; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.androidaps.queue.commands.CommandBolus; import info.nightscout.androidaps.queue.commands.CommandCancelExtendedBolus; @@ -37,6 +38,7 @@ import info.nightscout.androidaps.queue.commands.CommandLoadTDDs; import info.nightscout.androidaps.queue.commands.CommandReadStatus; import info.nightscout.androidaps.queue.commands.CommandSMBBolus; import info.nightscout.androidaps.queue.commands.CommandSetProfile; +import info.nightscout.androidaps.queue.commands.CommandSetUserSettings; import info.nightscout.androidaps.queue.commands.CommandTempBasalAbsolute; import info.nightscout.androidaps.queue.commands.CommandTempBasalPercent; @@ -176,6 +178,18 @@ public class CommandQueue { public synchronized boolean bolus(DetailedBolusInfo detailedBolusInfo, Callback callback) { Command.CommandType type = detailedBolusInfo.isSMB ? Command.CommandType.SMB_BOLUS : Command.CommandType.BOLUS; + if (type == Command.CommandType.SMB_BOLUS) { + if (isRunning(Command.CommandType.BOLUS) || bolusInQueue()) { + log.debug("Rejecting SMB since a bolus is queue/running"); + return false; + } + if (detailedBolusInfo.lastKnownBolusTime < TreatmentsPlugin.getPlugin().getLastBolusTime()) { + log.debug("Rejecting bolus, another bolus was issued since request time"); + return false; + } + } + + if(type.equals(Command.CommandType.BOLUS) && detailedBolusInfo.carbs > 0 && detailedBolusInfo.insulin == 0){ type = Command.CommandType.CARBS_ONLY_TREATMENT; //Carbs only can be added in parallel as they can be "in the future". @@ -213,6 +227,12 @@ public class CommandQueue { return true; } + public synchronized void cancelAllBoluses() { + removeAll(Command.CommandType.BOLUS); + removeAll(Command.CommandType.SMB_BOLUS); + ConfigBuilderPlugin.getActivePump().stopBolusDelivering(); + } + // returns true if command is queued public boolean tempBasalAbsolute(double absoluteRate, int durationInMinutes, boolean enforceNew, Profile profile, Callback callback) { if (!enforceNew && isRunning(Command.CommandType.TEMPBASAL)) { @@ -397,6 +417,25 @@ public class CommandQueue { return true; } + // returns true if command is queued + public boolean setUserOptions(Callback callback) { + if (isRunning(Command.CommandType.SETUSERSETTINGS)) { + if (callback != null) + callback.result(executingNowError()).run(); + return false; + } + + // remove all unfinished + removeAll(Command.CommandType.SETUSERSETTINGS); + + // add new command to queue + add(new CommandSetUserSettings(callback)); + + notifyAboutNewCommand(); + + return true; + } + // returns true if command is queued public boolean loadTDDs(Callback callback) { if (isRunning(Command.CommandType.LOADHISTORY)) { diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java index b865fac86e..7156a72f25 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/Command.java @@ -18,7 +18,8 @@ public abstract class Command { BASALPROFILE, READSTATUS, LOADHISTORY, // TDDs and so far only Dana specific - LOADEVENTS // so far only Dana specific + LOADEVENTS, // so far only Dana specific + SETUSERSETTINGS // so far only Dana specific } public CommandType commandType; diff --git a/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.java b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.java new file mode 100644 index 0000000000..f3d9fe2a4b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/queue/commands/CommandSetUserSettings.java @@ -0,0 +1,45 @@ +package info.nightscout.androidaps.queue.commands; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.PumpEnactResult; +import info.nightscout.androidaps.interfaces.DanaRInterface; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.PumpDanaRv2.DanaRv2Plugin; +import info.nightscout.androidaps.queue.Callback; + +/** + * Created by mike on 10.11.2017. + */ + +public class CommandSetUserSettings extends Command { + private static Logger log = LoggerFactory.getLogger(CommandSetUserSettings.class); + public CommandSetUserSettings(Callback callback) { + commandType = CommandType.SETUSERSETTINGS; + this.callback = callback; + } + + @Override + public void execute() { + PumpInterface pump = ConfigBuilderPlugin.getActivePump(); + if (pump instanceof DanaRInterface) { + DanaRInterface danaPump = (DanaRInterface) pump; + boolean isDanaRv2 = MainApp.getSpecificPlugin(DanaRv2Plugin.class) != null && MainApp.getSpecificPlugin(DanaRv2Plugin.class).isEnabled(PluginType.PUMP); + if(isDanaRv2){ + log.debug("MsgSetUserOptions detected for DanaRv2"); + } + PumpEnactResult r = danaPump.setUserOptions(); + if (callback != null) + callback.result(r).run(); + } + } + + @Override + public String status() { + return "SETUSERSETTINGS"; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java index af46321901..d574b08f40 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.java @@ -1,9 +1,5 @@ package info.nightscout.androidaps.receivers; -/** - * Created by mike on 07.07.2016. - */ - import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -11,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.os.PowerManager; +import info.nightscout.utils.FabricPrivacy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +22,10 @@ import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.LocalAlertUtils; + +/** + * Created by mike on 07.07.2016. + */ public class KeepAliveReceiver extends BroadcastReceiver { private static Logger log = LoggerFactory.getLogger(KeepAliveReceiver.class); public static final long STATUS_UPDATE_FREQUENCY = 15 * 60 * 1000L; @@ -45,6 +46,7 @@ public class KeepAliveReceiver extends BroadcastReceiver { LocalAlertUtils.shortenSnoozeInterval(); LocalAlertUtils.checkStaleBGAlert(); checkPump(); + FabricPrivacy.uploadDailyStats(); log.debug("KeepAlive received"); wl.release(); diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java index 4e01066bf9..a8bd49f430 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NetworkChangeReceiver.java @@ -36,7 +36,7 @@ public class NetworkChangeReceiver extends BroadcastReceiver { NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); if (activeNetwork != null) { - if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) { + if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI && activeNetwork.isConnected()) { event.wifiConnected = true; WifiManager wifiManager = (WifiManager) MainApp.instance().getApplicationContext().getSystemService(Context.WIFI_SERVICE); if (wifiManager != null) { diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java new file mode 100644 index 0000000000..c1669b73d0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.java @@ -0,0 +1,616 @@ +package info.nightscout.androidaps.setupwizard; + +import android.Manifest; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.PowerManager; +import android.provider.Settings; +import android.support.v7.app.AppCompatActivity; + +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.BuildConfig; +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.PreferencesActivity; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventConfigBuilderChange; +import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.Careportal.CareportalFragment; +import info.nightscout.androidaps.plugins.Careportal.Dialogs.NewNSTreatmentDialog; +import info.nightscout.androidaps.plugins.Careportal.OptionsToShow; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesFragment; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.Loop.LoopPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.NSClientPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.events.EventNSClientStatus; +import info.nightscout.androidaps.plugins.ProfileLocal.LocalProfileFragment; +import info.nightscout.androidaps.plugins.ProfileLocal.LocalProfilePlugin; +import info.nightscout.androidaps.plugins.ProfileNS.NSProfileFragment; +import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; +import info.nightscout.androidaps.plugins.ProfileSimple.SimpleProfileFragment; +import info.nightscout.androidaps.plugins.ProfileSimple.SimpleProfilePlugin; +import info.nightscout.androidaps.setupwizard.elements.SWBreak; +import info.nightscout.androidaps.setupwizard.elements.SWButton; +import info.nightscout.androidaps.setupwizard.elements.SWFragment; +import info.nightscout.androidaps.setupwizard.elements.SWHtmlLink; +import info.nightscout.androidaps.setupwizard.elements.SWInfotext; +import info.nightscout.androidaps.setupwizard.elements.SWPlugin; +import info.nightscout.androidaps.setupwizard.elements.SWRadioButton; +import info.nightscout.androidaps.setupwizard.elements.SWEditString; +import info.nightscout.androidaps.setupwizard.elements.SWEditUrl; +import info.nightscout.androidaps.setupwizard.events.EventSWLabel; +import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; +import info.nightscout.utils.AndroidPermission; +import info.nightscout.utils.ImportExportPrefs; +import info.nightscout.utils.LocaleHelper; +import info.nightscout.utils.PasswordProtection; +import info.nightscout.utils.SP; +import info.nightscout.utils.ToastUtils; + +public class SWDefinition { + private static Logger log = LoggerFactory.getLogger(SWDefinition.class); + + private String packageName; + + private AppCompatActivity activity; + private List screens = new ArrayList<>(); + + public void setActivity(AppCompatActivity activity) { + this.activity = activity; + packageName = activity.getPackageName(); + } + + public AppCompatActivity getActivity() { + return activity; + } + + public List getScreens() { + return screens; + } + + SWDefinition add(SWScreen newScreen) { + screens.add(newScreen); + return this; + } + + SWDefinition() { + if (BuildConfig.FLAVOR.equals("full") || BuildConfig.FLAVOR.equals("pumpcontrol")) + SWDefinitionFull(); + else if (BuildConfig.FLAVOR.equals("nsclient")) + SWDefinitionNSClient(); + } + + private void SWDefinitionFull() { + // List all the screens here + add(new SWScreen(R.string.nav_setupwizard) + .add(new SWInfotext() + .label(R.string.welcometosetupwizard)) + ) + .add(new SWScreen(R.string.language) + .skippable(false) + .add(new SWRadioButton() + .option(R.array.languagesArray, R.array.languagesValues) + .preferenceId(R.string.key_language).label(R.string.language) + .comment(R.string.setupwizard_language_prompt)) + .validator(() -> { + String lang = SP.getString("language", "en"); + LocaleHelper.setLocale(MainApp.instance().getApplicationContext(), lang); + return SP.contains(R.string.key_language); + }) + ) + .add(new SWScreen(R.string.end_user_license_agreement) + .skippable(false) + .add(new SWInfotext() + .label(R.string.end_user_license_agreement_text)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.end_user_license_agreement_i_understand) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .action(() -> { + SP.putBoolean(R.string.key_i_understand, true); + MainApp.bus().post(new EventSWUpdate(false)); + })) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .validator(() -> SP.getBoolean(R.string.key_i_understand, false)) + ) + .add(new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY))) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))) + ) + .add(new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(MainApp.gs(R.string.needlocationpermission))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION, AndroidPermission.CASE_LOCATION))) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) + .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION))) + ) + .add(new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(MainApp.gs(R.string.needstoragepermission))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE))) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) + .add(new SWScreen(R.string.nav_import) + .add(new SWInfotext() + .label(R.string.storedsettingsfound)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.nav_import) + .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) + .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) + .add(new SWScreen(R.string.nsclientinternal_title) + .skippable(true) + .add(new SWInfotext() + .label(R.string.nsclientinfotext)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.enable_nsclient) + .action(() -> { + NSClientPlugin.getPlugin().setPluginEnabled(PluginType.GENERAL, true); + NSClientPlugin.getPlugin().setFragmentVisible(PluginType.GENERAL, true); + ConfigBuilderFragment.processOnEnabledCategoryChanged(NSClientPlugin.getPlugin(), PluginType.GENERAL); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + MainApp.bus().post(new EventConfigBuilderChange()); + MainApp.bus().post(new EventSWUpdate(true)); + }) + .visibility(() -> !NSClientPlugin.getPlugin().isEnabled(PluginType.GENERAL))) + .add(new SWEditUrl() + .preferenceId(R.string.key_nsclientinternal_url) + .label(R.string.nsclientinternal_url_title) + .comment(R.string.nsclientinternal_url_dialogmessage)) + .add(new SWEditString() + .validator(text -> text.length() >= 12) + .preferenceId(R.string.key_nsclientinternal_api_secret) + .label(R.string.nsclientinternal_secret_dialogtitle) + .comment(R.string.nsclientinternal_secret_dialogmessage)) + .add(new SWBreak()) + .add(new SWEventListener(this) + .label(R.string.status) + .initialStatus(NSClientPlugin.getPlugin().status) + .listener(new Object() { + @Subscribe + public void onEventNSClientStatus(EventNSClientStatus event) { + MainApp.bus().post(new EventSWLabel(event.status)); + } + }) + ) + .add(new SWBreak()) + .validator(() -> NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth) + .visibility(() -> !(NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth)) + ) + .add(new SWScreen(R.string.patientage) + .skippable(false) + .add(new SWInfotext() + .label(R.string.patientage_summary)) + .add(new SWBreak()) + .add(new SWRadioButton() + .option(R.array.ageArray, R.array.ageValues) + .preferenceId(R.string.key_age) + .label(R.string.patientage) + .comment(R.string.patientage_summary)) + .validator(() -> SP.contains(R.string.key_age)) + ) + .add(new SWScreen(R.string.configbuilder_insulin) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin)) + .add(new SWBreak()) + .add(new SWInfotext() + .label(R.string.diawarning)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.insulinsourcesetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveInsulin(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveInsulin()!= null && ((PluginBase) MainApp.getConfigBuilder().getActiveInsulin()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveInsulin() != null) + ) + .add(new SWScreen(R.string.configbuilder_bgsource) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.BGSOURCE, R.string.configbuilder_bgsource_description) + .label(R.string.configbuilder_bgsource)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.bgsourcesetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveBgSource(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveBgSource()!= null && ((PluginBase) MainApp.getConfigBuilder().getActiveBgSource()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveBgSource() != null) + ) + .add(new SWScreen(R.string.configbuilder_profile) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_profile_description)) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.PROFILE, R.string.configbuilder_profile_description) + .label(R.string.configbuilder_profile)) + .validator(() -> MainApp.getConfigBuilder().getActiveProfileInterface() != null) + ) + .add(new SWScreen(R.string.nsprofile) + .skippable(false) + .add(new SWInfotext() + .label(R.string.adjustprofileinns)) + .add(new SWFragment(this) + .add(new NSProfileFragment())) + .validator(() -> NSProfilePlugin.getPlugin().getProfile() != null && NSProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> NSProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) + ) + .add(new SWScreen(R.string.localprofile) + .skippable(false) + .add(new SWFragment(this) + .add(new LocalProfileFragment())) + .validator(() -> LocalProfilePlugin.getPlugin().getProfile() != null && LocalProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> LocalProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) + ) + .add(new SWScreen(R.string.simpleprofile) + .skippable(false) + .add(new SWFragment(this) + .add(new SimpleProfileFragment())) + .validator(() -> SimpleProfilePlugin.getPlugin().getProfile() != null && SimpleProfilePlugin.getPlugin().getProfile().getDefaultProfile().isValid("StartupWizard")) + .visibility(() -> SimpleProfilePlugin.getPlugin().isEnabled(PluginType.PROFILE)) + ) + .add(new SWScreen(R.string.profileswitch) + .skippable(false) + .add(new SWInfotext() + .label(R.string.profileswitch_ismissing)) + .add(new SWButton() + .text(R.string.profileswitch) + .action(() -> { + NewNSTreatmentDialog newDialog = new NewNSTreatmentDialog(); + final OptionsToShow profileswitch = CareportalFragment.PROFILESWITCHDIRECT; + profileswitch.executeProfileSwitch = true; + newDialog.setOptions(profileswitch, R.string.careportal_profileswitch); + newDialog.show(getActivity().getSupportFragmentManager(), "NewNSTreatmentDialog"); + })) + .validator(() -> MainApp.getConfigBuilder().getProfile() != null) + .visibility(() -> MainApp.getConfigBuilder().getProfile() == null) + ) + .add(new SWScreen(R.string.configbuilder_pump) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.PUMP, R.string.configbuilder_pump_description) + .label(R.string.configbuilder_pump)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.pumpsetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActivePump(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> ((PluginBase) MainApp.getConfigBuilder().getActivePump()).getPreferencesId() > 0)) + .add(new SWButton() + .text(R.string.readstatus) + .action(() -> ConfigBuilderPlugin.getCommandQueue().readStatus("Clicked connect to pump", null)) + .visibility(() -> MainApp.getConfigBuilder().getActivePump() != null)) + .add(new SWEventListener(this) + .listener(new Object() { + @Subscribe + public void onEventPumpStatusChanged(EventPumpStatusChanged event) { + MainApp.bus().post(new EventSWLabel(event.textStatus())); + } + }) + ) + .validator(() -> MainApp.getConfigBuilder().getActivePump() != null && MainApp.getConfigBuilder().getActivePump().isInitialized()) + ) + .add(new SWScreen(R.string.configbuilder_aps) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_aps_description)) + .add(new SWBreak()) + .add(new SWHtmlLink() + .label("https://openaps.readthedocs.io/en/latest/")) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.APS, R.string.configbuilder_aps_description) + .label(R.string.configbuilder_aps)) + .add(new SWButton() + .text(R.string.apssetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveAPS(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveAPS() != null && ((PluginBase) MainApp.getConfigBuilder().getActiveAPS()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveAPS() != null) + .visibility(() -> Config.APS) + ) + .add(new SWScreen(R.string.configbuilder_loop) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_loop_description)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.enableloop) + .action(() -> { + LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true); + LoopPlugin.getPlugin().setFragmentVisible(PluginType.LOOP, true); + ConfigBuilderFragment.processOnEnabledCategoryChanged(LoopPlugin.getPlugin(), PluginType.LOOP); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + MainApp.bus().post(new EventConfigBuilderChange()); + MainApp.bus().post(new EventSWUpdate(true)); + }) + .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP))) + .validator(() -> LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) + .visibility(() -> !LoopPlugin.getPlugin().isEnabled(PluginType.LOOP) && Config.APS) + ) + .add(new SWScreen(R.string.configbuilder_sensitivity) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_sensitivity_description)) + .add(new SWHtmlLink() + .label(R.string.setupwizard_sensitivity_url)) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) + .label(R.string.configbuilder_sensitivity)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.sensitivitysetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveSensitivity(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveSensitivity() != null && ((PluginBase) MainApp.getConfigBuilder().getActiveSensitivity()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveSensitivity() != null) + ) + .add(new SWScreen(R.string.objectives) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_objectives_description)) + .add(new SWButton() + .text(R.string.enableobjectives) + .action(() -> { + ObjectivesPlugin.getPlugin().setPluginEnabled(PluginType.CONSTRAINTS, true); + ObjectivesPlugin.getPlugin().setFragmentVisible(PluginType.CONSTRAINTS, true); + ConfigBuilderFragment.processOnEnabledCategoryChanged(ObjectivesPlugin.getPlugin(), PluginType.CONSTRAINTS); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + MainApp.bus().post(new EventConfigBuilderChange()); + MainApp.bus().post(new EventSWUpdate(true)); + }) + .visibility(() -> !ObjectivesPlugin.getPlugin().isFragmentVisible())) + .validator(() -> ObjectivesPlugin.getPlugin().isEnabled(PluginType.CONSTRAINTS)) + .visibility(() -> !ObjectivesPlugin.getPlugin().isFragmentVisible() && Config.APS) + ) + .add(new SWScreen(R.string.objectives) + .skippable(false) + .add(new SWInfotext() + .label(R.string.startobjective)) + .add(new SWBreak()) + .add(new SWFragment(this) + .add(new ObjectivesFragment())) + .validator(() -> ObjectivesPlugin.getPlugin().objectives.get(0).isStarted()) + .visibility(() -> !ObjectivesPlugin.getPlugin().objectives.get(0).isStarted() && Config.APS) + ) + ; + } + + private void SWDefinitionNSClient() { + // List all the screens here + add(new SWScreen(R.string.nav_setupwizard) + .add(new SWInfotext() + .label(R.string.welcometosetupwizard)) + ) + .add(new SWScreen(R.string.language) + .skippable(false) + .add(new SWRadioButton() + .option(R.array.languagesArray, R.array.languagesValues) + .preferenceId(R.string.key_language).label(R.string.language) + .comment(R.string.setupwizard_language_prompt)) + .validator(() -> { + String lang = SP.getString("language", "en"); + LocaleHelper.setLocale(MainApp.instance().getApplicationContext(), lang); + return SP.contains(R.string.key_language); + }) + ) + .add(new SWScreen(R.string.end_user_license_agreement) + .skippable(false) + .add(new SWInfotext() + .label(R.string.end_user_license_agreement_text)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.end_user_license_agreement_i_understand) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .action(() -> { + SP.putBoolean(R.string.key_i_understand, true); + MainApp.bus().post(new EventSWUpdate(false)); + })) + .visibility(() -> !SP.getBoolean(R.string.key_i_understand, false)) + .validator(() -> SP.getBoolean(R.string.key_i_understand, false)) + ) + .add(new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, AndroidPermission.CASE_BATTERY))) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) + .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))) + ) + .add(new SWScreen(R.string.permission) + .skippable(false) + .add(new SWInfotext() + .label(MainApp.gs(R.string.needstoragepermission))) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.askforpermission) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .action(() -> AndroidPermission.askForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE, AndroidPermission.CASE_STORAGE))) + .visibility(() -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) + .validator(() -> !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) + .add(new SWScreen(R.string.nav_import) + .add(new SWInfotext() + .label(R.string.storedsettingsfound)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.nav_import) + .action(() -> ImportExportPrefs.importSharedPreferences(getActivity()))) + .visibility(() -> ImportExportPrefs.file.exists() && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !AndroidPermission.checkForPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))) + ) + .add(new SWScreen(R.string.nsclientinternal_title) + .skippable(true) + .add(new SWInfotext() + .label(R.string.nsclientinfotext)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.enable_nsclient) + .action(() -> { + NSClientPlugin.getPlugin().setPluginEnabled(PluginType.GENERAL, true); + NSClientPlugin.getPlugin().setFragmentVisible(PluginType.GENERAL, true); + ConfigBuilderFragment.processOnEnabledCategoryChanged(NSClientPlugin.getPlugin(), PluginType.GENERAL); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + MainApp.bus().post(new EventConfigBuilderChange()); + MainApp.bus().post(new EventSWUpdate(true)); + }) + .visibility(() -> !NSClientPlugin.getPlugin().isEnabled(PluginType.GENERAL))) + .add(new SWEditUrl() + .preferenceId(R.string.key_nsclientinternal_url) + .label(R.string.nsclientinternal_url_title) + .comment(R.string.nsclientinternal_url_dialogmessage)) + .add(new SWEditString() + .validator(text -> text.length() >= 12) + .preferenceId(R.string.key_nsclientinternal_api_secret) + .label(R.string.nsclientinternal_secret_dialogtitle) + .comment(R.string.nsclientinternal_secret_dialogmessage)) + .add(new SWBreak()) + .add(new SWEventListener(this) + .label(R.string.status) + .initialStatus(NSClientPlugin.getPlugin().status) + .listener(new Object() { + @Subscribe + public void onEventNSClientStatus(EventNSClientStatus event) { + MainApp.bus().post(new EventSWLabel(event.status)); + } + }) + ) + .add(new SWBreak()) + .validator(() -> NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth) + .visibility(() -> !(NSClientPlugin.getPlugin().nsClientService != null && NSClientPlugin.getPlugin().nsClientService.isConnected && NSClientPlugin.getPlugin().nsClientService.hasWriteAuth)) + ) + .add(new SWScreen(R.string.patientage) + .skippable(false) + .add(new SWInfotext() + .label(R.string.patientage_summary)) + .add(new SWBreak()) + .add(new SWRadioButton() + .option(R.array.ageArray, R.array.ageValues) + .preferenceId(R.string.key_age) + .label(R.string.patientage) + .comment(R.string.patientage_summary)) + .validator(() -> SP.contains(R.string.key_age)) + ) + .add(new SWScreen(R.string.configbuilder_insulin) + .skippable(false) + .add(new SWPlugin() + .option(PluginType.INSULIN, R.string.configbuilder_insulin_description) + .makeVisible(false) + .label(R.string.configbuilder_insulin)) + .add(new SWBreak()) + .add(new SWInfotext() + .label(R.string.diawarning)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.insulinsourcesetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveInsulin(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveInsulin()!= null && ((PluginBase) MainApp.getConfigBuilder().getActiveInsulin()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveInsulin() != null) + ) + .add(new SWScreen(R.string.configbuilder_sensitivity) + .skippable(false) + .add(new SWInfotext() + .label(R.string.setupwizard_sensitivity_description)) + .add(new SWHtmlLink() + .label(R.string.setupwizard_sensitivity_url)) + .add(new SWBreak()) + .add(new SWPlugin() + .option(PluginType.SENSITIVITY, R.string.configbuilder_sensitivity_description) + .label(R.string.configbuilder_sensitivity)) + .add(new SWBreak()) + .add(new SWButton() + .text(R.string.sensitivitysetup) + .action(() -> { + final PluginBase plugin = (PluginBase) MainApp.getConfigBuilder().getActiveSensitivity(); + PasswordProtection.QueryPassword(activity, R.string.settings_password, "settings_password", () -> { + Intent i = new Intent(activity, PreferencesActivity.class); + i.putExtra("id", plugin.getPreferencesId()); + activity.startActivity(i); + }, null); + }) + .visibility(() -> MainApp.getConfigBuilder().getActiveSensitivity() != null && ((PluginBase) MainApp.getConfigBuilder().getActiveSensitivity()).getPreferencesId() > 0)) + .validator(() -> MainApp.getConfigBuilder().getActiveSensitivity() != null) + ) + ; + } + +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java new file mode 100644 index 0000000000..487a80d91c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWEventListener.java @@ -0,0 +1,72 @@ +package info.nightscout.androidaps.setupwizard; + +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.setupwizard.elements.SWItem; +import info.nightscout.androidaps.setupwizard.events.EventSWLabel; + + +public class SWEventListener extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWEventListener.class); + + private int textLabel = 0; + private String status = ""; + TextView textView; + Object listener; + SWDefinition definition; + + SWEventListener(SWDefinition definition) { + super(Type.LISTENER); + this.definition = definition; + MainApp.bus().register(this); + } + + public SWEventListener label(int newLabel) { + this.textLabel = newLabel; + return this; + } + + public SWEventListener initialStatus(String status) { + this.status = status; + return this; + } + + public SWEventListener listener(Object listener) { + this.listener = listener; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + textView = new TextView(context); + textView.setId(layout.generateViewId()); + textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); + layout.addView(textView); + if (listener != null) + try { + MainApp.bus().register(listener); + } catch (Exception ignored) {} + } + + @Subscribe + public void onEventSWLabel(final EventSWLabel l) { + status = l.label; + if (definition != null && definition.getActivity() != null) + definition.getActivity().runOnUiThread(() -> { + if (textView != null) + textView.setText((textLabel != 0 ? MainApp.gs(textLabel) : "") + " " + status); + }); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWScreen.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWScreen.java new file mode 100644 index 0000000000..5c7b10315f --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWScreen.java @@ -0,0 +1,49 @@ +package info.nightscout.androidaps.setupwizard; + +import java.util.ArrayList; +import java.util.List; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.setupwizard.elements.SWItem; + +public class SWScreen { + + int header; + List items = new ArrayList<>(); + SWValidator validator; + SWValidator visibility; + boolean skippable = false; + + public SWScreen(int header) { + this.header = header; + } + + public String getHeader() { + return MainApp.gs(header); + } + + public SWScreen skippable(boolean skippable) { + this.skippable = skippable; + return this; + } + + public SWScreen add(SWItem newItem) { + items.add(newItem); + return this; + } + + public SWScreen validator(SWValidator validator) { + this.validator = validator; + return this; + } + + public SWScreen visibility(SWValidator visibility) { + this.visibility = visibility; + return this; + } + + public void processVisibility() { + for (SWItem i : items) + i.processVisibility(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWTextValidator.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWTextValidator.java new file mode 100644 index 0000000000..427afbd45d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWTextValidator.java @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.setupwizard; + +public interface SWTextValidator { + boolean isValid(String text); +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWValidator.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWValidator.java new file mode 100644 index 0000000000..3cc8d6b0c1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWValidator.java @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.setupwizard; + +public interface SWValidator { + boolean isValid(); +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java new file mode 100644 index 0000000000..5102e36ac6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SetupWizardActivity.java @@ -0,0 +1,226 @@ +package info.nightscout.androidaps.setupwizard; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.MainActivity; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.events.EventProfileStoreChanged; +import info.nightscout.androidaps.events.EventProfileSwitchChange; +import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.plugins.ConstraintsObjectives.events.EventObjectivesSaved; +import info.nightscout.androidaps.plugins.NSClientInternal.events.EventNSClientStatus; +import info.nightscout.androidaps.setupwizard.elements.SWItem; +import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; +import info.nightscout.utils.AndroidPermission; +import info.nightscout.utils.LocaleHelper; +import info.nightscout.utils.OKDialog; +import info.nightscout.utils.SP; + +public class SetupWizardActivity extends AppCompatActivity { + //logging + private static Logger log = LoggerFactory.getLogger(SetupWizardActivity.class); + + ScrollView scrollView; + + private SWDefinition swDefinition = new SWDefinition(); + private List screens = swDefinition.getScreens(); + private int currentWizardPage = 0; + public static final String INTENT_MESSAGE = "WIZZARDPAGE"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LocaleHelper.onCreate(this, "en"); + setContentView(R.layout.activity_setupwizard); + + scrollView = (ScrollView) findViewById(R.id.sw_scrollview); + + Intent intent = getIntent(); + currentWizardPage = intent.getIntExtra(SetupWizardActivity.INTENT_MESSAGE, 0); + if (screens.size() > 0 && currentWizardPage < screens.size()) { + SWScreen currentScreen = screens.get(currentWizardPage); + + //Set screen name + TextView screenName = (TextView) findViewById(R.id.sw_content); + screenName.setText(currentScreen.getHeader()); + + swDefinition.setActivity(this); + //Generate layout first + generateLayout(); + updateButtons(); + } + } + + @Override + public void onBackPressed() { + if (currentWizardPage == 0) OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); + else showPreviousPage(null); + } + + public void exitPressed(View view) { + SP.putBoolean(R.string.key_setupwizard_processed, true); + OKDialog.showConfirmation(this, MainApp.gs(R.string.exitwizard), this::finish); + } + + @Override + public void onPause() { + super.onPause(); + MainApp.bus().unregister(this); + } + + @Override + protected void onResume() { + super.onResume(); + MainApp.bus().register(this); + swDefinition.setActivity(this); + } + + @Subscribe + public void onContentUpdate(EventSWUpdate ev) { + if (ev.redraw) + generateLayout(); + updateButtons(); + } + + @Subscribe + public void onEventNSClientStatus(EventNSClientStatus ignored) { + updateButtons(); + } + + @Subscribe + public void onEventPumpStatusChanged(EventPumpStatusChanged ignored) { + updateButtons(); + } + + @Subscribe + public void onEventProfileStoreChanged(EventProfileStoreChanged ignored) { + updateButtons(); + } + + @Subscribe + public void onEventProfileSwitchChange(EventProfileSwitchChange ignored) { + updateButtons(); + } + + @Subscribe + public void onEventObjectivesSaved(EventObjectivesSaved ignored) { + updateButtons(); + } + + private void generateLayout() { + SWScreen currentScreen = screens.get(currentWizardPage); + LinearLayout layout = SWItem.generateLayout(this.findViewById(R.id.sw_content_fields)); + for (int i = 0; i < currentScreen.items.size(); i++) { + SWItem currentItem = currentScreen.items.get(i); + currentItem.generateDialog(layout); + } + scrollView.smoothScrollTo(0,0); + } + + private void updateButtons() { + runOnUiThread(() -> { + SWScreen currentScreen = screens.get(currentWizardPage); + if (currentScreen.validator == null || currentScreen.validator.isValid() || currentScreen.skippable) { + if (currentWizardPage == nextPage()) { + findViewById(R.id.finish_button).setVisibility(View.VISIBLE); + findViewById(R.id.next_button).setVisibility(View.GONE); + } else { + findViewById(R.id.finish_button).setVisibility(View.GONE); + findViewById(R.id.next_button).setVisibility(View.VISIBLE); + } + } else { + findViewById(R.id.finish_button).setVisibility(View.GONE); + findViewById(R.id.next_button).setVisibility(View.GONE); + } + if (currentWizardPage == 0) + findViewById(R.id.previous_button).setVisibility(View.GONE); + else + findViewById(R.id.previous_button).setVisibility(View.VISIBLE); + currentScreen.processVisibility(); + }); + } + + public void showNextPage(View view) { + this.finish(); + Intent intent = new Intent(this, SetupWizardActivity.class); + intent.putExtra(INTENT_MESSAGE, nextPage()); + startActivity(intent); + } + + public void showPreviousPage(View view) { + this.finish(); + Intent intent = new Intent(this, SetupWizardActivity.class); + intent.putExtra(INTENT_MESSAGE, previousPage()); + startActivity(intent); + } + + // Go back to overview + public void finishSetupWizard(View view) { + SP.putBoolean(R.string.key_setupwizard_processed, true); + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + finish(); + } + + private int nextPage() { + int page = currentWizardPage + 1; + while (page < screens.size()) { + if (screens.get(page).visibility == null || screens.get(page).visibility.isValid()) + return page; + page++; + } + return currentWizardPage; + } + + private int previousPage() { + int page = currentWizardPage - 1; + while (page >= 0) { + if (screens.get(page).visibility == null || screens.get(page).visibility.isValid()) + return page; + page--; + } + return currentWizardPage; + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (permissions.length != 0) { + if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { + switch (requestCode) { + case AndroidPermission.CASE_STORAGE: + //show dialog after permission is granted + AlertDialog.Builder alert = new AlertDialog.Builder(this); + alert.setMessage(R.string.alert_dialog_storage_permission_text); + alert.setPositiveButton(R.string.ok, null); + alert.show(); + break; + case AndroidPermission.CASE_LOCATION: + case AndroidPermission.CASE_SMS: + case AndroidPermission.CASE_BATTERY: + break; + } + } + } + updateButtons(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java new file mode 100644 index 0000000000..c6a9cf3f86 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWBreak.java @@ -0,0 +1,47 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWValidator; + + +public class SWBreak extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWBreak.class); + + private TextView l; + private SWValidator visibilityValidator; + + public SWBreak() { + super(Type.TEXT); + } + + public SWBreak visibility(SWValidator visibilityValidator) { + this.visibilityValidator = visibilityValidator; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + l = new TextView(context); + l.setId(View.generateViewId()); + l.setText("\n"); + layout.addView(l); + + } + + @Override + public void processVisibility() { + if (visibilityValidator != null && !visibilityValidator.isValid()) + l.setVisibility(View.GONE); + else + l.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java new file mode 100644 index 0000000000..afa28c1803 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWButton.java @@ -0,0 +1,63 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWValidator; + +public class SWButton extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWButton.class); + + private Runnable buttonRunnable; + private int buttonText; + private SWValidator buttonValidator; + + private Button button; + + public SWButton() { + super(Type.BUTTON); + } + + public SWButton text(int buttonText) { + this.buttonText = buttonText; + return this; + } + + public SWButton action(Runnable buttonRunnable) { + this.buttonRunnable = buttonRunnable; + return this; + } + + public SWButton visibility(SWValidator buttonValidator) { + this.buttonValidator = buttonValidator; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + button = new Button(context); + button.setText(buttonText); + button.setOnClickListener((v) -> { + if (buttonRunnable != null) + buttonRunnable.run(); + }); + processVisibility(); + layout.addView(button); + super.generateDialog(layout); + } + + @Override + public void processVisibility() { + if (buttonValidator != null && !buttonValidator.isValid()) + button.setVisibility(View.GONE); + else + button.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java new file mode 100644 index 0000000000..ed96b37944 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWCheckbox.java @@ -0,0 +1,76 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.CheckBox; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.utils.SP; + +/** + * Created by Rumen Georgiev on 5/9/2018. + */ + +public class SWCheckbox extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWCheckbox.class); + + int labelsArray; + int valuesArray; + String label = ""; + int preferenceID; + private CheckBox checkBox; + + public SWCheckbox() { + super(Type.CHECKBOX); + } + + public SWCheckbox option(String label, int preferenceID) { + this.label = label; + this.preferenceID = preferenceID; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + // Get if there is already value in SP + Boolean previousValue; + previousValue = SP.getBoolean(preferenceId, false); + checkBox = new CheckBox(context); + checkBox.setText(label); + checkBox.setChecked(previousValue); + checkBox.setVisibility(View.VISIBLE); + checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + ArrayList pluginsInCategory; + pluginsInCategory = MainApp.getSpecificPluginsList(PluginType.PUMP); + PluginBase found = null; + for (PluginBase p : pluginsInCategory) { + if (p.isEnabled(PluginType.PUMP) && found == null) { + found = p; + } else if (p.isEnabled(PluginType.PUMP)) { + // set others disabled + p.setPluginEnabled(PluginType.PUMP, false); + } + } + log.debug("Enabled pump plugin:"+found.getClass()); + save(checkBox.isChecked()); + } + }); + layout.addView(checkBox); + super.generateDialog(layout); + } + public void save(boolean value){ + SP.putBoolean(preferenceID, value); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java new file mode 100644 index 0000000000..d02f10acf0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditString.java @@ -0,0 +1,79 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWTextValidator; +import info.nightscout.utils.SP; + + +public class SWEditString extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWEditString.class); + + private SWTextValidator validator = null; + + public SWEditString() { + super(Type.STRING); + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + TextView l = new TextView(context); + l.setId(layout.generateViewId()); + l.setText(label); + l.setTypeface(l.getTypeface(), Typeface.BOLD); + layout.addView(l); + + TextView c = new TextView(context); + c.setId(layout.generateViewId()); + c.setText(comment); + c.setTypeface(c.getTypeface(), Typeface.ITALIC); + layout.addView(c); + + EditText editText = new EditText(context); + editText.setId(layout.generateViewId()); + editText.setInputType(InputType.TYPE_CLASS_TEXT); + editText.setMaxLines(1); + editText.setText(SP.getString(preferenceId, "")); + layout.addView(editText); + super.generateDialog(layout); + + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (validator != null && validator.isValid(s.toString())) + save(s.toString()); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + } + + public SWEditString preferenceId(int preferenceId) { + this.preferenceId = preferenceId; + return this; + } + + public SWEditString validator(SWTextValidator validator) { + this.validator = validator; + return this; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java new file mode 100644 index 0000000000..04b02a5bc2 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWEditUrl.java @@ -0,0 +1,77 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.Editable; +import android.text.InputType; +import android.text.TextWatcher; +import android.util.Patterns; +import android.view.View; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.setupwizard.events.EventSWLabel; +import info.nightscout.utils.SP; + +public class SWEditUrl extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWEditUrl.class); + + public SWEditUrl() { + super(Type.URL); + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + TextView l = new TextView(context); + l.setId(View.generateViewId()); + l.setText(label); + l.setTypeface(l.getTypeface(), Typeface.BOLD); + layout.addView(l); + + TextView c = new TextView(context); + c.setId(View.generateViewId()); + c.setText(comment); + c.setTypeface(c.getTypeface(), Typeface.ITALIC); + layout.addView(c); + + EditText editText = new EditText(context); + editText.setId(View.generateViewId()); + editText.setInputType(InputType.TYPE_CLASS_TEXT); + editText.setMaxLines(1); + editText.setText(SP.getString(preferenceId, "")); + layout.addView(editText); + super.generateDialog(layout); + + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (Patterns.WEB_URL.matcher(s).matches()) + save(s.toString()); + else + MainApp.bus().post(new EventSWLabel(MainApp.gs(R.string.error_url_not_valid))); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + } + + public SWEditUrl preferenceId(int preferenceId) { + this.preferenceId = preferenceId; + return this; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java new file mode 100644 index 0000000000..a061f57eb6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWFragment.java @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.support.v4.app.Fragment; +import android.view.View; +import android.widget.LinearLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWDefinition; + + +public class SWFragment extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWFragment.class); + + SWDefinition definition; + Fragment fragment; + + public SWFragment(SWDefinition definition) { + super(Type.FRAGMENT); + this.definition = definition; + } + + public SWFragment add(Fragment fragment) { + this.fragment = fragment; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + definition.getActivity().getSupportFragmentManager().beginTransaction().add(layout.getId(), fragment, fragment.getTag()).commit(); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java new file mode 100644 index 0000000000..fd4ffdd524 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWHtmlLink.java @@ -0,0 +1,63 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.text.util.Linkify; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWValidator; + + +public class SWHtmlLink extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWHtmlLink.class); + private String textLabel = null; + + private TextView l; + private SWValidator visibilityValidator; + + public SWHtmlLink() { + super(Type.HTMLLINK); + } + + public SWHtmlLink label(int label) { + this.label = label; + return this; + } + + public SWHtmlLink label(String newLabel){ + this.textLabel = newLabel; + return this; + } + + public SWHtmlLink visibility(SWValidator visibilityValidator) { + this.visibilityValidator = visibilityValidator; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + l = new TextView(context); + l.setId(View.generateViewId()); + l.setAutoLinkMask(Linkify.ALL); + if(textLabel != null) + l.setText(textLabel); + else + l.setText(label); + layout.addView(l); + + } + + @Override + public void processVisibility() { + if (visibilityValidator != null && !visibilityValidator.isValid()) + l.setVisibility(View.GONE); + else + l.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java new file mode 100644 index 0000000000..91e5c2da2d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWInfotext.java @@ -0,0 +1,61 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.setupwizard.SWValidator; + + +public class SWInfotext extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWInfotext.class); + private String textLabel = null; + + private TextView l; + private SWValidator visibilityValidator; + + public SWInfotext() { + super(Type.TEXT); + } + + public SWInfotext label(int label) { + this.label = label; + return this; + } + + public SWInfotext label(String newLabel){ + this.textLabel = newLabel; + return this; + } + + public SWInfotext visibility(SWValidator visibilityValidator) { + this.visibilityValidator = visibilityValidator; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + + l = new TextView(context); + l.setId(View.generateViewId()); + if(textLabel != null) + l.setText(textLabel); + else + l.setText(label); + layout.addView(l); + + } + + @Override + public void processVisibility() { + if (visibilityValidator != null && !visibilityValidator.isValid()) + l.setVisibility(View.GONE); + else + l.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java new file mode 100644 index 0000000000..4f454b843d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWItem.java @@ -0,0 +1,86 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.view.View; +import android.widget.LinearLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.events.EventPreferenceChange; +import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; +import info.nightscout.utils.SP; + +public class SWItem { + private static Logger log = LoggerFactory.getLogger(SWItem.class); + + public enum Type { + NONE, + TEXT, + HTMLLINK, + BREAK, + LISTENER, + URL, + STRING, + NUMBER, + DECIMALNUMBER, + CHECKBOX, + RADIOBUTTON, + PLUGIN, + BUTTON, + FRAGMENT + } + + Type type; + Integer label; + Integer comment; + int preferenceId; + + + public SWItem(Type type) { + this.type = type; + } + + String getLabel() { + return MainApp.gs(label); + } + + String getComment() { + if (comment != null) + return MainApp.gs(comment); + else + return ""; + } + + Type getType() { + return type; + } + + public SWItem label(int label) { + this.label = label; + return this; + } + + public SWItem comment(int comment) { + this.comment = comment; + return this; + } + + public void save(String value) { + SP.putString(preferenceId, value); + MainApp.bus().post(new EventPreferenceChange(preferenceId)); + MainApp.bus().post(new EventSWUpdate()); + } + + public static LinearLayout generateLayout(View view) { + LinearLayout layout = (LinearLayout) view.findViewById(view.getId()); + layout.removeAllViews(); + return layout; + } + + public void generateDialog(LinearLayout layout) { + } + + public void processVisibility() { + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java new file mode 100644 index 0000000000..80f60eb678 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWPlugin.java @@ -0,0 +1,97 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.events.EventConfigBuilderChange; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.setupwizard.events.EventSWUpdate; + +public class SWPlugin extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWPlugin.class); + + private PluginType pType; + private RadioGroup radioGroup; + private int pluginDescription; + + private boolean makeVisible = true; + + public SWPlugin() { + super(Type.PLUGIN); + } + + public SWPlugin option(PluginType pType, int pluginDescription) { + this.pType = pType; + this.pluginDescription = pluginDescription; + return this; + } + + public SWPlugin makeVisible(boolean makeVisible) { + this.makeVisible = makeVisible; + return this; + } + + @Override + public void generateDialog(LinearLayout layout) { + + Context context = layout.getContext(); + radioGroup = new RadioGroup(context); + radioGroup.clearCheck(); + + ArrayList pluginsInCategory = MainApp.getSpecificPluginsList(pType); + + radioGroup.setOrientation(LinearLayout.VERTICAL); + radioGroup.setVisibility(View.VISIBLE); + + TextView pdesc = new TextView(context); + pdesc.setText(pluginDescription); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(0, 0, 0, 40); + pdesc.setLayoutParams(params); + layout.addView(pdesc); + + for (int i = 0; i < pluginsInCategory.size(); i++) { + RadioButton rdbtn = new RadioButton(context); + PluginBase p = pluginsInCategory.get(i); + rdbtn.setId(View.generateViewId()); + rdbtn.setText(p.getName()); + if (p.isEnabled(pType)) + rdbtn.setChecked(true); + rdbtn.setTag(p); + radioGroup.addView(rdbtn); + params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + params.setMargins(80, 0, 0, 0); + TextView desc = new TextView(context); + desc.setText(p.getDescription()); + desc.setLayoutParams(params); + radioGroup.addView(desc); + } + + radioGroup.setOnCheckedChangeListener((group, checkedId) -> { + RadioButton rb = group.findViewById(checkedId); + PluginBase plugin = (PluginBase) rb.getTag(); + plugin.setPluginEnabled(pType, rb.isChecked()); + plugin.setFragmentVisible(pType, rb.isChecked() && makeVisible); + ConfigBuilderFragment.processOnEnabledCategoryChanged(plugin, pType); + ConfigBuilderPlugin.getPlugin().storeSettings("SetupWizard"); + MainApp.bus().post(new EventConfigBuilderChange()); + MainApp.bus().post(new EventSWUpdate()); + }); + layout.addView(radioGroup); + super.generateDialog(layout); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java new file mode 100644 index 0000000000..80346c433c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/elements/SWRadioButton.java @@ -0,0 +1,73 @@ +package info.nightscout.androidaps.setupwizard.elements; + +import android.content.Context; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.utils.SP; + +public class SWRadioButton extends SWItem { + private static Logger log = LoggerFactory.getLogger(SWRadioButton.class); + + int labelsArray; + int valuesArray; + private RadioGroup radioGroup; + + public SWRadioButton() { + super(Type.RADIOBUTTON); + } + + public SWRadioButton option(int labels, int values) { + this.labelsArray = labels; + this.valuesArray = values; + return this; + } + + public String[] labels() { + return MainApp.sResources.getStringArray(labelsArray); + } + + public String[] values() { + return MainApp.sResources.getStringArray(valuesArray); + } + + @Override + public void generateDialog(LinearLayout layout) { + Context context = layout.getContext(); + // Get if there is already value in SP + String previousValue = SP.getString(preferenceId, "none"); + radioGroup = new RadioGroup(context); + radioGroup.clearCheck(); + radioGroup.setOrientation(LinearLayout.VERTICAL); + radioGroup.setVisibility(View.VISIBLE); + + for (int i = 0; i < labels().length; i++) { + RadioButton rdbtn = new RadioButton(context); + rdbtn.setId(View.generateViewId()); + rdbtn.setText(labels()[i]); + if (previousValue.equals(values()[i])) + rdbtn.setChecked(true); + rdbtn.setTag(i); + radioGroup.addView(rdbtn); + } + + radioGroup.setOnCheckedChangeListener((group, checkedId) -> { + int i = (int) group.findViewById(checkedId).getTag(); + save(values()[i]); + }); + layout.addView(radioGroup); + super.generateDialog(layout); + } + + public SWRadioButton preferenceId(int preferenceId) { + this.preferenceId = preferenceId; + return this; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWLabel.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWLabel.java new file mode 100644 index 0000000000..f0f4ff7628 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWLabel.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.setupwizard.events; + +import info.nightscout.androidaps.events.Event; + +public class EventSWLabel extends Event { + public String label; + + public EventSWLabel(String label) { + this.label = label; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java new file mode 100644 index 0000000000..181960ac40 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/events/EventSWUpdate.java @@ -0,0 +1,14 @@ +package info.nightscout.androidaps.setupwizard.events; + +import info.nightscout.androidaps.events.Event; + +public class EventSWUpdate extends Event { + public boolean redraw = false; + + public EventSWUpdate() { + } + + public EventSWUpdate(boolean redraw) { + this.redraw = redraw; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabLayout.java b/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabLayout.java deleted file mode 100644 index 80424a04f7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabLayout.java +++ /dev/null @@ -1,322 +0,0 @@ -package info.nightscout.androidaps.tabs;/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import android.content.Context; -import android.graphics.Typeface; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.HorizontalScrollView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import info.nightscout.androidaps.R; - -/** - * To be used with ViewPager to provide a tab indicator component which give constant feedback as to - * the user's scroll progress. - *

- * To use the component, simply add it to your view hierarchy. Then in your - * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call - * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. - *

- * The colors can be customized in two ways. The first and simplest is to provide an array of colors - * via {@link #setSelectedIndicatorColors(int...)}. The - * alternative is via the {@link TabColorizer} interface which provides you complete control over - * which color is used for any individual position. - *

- * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, - * providing the layout ID of your custom layout. - */ -public class SlidingTabLayout extends HorizontalScrollView { - /** - * Allows complete control over the colors drawn in the tab layout. Set with - * {@link #setCustomTabColorizer(TabColorizer)}. - */ - public interface TabColorizer { - - /** - * @return return the color of the indicator used when {@code position} is selected. - */ - int getIndicatorColor(int position); - - } - - private static final int TITLE_OFFSET_DIPS = 24; - private static final int TAB_VIEW_PADDING_DIPS = 9; - private static final int TAB_VIEW_TEXT_SIZE_SP = 12; - - private int mTitleOffset; - - private int mTabViewLayoutId; - private int mTabViewTextViewId; - private boolean mDistributeEvenly; - - private ViewPager mViewPager; - private SparseArray mContentDescriptions = new SparseArray(); - private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; - - private final SlidingTabStrip mTabStrip; - - public SlidingTabLayout(Context context) { - this(context, null); - } - - public SlidingTabLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - // Disable the Scroll Bar - setHorizontalScrollBarEnabled(false); - // Make sure that the Tab Strips fills this View - setFillViewport(true); - - mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); - - mTabStrip = new SlidingTabStrip(context); - addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - setBackgroundColor(context.getResources().getColor(R.color.tabBgColor)); - } - - /** - * Set the custom {@link TabColorizer} to be used. - * - * If you only require simple custmisation then you can use - * {@link #setSelectedIndicatorColors(int...)} to achieve - * similar effects. - */ - public void setCustomTabColorizer(TabColorizer tabColorizer) { - mTabStrip.setCustomTabColorizer(tabColorizer); - } - - public void setDistributeEvenly(boolean distributeEvenly) { - mDistributeEvenly = distributeEvenly; - } - - /** - * Sets the colors to be used for indicating the selected tab. These colors are treated as a - * circular array. Providing one color will mean that all tabs are indicated with the same color. - */ - public void setSelectedIndicatorColors(int... colors) { - mTabStrip.setSelectedIndicatorColors(colors); - } - - /** - * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are - * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so - * that the layout can update it's scroll position correctly. - * - * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) - */ - public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { - mViewPagerPageChangeListener = listener; - } - - /** - * Set the custom layout to be inflated for the tab views. - * - * @param layoutResId Layout id to be inflated - * @param textViewId id of the {@link TextView} in the inflated view - */ - public void setCustomTabView(int layoutResId, int textViewId) { - mTabViewLayoutId = layoutResId; - mTabViewTextViewId = textViewId; - } - - /** - * Sets the associated view pager. Note that the assumption here is that the pager content - * (number of tabs and tab titles) does not change after this call has been made. - */ - public void setViewPager(ViewPager viewPager) { - mTabStrip.removeAllViews(); - - mViewPager = viewPager; - if (viewPager != null) { - viewPager.setOnPageChangeListener(new InternalViewPagerListener()); - populateTabStrip(); - } - } - - /** - * Create a default view to be used for tabs. This is called if a custom tab view is not set via - * {@link #setCustomTabView(int, int)}. - */ - protected TextView createDefaultTabView(Context context) { - TextView textView = new TextView(context); - textView.setGravity(Gravity.CENTER); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); - textView.setTypeface(Typeface.DEFAULT_BOLD); - textView.setLayoutParams(new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - TypedValue outValue = new TypedValue(); - getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true); - textView.setBackgroundResource(outValue.resourceId); - textView.setAllCaps(true); - - int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); - textView.setPadding(padding, padding, padding, padding); - - return textView; - } - - private void populateTabStrip() { - final PagerAdapter adapter = mViewPager.getAdapter(); - final View.OnClickListener tabClickListener = new TabClickListener(); - - for (int i = 0; i < adapter.getCount(); i++) { - View tabView = null; - TextView tabTitleView = null; - - if (mTabViewLayoutId != 0) { - // If there is a custom tab view layout id set, try and inflate it - tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, - false); - tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); - } - - if (tabView == null) { - tabView = createDefaultTabView(getContext()); - } - - if (tabTitleView == null && TextView.class.isInstance(tabView)) { - tabTitleView = (TextView) tabView; - } - - if (mDistributeEvenly) { - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams(); - lp.width = 0; - lp.weight = 1; - } - - tabTitleView.setText(adapter.getPageTitle(i)); - tabView.setOnClickListener(tabClickListener); - String desc = mContentDescriptions.get(i, null); - if (desc != null) { - tabView.setContentDescription(desc); - } - - mTabStrip.addView(tabView); - if (i == mViewPager.getCurrentItem()) { - tabView.setSelected(true); - } - } - } - - public void setContentDescription(int i, String desc) { - mContentDescriptions.put(i, desc); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mViewPager != null) { - scrollToTab(mViewPager.getCurrentItem(), 0); - } - } - - private void scrollToTab(int tabIndex, int positionOffset) { - final int tabStripChildCount = mTabStrip.getChildCount(); - if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { - return; - } - - View selectedChild = mTabStrip.getChildAt(tabIndex); - if (selectedChild != null) { - int targetScrollX = selectedChild.getLeft() + positionOffset; - - if (tabIndex > 0 || positionOffset > 0) { - // If we're not at the first child and are mid-scroll, make sure we obey the offset - targetScrollX -= mTitleOffset; - } - - scrollTo(targetScrollX, 0); - } - } - - private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { - private int mScrollState; - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - int tabStripChildCount = mTabStrip.getChildCount(); - if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { - return; - } - - mTabStrip.onViewPagerPageChanged(position, positionOffset); - - View selectedTitle = mTabStrip.getChildAt(position); - int extraOffset = (selectedTitle != null) - ? (int) (positionOffset * selectedTitle.getWidth()) - : 0; - scrollToTab(position, extraOffset); - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, - positionOffsetPixels); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - mScrollState = state; - - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageScrollStateChanged(state); - } - } - - @Override - public void onPageSelected(int position) { - if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { - mTabStrip.onViewPagerPageChanged(position, 0f); - scrollToTab(position, 0); - } - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - mTabStrip.getChildAt(i).setSelected(position == i); - } - if (mViewPagerPageChangeListener != null) { - mViewPagerPageChangeListener.onPageSelected(position); - } - } - - } - - private class TabClickListener implements View.OnClickListener { - @Override - public void onClick(View v) { - for (int i = 0; i < mTabStrip.getChildCount(); i++) { - if (v == mTabStrip.getChildAt(i)) { - mViewPager.setCurrentItem(i); - return; - } - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabStrip.java b/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabStrip.java deleted file mode 100644 index 6c8e034477..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/tabs/SlidingTabStrip.java +++ /dev/null @@ -1,165 +0,0 @@ -package info.nightscout.androidaps.tabs;/* - * Copyright 2014 Google Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.View; -import android.widget.LinearLayout; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; - -class SlidingTabStrip extends LinearLayout { - - private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0; - private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; - private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3; - - private final int mBottomBorderThickness; - private final Paint mBottomBorderPaint; - - private final int mSelectedIndicatorThickness; - private final Paint mSelectedIndicatorPaint; - - private int mSelectedPosition; - private float mSelectionOffset; - - private SlidingTabLayout.TabColorizer mCustomTabColorizer; - private final SimpleTabColorizer mDefaultTabColorizer; - - SlidingTabStrip(Context context) { - this(context, null); - } - - SlidingTabStrip(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - - final float density = getResources().getDisplayMetrics().density; - - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true); - final int themeForegroundColor = outValue.data; - - int defaultBottomBorderColor = setColorAlpha(themeForegroundColor, - DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); - - mDefaultTabColorizer = new SimpleTabColorizer(); - mDefaultTabColorizer.setIndicatorColors(MainApp.gc(R.color.tabBgColorSelected)); - - mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); - mBottomBorderPaint = new Paint(); - mBottomBorderPaint.setColor(defaultBottomBorderColor); - - mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); - mSelectedIndicatorPaint = new Paint(); - } - - void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { - mCustomTabColorizer = customTabColorizer; - invalidate(); - } - - void setSelectedIndicatorColors(int... colors) { - // Make sure that the custom colorizer is removed - mCustomTabColorizer = null; - mDefaultTabColorizer.setIndicatorColors(colors); - invalidate(); - } - - void onViewPagerPageChanged(int position, float positionOffset) { - mSelectedPosition = position; - mSelectionOffset = positionOffset; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - final int height = getHeight(); - final int childCount = getChildCount(); - final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null - ? mCustomTabColorizer - : mDefaultTabColorizer; - - // Thick colored underline below the current selection - if (childCount > 0) { - View selectedTitle = getChildAt(mSelectedPosition); - int left = selectedTitle.getLeft(); - int right = selectedTitle.getRight(); - int color = tabColorizer.getIndicatorColor(mSelectedPosition); - - if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { - int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); - if (color != nextColor) { - color = blendColors(nextColor, color, mSelectionOffset); - } - - // Draw the selection partway between the tabs - View nextTitle = getChildAt(mSelectedPosition + 1); - left = (int) (mSelectionOffset * nextTitle.getLeft() + - (1.0f - mSelectionOffset) * left); - right = (int) (mSelectionOffset * nextTitle.getRight() + - (1.0f - mSelectionOffset) * right); - } - - mSelectedIndicatorPaint.setColor(color); - - canvas.drawRect(left, height - mSelectedIndicatorThickness, right, - height, mSelectedIndicatorPaint); - } - - // Thin underline along the entire bottom edge - canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); - } - - /** - * Set the alpha value of the {@code color} to be the given {@code alpha} value. - */ - private static int setColorAlpha(int color, byte alpha) { - return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - /** - * Blend {@code color1} and {@code color2} using the given ratio. - * - * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, - * 0.0 will return {@code color2}. - */ - private static int blendColors(int color1, int color2, float ratio) { - final float inverseRation = 1f - ratio; - float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); - float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); - float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); - return Color.rgb((int) r, (int) g, (int) b); - } - - private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { - private int[] mIndicatorColors; - - @Override - public final int getIndicatorColor(int position) { - return mIndicatorColors[position % mIndicatorColors.length]; - } - - void setIndicatorColors(int... colors) { - mIndicatorColors = colors; - } - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java index 8397da97aa..79037ebcd8 100644 --- a/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java +++ b/app/src/main/java/info/nightscout/androidaps/tabs/TabPageAdapter.java @@ -3,9 +3,11 @@ package info.nightscout.androidaps.tabs; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter; import android.view.ViewGroup; @@ -19,7 +21,7 @@ import info.nightscout.androidaps.interfaces.PluginBase; /** * Created by mike on 30.05.2016. */ -public class TabPageAdapter extends FragmentStatePagerAdapter { +public class TabPageAdapter extends FragmentPagerAdapter { ArrayList visibleFragmentList = new ArrayList<>(); @@ -39,6 +41,10 @@ public class TabPageAdapter extends FragmentStatePagerAdapter { return Fragment.instantiate(context, visibleFragmentList.get(position).pluginDescription.getFragmentClass()); } + public PluginBase getPluginAt(int position) { + return visibleFragmentList.get(position); + } + @Override public void finishUpdate(ViewGroup container) { try{ @@ -72,5 +78,8 @@ public class TabPageAdapter extends FragmentStatePagerAdapter { } } - + @Override + public long getItemId(int position) { + return System.identityHashCode(visibleFragmentList.get(position)); + } } diff --git a/app/src/main/java/info/nightscout/utils/AndroidPermission.java b/app/src/main/java/info/nightscout/utils/AndroidPermission.java new file mode 100644 index 0000000000..ca3cac82f1 --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/AndroidPermission.java @@ -0,0 +1,94 @@ +package info.nightscout.utils; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; +import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.Overview.notifications.Notification; +import info.nightscout.androidaps.plugins.Overview.notifications.NotificationWithAction; + +public class AndroidPermission { + + public static final int CASE_STORAGE = 0x1; + public static final int CASE_SMS = 0x2; + public static final int CASE_LOCATION = 0x3; + public static final int CASE_BATTERY = 0x4; + + public static void askForPermission(Activity activity, String[] permission, Integer requestCode) { + boolean test = false; + for (int i = 0; i < permission.length; i++) { + test = test || (ContextCompat.checkSelfPermission(activity, permission[i]) != PackageManager.PERMISSION_GRANTED); + } + if (test) { + ActivityCompat.requestPermissions(activity, permission, requestCode); + } + } + + public static void askForPermission(Activity activity, String permission, Integer requestCode) { + String[] permissions = {permission}; + + if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) + ActivityCompat.requestPermissions(activity, permissions, requestCode); + } + + public static boolean checkForPermission(Context context, String permission) { + return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; + } + + public static synchronized void notifyForSMSPermissions(Activity activity) { + if (SP.getBoolean(R.string.smscommunicator_remotecommandsallowed, false)) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { + if (!checkForPermission(activity, Manifest.permission.RECEIVE_SMS)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_SMS, MainApp.gs(R.string.smscommunicator_missingsmspermission), Notification.URGENT); + notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.RECEIVE_SMS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_MMS}, AndroidPermission.CASE_SMS)); + MainApp.bus().post(new EventNewNotification(notification)); + } else + MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_SMS)); + } + } + } + + public static synchronized void notifyForBatteryOptimizationPermission(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!checkForPermission(activity, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_BATTERY, String.format(MainApp.gs(R.string.needwhitelisting), MainApp.gs(R.string.app_name)), Notification.URGENT); + notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}, AndroidPermission.CASE_BATTERY)); + MainApp.bus().post(new EventNewNotification(notification)); + } else + MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_BATTERY)); + } + } + + public static synchronized void notifyForStoragePermission(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!checkForPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_STORAGE, MainApp.gs(R.string.needstoragepermission), Notification.URGENT); + notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, AndroidPermission.CASE_STORAGE)); + MainApp.bus().post(new EventNewNotification(notification)); + } else + MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_STORAGE)); + } + } + + public static synchronized void notifyForLocationPermissions(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!checkForPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION)) { + NotificationWithAction notification = new NotificationWithAction(Notification.PERMISSION_LOCATION, MainApp.gs(R.string.needlocationpermission), Notification.URGENT); + notification.action(MainApp.gs(R.string.request), () -> AndroidPermission.askForPermission(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, AndroidPermission.CASE_LOCATION)); + MainApp.bus().post(new EventNewNotification(notification)); + } else + MainApp.bus().post(new EventDismissNotification(Notification.PERMISSION_LOCATION)); + } + } +} diff --git a/app/src/main/java/info/nightscout/utils/DateUtil.java b/app/src/main/java/info/nightscout/utils/DateUtil.java index 441449e8ee..c2164fd40e 100644 --- a/app/src/main/java/info/nightscout/utils/DateUtil.java +++ b/app/src/main/java/info/nightscout/utils/DateUtil.java @@ -135,6 +135,11 @@ public class DateUtil { return MainApp.gs(R.string.minago, mins); } + public static String minAgoShort(long time) { + Integer mins = (int) ((time - now()) / 1000 / 60); + return (mins > 0 ? "+" : "") + mins.toString(); + } + public static String hourAgo(long time) { double hours = (now() - time) / 1000d / 60 / 60; return MainApp.gs(R.string.hoursago, hours); @@ -174,5 +179,4 @@ public class DateUtil { public static long roundDateToSec(long date) { return date - date % 1000; } - } diff --git a/app/src/main/java/info/nightscout/utils/FabricPrivacy.java b/app/src/main/java/info/nightscout/utils/FabricPrivacy.java index bd117ed5c3..ad498fa4f7 100644 --- a/app/src/main/java/info/nightscout/utils/FabricPrivacy.java +++ b/app/src/main/java/info/nightscout/utils/FabricPrivacy.java @@ -3,6 +3,13 @@ package info.nightscout.utils; import com.crashlytics.android.Crashlytics; import com.crashlytics.android.answers.Answers; import com.crashlytics.android.answers.CustomEvent; +import info.nightscout.androidaps.BuildConfig; +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.PluginBase; + +import java.util.Date; /** * Created by jamorham on 21/02/2018. @@ -80,4 +87,51 @@ public class FabricPrivacy { } } + public static void uploadDailyStats() { + if (!fabricEnabled()) return; + + long lastUploadDay = SP.getLong(MainApp.gs(R.string.key_plugin_stats_report_timestamp), 0L); + + Date date = new Date(); + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + long today = date.getTime() - date.getTime() % 1000; + + if (today > lastUploadDay) { + uploadAppUsageType(); + uploadPluginStats(); + + SP.putLong(MainApp.gs(R.string.key_plugin_stats_report_timestamp), today); + } + } + + private static void uploadPluginStats() { + CustomEvent pluginStats = new CustomEvent("PluginStats"); + pluginStats.putCustomAttribute("version", BuildConfig.VERSION); + for (PluginBase plugin : MainApp.getPluginsList()) { + if (plugin.isEnabled(plugin.getType()) && !plugin.pluginDescription.alwaysEnabled) { + pluginStats.putCustomAttribute(plugin.getClass().getSimpleName(), "enabled"); + } + } + + getInstance().logCustom(pluginStats); + } + + private static void uploadAppUsageType() { + CustomEvent type = new CustomEvent("AppUsageType"); + if (Config.NSCLIENT) + type.putCustomAttribute("type", "NSClient"); + else if (Config.G5UPLOADER) + type.putCustomAttribute("type", "G5Uploader"); + else if (Config.PUMPCONTROL) + type.putCustomAttribute("type", "PumpControl"); + else if (MainApp.getConstraintChecker().isClosedLoopAllowed().value()) + type.putCustomAttribute("type", "ClosedLoop"); + else + type.putCustomAttribute("type", "OpenLoop"); + + getInstance().logCustom(type); + } + } diff --git a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java index c2fbbf581e..22175e0281 100644 --- a/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java +++ b/app/src/main/java/info/nightscout/utils/ImportExportPrefs.java @@ -33,7 +33,7 @@ import info.nightscout.androidaps.events.EventAppExit; public class ImportExportPrefs { private static Logger log = LoggerFactory.getLogger(ImportExportPrefs.class); static File path = new File(Environment.getExternalStorageDirectory().toString()); - static final File file = new File(path, MainApp.gs(R.string.app_name) + "Preferences"); + static public final File file = new File(path, MainApp.gs(R.string.app_name) + "Preferences"); private static final int REQUEST_EXTERNAL_STORAGE = 1; private static String[] PERMISSIONS_STORAGE = { diff --git a/app/src/main/java/info/nightscout/utils/NSUpload.java b/app/src/main/java/info/nightscout/utils/NSUpload.java index 2e5506f9c6..00d5e39821 100644 --- a/app/src/main/java/info/nightscout/utils/NSUpload.java +++ b/app/src/main/java/info/nightscout/utils/NSUpload.java @@ -289,19 +289,7 @@ public class NSUpload { public static void uploadProfileSwitch(ProfileSwitch profileSwitch) { try { - JSONObject data = new JSONObject(); - data.put("eventType", CareportalEvent.PROFILESWITCH); - data.put("duration", profileSwitch.durationInMinutes); - data.put("profile", profileSwitch.getCustomizedName()); - data.put("profileJson", profileSwitch.profileJson); - data.put("profilePlugin", profileSwitch.profilePlugin); - if (profileSwitch.isCPP) { - data.put("CircadianPercentageProfile", true); - data.put("timeshift", profileSwitch.timeshift); - data.put("percentage", profileSwitch.percentage); - } - data.put("created_at", DateUtil.toISOString(profileSwitch.date)); - data.put("enteredBy", MainApp.gs(R.string.app_name)); + JSONObject data = getJson(profileSwitch); uploadCareportalEntryToNS(data); } catch (JSONException e) { log.error("Unhandled exception", e); @@ -334,19 +322,7 @@ public class NSUpload { public static void updateProfileSwitch(ProfileSwitch profileSwitch) { try { - JSONObject data = new JSONObject(); - data.put("eventType", CareportalEvent.PROFILESWITCH); - data.put("duration", profileSwitch.durationInMinutes); - data.put("profile", profileSwitch.getCustomizedName()); - data.put("profileJson", profileSwitch.profileJson); - data.put("profilePlugin", profileSwitch.profilePlugin); - if (profileSwitch.isCPP) { - data.put("CircadianPercentageProfile", true); - data.put("timeshift", profileSwitch.timeshift); - data.put("percentage", profileSwitch.percentage); - } - data.put("created_at", DateUtil.toISOString(profileSwitch.date)); - data.put("enteredBy", MainApp.gs(R.string.app_name)); + JSONObject data = getJson(profileSwitch); if (profileSwitch._id != null) { Context context = MainApp.instance().getApplicationContext(); Bundle bundle = new Bundle(); @@ -365,6 +341,24 @@ public class NSUpload { } } + private static JSONObject getJson(ProfileSwitch profileSwitch) throws JSONException { + JSONObject data = new JSONObject(); + data.put("eventType", CareportalEvent.PROFILESWITCH); + data.put("duration", profileSwitch.durationInMinutes); + data.put("profile", profileSwitch.getCustomizedName()); + data.put("profileJson", profileSwitch.profileJson); + data.put("profilePlugin", profileSwitch.profilePlugin); + if (profileSwitch.isCPP) { + data.put("CircadianPercentageProfile", true); + data.put("timeshift", profileSwitch.timeshift); + data.put("percentage", profileSwitch.percentage); + } + data.put("created_at", DateUtil.toISOString(profileSwitch.date)); + data.put("enteredBy", MainApp.gs(R.string.app_name)); + + return data; + } + public static void uploadCareportalEntryToNS(JSONObject data) { try { if (data.has("preBolus") && data.has("carbs")) { diff --git a/app/src/main/java/info/nightscout/utils/PercentageSplitter.java b/app/src/main/java/info/nightscout/utils/PercentageSplitter.java index 53e50466b5..40dfdf2455 100644 --- a/app/src/main/java/info/nightscout/utils/PercentageSplitter.java +++ b/app/src/main/java/info/nightscout/utils/PercentageSplitter.java @@ -8,14 +8,23 @@ import java.util.regex.Pattern; */ public class PercentageSplitter { + // "Profile name (200%,2h)" + private static final Pattern percentagePattern = Pattern.compile("(.+)\\(\\d+%,\\d+h\\)"); + // "Profile name (200%)" + private static final Pattern percentageShiftPattern = Pattern.compile("(.+)\\(\\d+%\\)"); + + /** Removes the suffix for percentage and timeshift from a profile name. */ public static String pureName(String name) { - String newName = name; - String s = "(.*)\\((\\d+)\\%\\)"; - Pattern r = Pattern.compile(s); - Matcher m = r.matcher(name); - if (m.find()) { - newName = m.group(1); + Matcher percentageMatch = percentagePattern.matcher(name); + if (percentageMatch.find()) { + return percentageMatch.group(1).trim(); } - return newName; + + Matcher percentageShiftMatch = percentageShiftPattern.matcher(name); + if (percentageShiftMatch.find()) { + return percentageShiftMatch.group(1).trim(); + } + + return name; } } diff --git a/app/src/main/java/info/nightscout/utils/SP.java b/app/src/main/java/info/nightscout/utils/SP.java index e7bb5bc16d..35b97b5336 100644 --- a/app/src/main/java/info/nightscout/utils/SP.java +++ b/app/src/main/java/info/nightscout/utils/SP.java @@ -16,6 +16,10 @@ public class SP { return sharedPreferences.contains(key); } + static public boolean contains(int resourceId) { + return sharedPreferences.contains(MainApp.gs(resourceId)); + } + static public String getString(int resourceID, String defaultValue) { return sharedPreferences.getString(MainApp.gs(resourceID), defaultValue); } diff --git a/app/src/main/java/info/nightscout/utils/StringUtils.java b/app/src/main/java/info/nightscout/utils/StringUtils.java new file mode 100644 index 0000000000..de16f7964e --- /dev/null +++ b/app/src/main/java/info/nightscout/utils/StringUtils.java @@ -0,0 +1,20 @@ +package info.nightscout.utils; + +/** + * class contains useful String functions + */ +public class StringUtils { + + private StringUtils() { + // this constructor is private, since this class should not get instantiated + } + + public static String removeSurroundingQuotes(String string) { + if (string.length() >= 2 && string.charAt(0) == '"' + && string.charAt(string.length() - 1) == '"') { + string = string.substring(1, string.length() - 1); + } + + return string; + } +} diff --git a/app/src/main/java/info/nightscout/utils/T.java b/app/src/main/java/info/nightscout/utils/T.java index ce8541dd8c..99f4e573c3 100644 --- a/app/src/main/java/info/nightscout/utils/T.java +++ b/app/src/main/java/info/nightscout/utils/T.java @@ -7,6 +7,12 @@ package info.nightscout.utils; public class T { private long time; // in msec + public static T now() { + T t = new T(); + t.time = System.currentTimeMillis(); + return t; + } + public static T msecs(long msec) { T t = new T(); t.time = msec; @@ -56,4 +62,12 @@ public class T { public long days() { return time / 24 / 60 / 60 / 1000L; } + + public T plus(T plus) { + return T.msecs(time + plus.time); + } + + public T minus(T minus) { + return T.msecs(time - minus.time); + } } diff --git a/app/src/main/res/drawable-hdpi-v11/ic_notification.png b/app/src/main/res/drawable-hdpi-v11/ic_notification.png deleted file mode 100644 index 1e28cbeb7a..0000000000 Binary files a/app/src/main/res/drawable-hdpi-v11/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi-v9/ic_notification.png b/app/src/main/res/drawable-hdpi-v9/ic_notification.png deleted file mode 100644 index ad1bcf514f..0000000000 Binary files a/app/src/main/res/drawable-hdpi-v9/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_exit_to_app.png b/app/src/main/res/drawable-hdpi/ic_exit_to_app.png new file mode 100644 index 0000000000..f028ac5539 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_exit_to_app.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png index bb8248c78f..453ed7b382 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_notification.png and b/app/src/main/res/drawable-hdpi/ic_notification.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_visibility.png b/app/src/main/res/drawable-hdpi/ic_visibility.png new file mode 100644 index 0000000000..a014628e30 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_visibility.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_danar_useropthdpi.png b/app/src/main/res/drawable-hdpi/icon_danar_useropthdpi.png new file mode 100644 index 0000000000..ee3ab7725e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_danar_useropthdpi.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_local_activatehdpi.png b/app/src/main/res/drawable-hdpi/icon_local_activatehdpi.png new file mode 100644 index 0000000000..4ef567ffb6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_activatehdpi.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_local_resethdpi.png b/app/src/main/res/drawable-hdpi/icon_local_resethdpi.png new file mode 100644 index 0000000000..7d38cf0b50 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_resethdpi.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_local_savehdpi.png b/app/src/main/res/drawable-hdpi/icon_local_savehdpi.png new file mode 100644 index 0000000000..e5c1d5cedc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_local_savehdpi.png differ diff --git a/app/src/main/res/drawable-hdpi/nsclient_smallicon.png b/app/src/main/res/drawable-hdpi/nsclient_smallicon.png new file mode 100644 index 0000000000..e6d52996a6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/nsclient_smallicon.png differ diff --git a/app/src/main/res/drawable-mdpi-v11/ic_notification.png b/app/src/main/res/drawable-mdpi-v11/ic_notification.png deleted file mode 100644 index fbed7675b1..0000000000 Binary files a/app/src/main/res/drawable-mdpi-v11/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi-v9/ic_notification.png b/app/src/main/res/drawable-mdpi-v9/ic_notification.png deleted file mode 100644 index 3be3f1383f..0000000000 Binary files a/app/src/main/res/drawable-mdpi-v9/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_exit_to_app.png b/app/src/main/res/drawable-mdpi/ic_exit_to_app.png new file mode 100644 index 0000000000..d2531b3e46 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_exit_to_app.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_notification.png b/app/src/main/res/drawable-mdpi/ic_notification.png index c19b973e2a..c483ada178 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_notification.png and b/app/src/main/res/drawable-mdpi/ic_notification.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_visibility.png b/app/src/main/res/drawable-mdpi/ic_visibility.png new file mode 100644 index 0000000000..d5a9dc9ca4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_visibility.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_danar_useroptmdpi.png b/app/src/main/res/drawable-mdpi/icon_danar_useroptmdpi.png new file mode 100644 index 0000000000..1fadcd446b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_danar_useroptmdpi.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_activatemdpi.png b/app/src/main/res/drawable-mdpi/icon_local_activatemdpi.png new file mode 100644 index 0000000000..b39c72b407 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_activatemdpi.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_resetmdpi.png b/app/src/main/res/drawable-mdpi/icon_local_resetmdpi.png new file mode 100644 index 0000000000..ad2e0eaf6d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_resetmdpi.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_local_savemdpi.png b/app/src/main/res/drawable-mdpi/icon_local_savemdpi.png new file mode 100644 index 0000000000..843beed523 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_local_savemdpi.png differ diff --git a/app/src/main/res/drawable-mdpi/nsclient_smallicon.png b/app/src/main/res/drawable-mdpi/nsclient_smallicon.png new file mode 100644 index 0000000000..0e55ac39eb Binary files /dev/null and b/app/src/main/res/drawable-mdpi/nsclient_smallicon.png differ diff --git a/app/src/main/res/drawable-xhdpi-v11/ic_notification.png b/app/src/main/res/drawable-xhdpi-v11/ic_notification.png deleted file mode 100644 index 0427fc6b02..0000000000 Binary files a/app/src/main/res/drawable-xhdpi-v11/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi-v9/ic_notification.png b/app/src/main/res/drawable-xhdpi-v9/ic_notification.png deleted file mode 100644 index b779bd4090..0000000000 Binary files a/app/src/main/res/drawable-xhdpi-v9/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_exit_to_app.png b/app/src/main/res/drawable-xhdpi/ic_exit_to_app.png new file mode 100644 index 0000000000..d35a1f0d4e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_exit_to_app.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_notification.png b/app/src/main/res/drawable-xhdpi/ic_notification.png index 9eb0dcf193..56c4049e84 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_notification.png and b/app/src/main/res/drawable-xhdpi/ic_notification.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_visibility.png b/app/src/main/res/drawable-xhdpi/ic_visibility.png new file mode 100644 index 0000000000..1e16fb934f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_visibility.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_danar_useroptxhdpi.png b/app/src/main/res/drawable-xhdpi/icon_danar_useroptxhdpi.png new file mode 100644 index 0000000000..126122a570 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_danar_useroptxhdpi.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_activatexhdpi.png b/app/src/main/res/drawable-xhdpi/icon_local_activatexhdpi.png new file mode 100644 index 0000000000..75e9af38ed Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_activatexhdpi.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_resetxhdpi.png b/app/src/main/res/drawable-xhdpi/icon_local_resetxhdpi.png new file mode 100644 index 0000000000..2813cb448d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_resetxhdpi.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_local_savexhdpi.png b/app/src/main/res/drawable-xhdpi/icon_local_savexhdpi.png new file mode 100644 index 0000000000..0cf81430cc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_local_savexhdpi.png differ diff --git a/app/src/main/res/drawable-xhdpi/nsclient_smallicon.png b/app/src/main/res/drawable-xhdpi/nsclient_smallicon.png new file mode 100644 index 0000000000..0ec8b219b6 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/nsclient_smallicon.png differ diff --git a/app/src/main/res/drawable-xxhdpi-v11/ic_notification.png b/app/src/main/res/drawable-xxhdpi-v11/ic_notification.png deleted file mode 100644 index 8a586cce76..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi-v11/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi-v9/ic_notification.png b/app/src/main/res/drawable-xxhdpi-v9/ic_notification.png deleted file mode 100644 index b3cd107f78..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi-v9/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_exit_to_app.png b/app/src/main/res/drawable-xxhdpi/ic_exit_to_app.png new file mode 100644 index 0000000000..884eb3b242 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_exit_to_app.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png index 9da9b83a84..6ef4df1823 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_notification.png and b/app/src/main/res/drawable-xxhdpi/ic_notification.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_visibility.png b/app/src/main/res/drawable-xxhdpi/ic_visibility.png new file mode 100644 index 0000000000..dbc192368f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_visibility.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_danar_useroptxxhdpi.png b/app/src/main/res/drawable-xxhdpi/icon_danar_useroptxxhdpi.png new file mode 100644 index 0000000000..b2a8eb9313 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_danar_useroptxxhdpi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_activatexxhdpi.png b/app/src/main/res/drawable-xxhdpi/icon_local_activatexxhdpi.png new file mode 100644 index 0000000000..600e6ee61c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_activatexxhdpi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_resetxxhdpi.png b/app/src/main/res/drawable-xxhdpi/icon_local_resetxxhdpi.png new file mode 100644 index 0000000000..dc9d659dd7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_resetxxhdpi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_local_savexxhdpi.png b/app/src/main/res/drawable-xxhdpi/icon_local_savexxhdpi.png new file mode 100644 index 0000000000..20448e4bf9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_local_savexxhdpi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/nsclient_smallicon.png b/app/src/main/res/drawable-xxhdpi/nsclient_smallicon.png new file mode 100644 index 0000000000..ce7eb909ff Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/nsclient_smallicon.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app.png b/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app.png new file mode 100644 index 0000000000..903a7230fd Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_exit_to_app.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_visibility.png b/app/src/main/res/drawable-xxxhdpi/ic_visibility.png new file mode 100644 index 0000000000..882eacdee5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_visibility.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_danar_useroptxxxhdpi.png b/app/src/main/res/drawable-xxxhdpi/icon_danar_useroptxxxhdpi.png new file mode 100644 index 0000000000..52f952ced4 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_danar_useroptxxxhdpi.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_activatexxxhdpi.png b/app/src/main/res/drawable-xxxhdpi/icon_local_activatexxxhdpi.png new file mode 100644 index 0000000000..2de9ce9d4d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_activatexxxhdpi.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_resetxxxhdpi.png b/app/src/main/res/drawable-xxxhdpi/icon_local_resetxxxhdpi.png new file mode 100644 index 0000000000..ec54479149 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_resetxxxhdpi.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/icon_local_savexxxhdpi.png b/app/src/main/res/drawable-xxxhdpi/icon_local_savexxxhdpi.png new file mode 100644 index 0000000000..f2ecc45ac0 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/icon_local_savexxxhdpi.png differ diff --git a/app/src/main/res/drawable/icon_danar_useropt.png b/app/src/main/res/drawable/icon_danar_useropt.png new file mode 100644 index 0000000000..1fadcd446b Binary files /dev/null and b/app/src/main/res/drawable/icon_danar_useropt.png differ diff --git a/app/src/main/res/drawable/notif_icon.png b/app/src/main/res/drawable/notif_icon.png index 1b89799dc6..480d0eaf77 100644 Binary files a/app/src/main/res/drawable/notif_icon.png and b/app/src/main/res/drawable/notif_icon.png differ diff --git a/app/src/main/res/drawable/visibility_black_16x16.png b/app/src/main/res/drawable/visibility_black_16x16.png deleted file mode 100644 index d24867dcdf..0000000000 Binary files a/app/src/main/res/drawable/visibility_black_16x16.png and /dev/null differ diff --git a/app/src/main/res/layout/actions_fill_dialog.xml b/app/src/main/res/layout/actions_fill_dialog.xml index 2883485bf7..21fb5d3c66 100644 --- a/app/src/main/res/layout/actions_fill_dialog.xml +++ b/app/src/main/res/layout/actions_fill_dialog.xml @@ -11,27 +11,24 @@ android:padding="10dp"> diff --git a/app/src/main/res/layout/activity_historybrowse.xml b/app/src/main/res/layout/activity_historybrowse.xml index 1d52560eff..f05672e765 100644 --- a/app/src/main/res/layout/activity_historybrowse.xml +++ b/app/src/main/res/layout/activity_historybrowse.xml @@ -113,7 +113,8 @@ + android:layout_height="wrap_content" + android:visibility="gone"/> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 75b54a74cb..c0f382fbb8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,46 +1,59 @@ - + android:layout_height="match_parent" + android:clipChildren="false" + android:clipToPadding="false"> - + android:layout_height="?attr/actionBarSize" + android:background="@color/colorPrimary" + app:contentInsetEndWithActions="48dp" + app:contentInsetStartWithNavigation="48dp" + android:elevation="4dp"> - + android:background="@android:color/transparent" + app:tabIndicatorColor="#FFFFFF" + app:tabMode="scrollable" /> + + - + - + - + + - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setupwizard.xml b/app/src/main/res/layout/activity_setupwizard.xml new file mode 100644 index 0000000000..8510abd33f --- /dev/null +++ b/app/src/main/res/layout/activity_setupwizard.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + +